diff --git a/CHANGELOG b/CHANGELOG index 755dd82a8..4542f4fdd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6205 +1,6251 @@ +2.6.8rc1 +==== +8b7444d (#2331) Remove darwinports pkg provider, replace with rewritten macports provider +65c4e14 Fixed #7082 - Added system support for groups +b7f4ff7 (#7018) Give more context on the service type's assumptions. Wording tweaks. +bb19dea (#7018) explain internals better in service provider documentation +23c9663 maint: Fix sqlite3 require to really be optional +4b73d41 maint: Fix sporadic sqlite error +54b9f5d (#6818) Stop from getting Rails 3 named_scope deprecation warning +e493f8a (#6856) Copy dangling symlinks with 'links => manage' File resource. +1e4968e (maint) Indentation fixes +99d78f2 (#6490) Add plugin initialization callback system to core +5d1cb02 Fix #4339 - Locally save the last report to $lastrunreport +306aa30 Fix #4339 - Save a last run report summary to $statedir/last_run_summary.yaml +9bb3018 Fixed #3127 - removed legacy debug code +d2bacd3 Fixed #3127 - Fixed gem selection regex +1b66c28 (#5437) Invalidate cached TypeCollection when there was an error parsing +0675c9a (#6937) Adjust formatting of recurse's desc +2cdadf9 (#6937) Document the recurse parameter of File type. +647a640 (#6893) Document the cron type in the case of specials. +87ca313 (#5670) Don't trigger refresh from a failed resource +f5aabf5 (#5908) Add support for new update-rc.d disable API +37f9ca0 (#6862) Add a default subject for the mail_patches rake task +9a4de12 Fixed #6256 - Creation of rrd directory. +7c60db5 (#5477) Allow watch_file to watch non-existent files, especially site.pp +7761acb (#5221) Add test for fix to fileset with trailing separator +357514c (#5221) Fix fileset path absoluteness checking with trailing slash +f8941b8 (#4769) Fix negative timeout support for newer rubies +a29c7fd Fixed #6562 - Minor kick documentation fix +df20513 (#6658) Propagate ENC connection errors to the agent +08115c0 (#4884) Remove typo from spec test +f2c771b (#4884) Modify tests to pass on non-OS X systems +ec1aa19 (#4884) Revise new exec tests, add a few more +196294a (4576) - if ENC declares invalid class, it is logged at warning. +0d2d6f3 (#4884) Add an shell provider for execs +d2e911a (#4884) Fix Test::Unit exec tests +fa0cfc6 (#4884) Break the exec type out to have a posix provider +c86a980 (#4884) Add consistent path validation and behavior +77fbf7f (#4884) Add expand_path to requiring the spec_helper +7ec9057 (#4884) Autorequire shared behaviors and method to silence warnings +acc99ba (#4884) Fix whitespace +6a4d291 (#4884) Get rid of open3 require since it wasn't being used +3e7ebbb Fixed #6554 - Missing $haveftool if/else conditional in install.rb breaking Ruby 1.9 +fddc165 (#5814) Improved cron type specs +f2dfee6 (#5814) cron_spec shouldn't depend on cron provider + 2.6.7 ===== 17f673d Updated CHANGELOG for 2.6.7rc1 852fb97 (#5073) Download plugins even if you're filtering on tags 4f34dbf Fix #5610: Prevent unnecessary RAL lookups 9781032 Revert "Merge branch 'ticket/2.6.x/5605' of git://github.com/stschulte/puppet into 2.6.next" 25926d1 (#6723) Fix withenv environment restoration bug 093f162 (#6689) Remove extraneous include of Puppet::Util in InventoryActiveRecord 4c19299 Remove extra trailing whitespace from lib/puppet/resource.rb ff9e242 (#5428) More fully "stub" Puppet::Resource::Reference for use with storedconfigs 0262633 (#6707) Fix typo in rest_authconfig.rb 8858e40 (#6689) Make inventory_active_record terminus search quickly 285c4cc (#5392) Give a better error when realizing a non-existant resource cd5deda (#2645) Adding a less-stubby test to verify the "system" attribute's behavior 531e258 maint: Remove serialization of InventoryFact values 3489412 maint: Rename InventoryHost to InventoryNode 4bd5493 Fixed #2645 - Added support for creating system users a3f2357 maint: Remove spec run noise 7764412 maint:Refactor of mount provider integration tests 880d0c6 (#6338) Support searching on metadata in InventoryActiveRecord terminus f836366 (#6338) Implement search for InventoryActiveRecord facts terminus 8ce30c8 (#6338) Add an InventoryActiveRecord terminus for Facts 1ef83cb Added integration tests for the mount provider 64440e5 (#6513) Propagate the environment when doing variable lookup in settings 92dffb2 (#6513) Adjust P::U::Settings test name to reflect what it tests 5ef1031 (#6632) Adding a new mount no longer causes error with umount bd5517d Adjust Darwin mount provider tests to pass on Linux 9d2fceb Maint: Begin adding integration tests for the mount provider 23d1c03 Maint: Added the ability to replace the behavior of Puppet::Util.execute with an arbitrary code block for ease in spec testing. 455a891 (#5794) create reports directory when creating host specific directory 1b1e803 (5724) Prep for deprecation of DESTDIR f4a0af1 Refactoring duplicate code and logic in prep for DESTDIR deprecation. 7a00d6b (#6606) Inline docs: Document all autorequire relationships e3aec14 (#5148) Fix failing spec due to timezone 8bd80a9 (#5148) Add support for PSON to facts c3baa28 (#6338) Remove inventory indirection, and move to facts indirection 6c53eb3 (#6445) Fix inline docs: puppet agent does not accept --mkusers 4e29f43 (#6541) maint: whitespace cleanup on the file integration spec b907ba3 (#6541) Fix content with checksum truncation bug 422399b (#5466) Write specs for output of puppet resource 8cc390c (#5466) Monkey patch Symbol so that you can sort them 24eacb7 (#5466) Fixed puppet resource bug with trailing , 743e039 (#4922) Don't truncate remotely-sourced files on 404 bb69011 (#6338) Remove unused version control tags e2a5085 Maint: Align tabs in a code block in the Augeas type. 65a5496 (#6509) Inline docs: Fix erroneous code block in directoryservice provider for computer type ea9f1f0 Maint: Rewrite comments about symlinks to reflect best practice. 94f8ead (#6509) Inline docs: Fix broken lists in Launchd provider. c80a77d (#6509) Inline docs: Fix broken code blocks in zpool type 27863c3 (#6509) Inline docs: Fix code blocks in service type. f4034f7 (#6509) Inline docs: fix broken code blocks in schedule.rb. 6f6c4b5 (#6509) Inline docs: Fix broken code block in file type (content attribute) a949a83 Revert "(#6309) Ensure the correct device is mounted when managing mounts" 23a510a (#4914) Improved stubbing in mount/parsed_spec tests. ac2262d (#3999) Allow disabling of default SELinux context detection for files 23eb77d (#6322) --noop should not suppress error codes 439115e (#6499) Make puppet respond identically to -h and --help 23b7119 Maint: Add an assertion mechanism to Puppet e3dfe41 (#6418) Recursive files shouldn't be audited 0e9858f (#6407) Fix spec test hang with Mocha >= 0.9.11 in zlib testing 309b932 (#5552) Display help when no subcommand is given. de6a205 (#5552) Clean up subcommand handling inside puppet cert. bb31c3d (#6376) Add test case for facts find request 2ecf913 Revert "(#5935) Allow functions to accept negated values" c57c508 (#4914) Improved parsed_spec for mount ec33a09 (#4914) Remove mount specs e854205 Remove pending tests from parsed mount provider 6cb365a (#6309) Ensure the correct device is mounted when managing mounts d1f1858 (#6376) Add support and testing for _search GET requests 3b41d44 Clean up whitespace, and commented out code in parsed mount provider a7cebf8 (#6337) Fix Ruby warning on 1.8.6 about "future compatibility" 8a48560 (#5150) Make fact REST terminus configurable to connect to inventory service e6870f6 (#5166) Inventory service is now searchable by timestamp. 2d2f9ab Maint: backport timestamp accessor for facts from 2.7 branch fa0ed63 Refactored Puppet::Node::Inventory::Yaml tests in preparation for adding freshness check 67f24e4 Refactor Puppet::Node::Inventory::Yaml in preparation for adding freshness 23fc4db (#5132) Provide a query REST interface for inventory e3c59df (#5935) Allow functions to accept negated values 7cb884e (#6346) Move the trap calls onto Signal so they're easier to stub b5bae9f (#6331) Remove final special :trac: link from the file content property 4d25d90 (#6331) Inline documentation: Fix rotted links pointing at the Trac wiki b25d9e4 maint: make rspec exit with status 1 when there are failures 5c26f68 (#5516) Rebuild parser.rb after merge. e512e3e (#5977) fix spec test failure when new applications are introduced. b5b5923 misc: ast_context has two arguments, not one. 414e3a5 Fix #5516 - Hashes can't be used in selectors c373b62 Fix #6269 - Hashes only work with two levels of access 9090507 Fix #6267 - puppetdoc embedded links to puppet entities are not hyperlinked b4a171e Fix #5720 - puppetdoc misses some class comments cfa0c32 Fix #6281 - Make sure puppetdoc analyzes all files 48bc7d0 Fix #6280 - puppetdoc crashing on string interpolation 0b7faa6 (#6270) Fix formatting in split function's doc string 637e139 (#6270) Fix formatting in regsubst function's doc string e9ee621 (6130) Change header level on metaparameter reference d6e4ffe (#4914) Specs for mounted? match new behaviour f534470 (#4914) Add specs for modified mount provider b753038 (#4914) Add specs for modified mount type 9f40608 (#4914) Update property blocks fd111f2 (#4914) Query property_hash for mountstate 2884660 (#4914) Prefetch mountstate ade951a (#4914) Join lines for better readability 8b98526 (#5662) Fixed tests that didnt stub key_attributes 02b3111 (#5605) Prefetch doesnt work with composite keys 2a0c970 (#5662) Parsedfile doesnt work with mult keyattr 35dd070 (#5661) Creating types dont work with >1 namevar 2.6.6 ===== d24e32a Update CHANGELOG and version for 2.6.6rc1 7c2a980 (#6541) Fix content with checksum truncation bug 63e911f (#6418) Recursive files shouldn't be audited 2.6.5 ===== 30fa41d Updated CHANGELOG for 2.6.5rc5 b481321 (#6337) Fix Ruby warning on 1.8.6 about "future compatibility" dcce45c (#6353) Restore the ability to store paths in the filebucket 0450197 (#6126) Puppet inspect now reports status after run completes. 960890f (#6364) Adjust mis-translated regex in mount provider for AIX 9e0f9c5 Updated CHANGELOG for 2.6.5rc4 664ef67 (#3646) Fix the documentation fix for `puppet apply --apply` 4b6519a Updated CHANGELOG for 2.6.5rc3 7ef2fbf Updated fix for #3646 - apply / compile documentation 193016d (#5977) fix spec test failure when new applications are introduced. c08fc1b Updated CHANGELOG for 2.6.5rc2 1f89906 (#6257) Speed up PUT and POST requests under rack 70a43c4 Updated CHANGELOG and version for 2.6.5rc1 f108f03 (#6018) Nick F's --help text for puppet inspect. 04ea826 (#5823) document the not-an-API status of set_run_mode 4ff5769 (#5823) run mode can now be set dynamically... bddfa1e (6114) Update the audit metaparameter for 2.6.5. ac8d316 Fix for #5755 -- making zaml serialization robust over projected objects c912a2a (#4139) hook log autoflush into global defaults f9e2e2b Augmentation of tests for prior commit 392504a Fix to fix for #5755 -- backref serialization issues in zaml a732a15 Fixed #5564 - Added some more fqdn_rand documentation f279f2c Fixed #4968 - Updated list of options turned on by --test in documentation ce5a2bf (#5061) - allow special hostclass/define variables to be evaluated as defaults. fd73874 (#6107) Fix an error when auditing a file with empty content 530496b Remove already initialized constant warning from file_spec.rb tests 76788f8 (#5566) Treat source only File checksums as syntax errors when used with content d657292 Rename variable used in File type validation to be more clear 3398139 Remove invalid "timestamp" and "time", and add missing "ctime" File checksum types. 6c93eb2 Remove order dependency when specifying source and checksum on File type 3a125d4 Bug #5755 -- ZAML generates extra newline in some hash backreferences. 50c12e5 bug #5681 -- code fix to handle AIX mount output 139760b Bug #5681 -- parse AIX mount command output. 2f74d83 Spec for #5681 to allow parsing of AIX mount output in mount provider 878f266 Fixed #6091 - Changed POSIX path matching to allow multiple leading slashes eb97aa5 Bug #6091 -- test leading double-slash in filenames are allowed. 1bfc9a0 Fixed #6071 - Fixed typo and improved exec path error message c50a48e Fixed #6061 - Allowed -1 as password min/max age bf44e72 Bug #6061 -- verify that negative {min,max}_password_age are accepted. af1c1fe Feature #5855 -- fix withenv call in freebsd package provider d871641 Feature #5855 -- undefined method 'withenv' in FreeBSD package provider. f1ab588 Fixed #6009 - nested member list vs directory service group provider 86a2a00 (#5944) Remove documentation of define() when used on nodes, as it is not a supported use of this function. 2b9f653 (#5944) Further edits of inline defined() documentation. 5d108e8 (#5944) Improve documentation of defined() function 7d38ab2 (#5594) Update documentation of exec resource type. 67e1bba (#5931) Prevent errors when calling insync? on audited properties 0f9d236 Maint: Removed dead code from resource harness. 0765afb Maint: Rename misleading insync? method in file provider 0084b08 (#5548) Specify return values of manual status commands in service type description. dd332f6 Fixed #6002 - Added note about function execution 3cfbd07 (#5045) Cleaning up some tests and code a2036ea (#5045) External node classifiers should be able to specify params for classes 18ca97b (#5045) Adds support to resource/type to also accept a param hash 70630b9 Fix #3165 Ralsh (bin/puppet resource) can't manage files 1fd3600 Fixed #3646 - Added documentation for compile and apply to man page ae48634 Fixed #5914 Removed genconfig = true from genconfig output 7e7f342 Fixed #1657 - Added note about target file 069f29b Fixed #2096 - clarified option modification and tested it is working 66b442b Fixes #5916 - Cleanup of unused doc methods and documentation 9b74968 Modified rubydoc in lib/puppet/util/command_line/puppetca to fix inaccurate description of --clean. e58f5dc Fixed #5742 - Removed legacy fqdn option from documentation 4d1b51f Fixed #5167 - misleading documentation in the defaults of [main] c1b5c7f (#5913) Fix Puppet::Application.find constant lookup behavior 7b3b56e (5977) Puppet::Applications can be loaded from multiple paths. f9bfb96 (#5900) Include ResourceStatus#failed in serialized reports 79b6332 (#5882) Added error-handling for bucketing files in puppet inspect 17843d5 (#5882) Added error-handling to puppet inspect when auditing 1a6fab2 (#5171) Made "puppet inspect" upload audited files to a file bucket a7cd185 Prep for #5171: Added a missing require to inspect application. 71ac9cf Locked Puppet license to GPLv2 abc6256 (#5838) Support paths as part of file bucket requests. 002f9f1 (#5838) Improve the quality of file bucket specs. 94d7179 (#5838) Make file bucket dipper efficient when saving a file that already exists 89f5692 (#5838) Implemented the "head" method for FileBucketFile::File terminus. 9cfd3d5 (#5838) Reworked file dipper spec to perform less stubbing. c514c64 (#5838) Added support for HEAD requests to the indirector. 2b9b7a5 (#5838) Refactored error handling logic into find_in_cache. 08561b2 (#5838) Refactored Puppet::Network::Rights#fail_on_deny 87c5c30 (#5910) Improved logging when declared classes cannot be found: 4efc98a maint: Remove unused Rakefile in spec directory a002231 (#5171) Made filebucket able to perform diffs 8f314f2 (#5710) Removed unnecessary calls to insync? e270086 Prep for fixing #5710: Refactor stub provider in resource harness spec c57a677 Maint: test partial resource failure 8aa8b9d (#5799) Simplify report dir creation 2d88844 maint: Add vim swap files to .gitignore 3d3baec maint: Remove rspec options from the Rakefile df65304 maint: Inspect reports should have audited = true on events 4c9eca1 Maint: Added "skipped" to the YAML output for Puppet::Resource::Status 717670f (#5771): Fix spec failures associated with rspec upgrade 52760a4 (#5771) Upgrade rspec to version 2 7603b05 maint: remove stray debug statement. 7661ba8 maint: Prune #inspect methods on various objects 80bfb54 (#5758) Verify that report events are correctly created de85f8d Prep work for #5758: set audited=true on all audit events e162da9 Prep work for #5758: clean up initializer for Puppet::Transaction::Event 06a8d1e Fix #5698 puppet inspect shouldn't report of attributes of deleted files 1f72c31 (#5715) Added attributes resource_type and title to Puppet::Resource::Status. a6cd736 (#5715) Removed attribute source_description from the YAML representation of Puppet::Resource::Status. 98db2da (#5715) Removed unnecessary attributes from YAML of Puppet::Transaction::Event. bd4a8a1 (#5715) Make certain report attributes always present. 716ee1c (#5715) Changed the type of metric names to always be strings. 037eac4 (#5715) Add status attribute to reports. e4a2e04 (#5715) Made the report "calculate" methods strictly functional. 71db5be (#5715) Made the changes/total and events/total metrics always present a4e40f4 (#5715) Refactor in preparation for adding a status attribute to reports. 15dda94 (#5715) Added total time to inspect reports and made inspect metrics more consistent. d1bcdec (#5715) Removed Puppet::Transaction::Report#external_times from YAML output. 1550bbb (#5715) Added total time metric to apply reports. 4cc42cd (#5715) Removed redundant attribute Transaction::Event#version 1907650 (#5715) Removed redundant attribute Resource::Status#version e596a57 (#5715) Removed Puppet::Util::Log#version. 908e0e0 (#5715) Removed the unused attribute Puppet::Transaction::Event#node 0e39ec5 (#5715) Removed Resource::Status#skipped_reason. It was never used. b765f0e (#5715) Prep work: Fixed add_statuses in report_spec. 8631709 (#5723) Fix failing type/package specs 76fe2b3 Implement #5168 and #5169 ctime and mtime are properties d11ae78 [3782] Test isolation problem in test/ral/providers/cron/crontab.rb 4d3030c Modified the behavior of Puppet::Resource::Status as follows: 7fff780 (#5408) Reworked ResourceHarness so that code is clearer and all behaviors are tested d516f63 (#5493) Add report_format, puppet_version, and configuration_version to Reports 093c45f (#5375) Rework puppet apply to use configurer.run e99a3ea Fix #5566 none, mtime, and ctime checksum types can write file contents d74e8e1 maint: Fix ActiveRecord confine issue 6daeb16 maint: Fix a test that was missing a require 5db696b maint: Fix tests that don't run on their own 7f4e058 (#4487) Fix environment column in hosts table 3ac50fa maint: restore plugin handler safety f38c36c (#5408) Attributes can be both audited and managed 54a1025 maint: missing stub 1d3192e maint: missing stub 1aa8157 maint: missing line and filename stubs 5e5ee97 maint: Fully stub partially stubbed test. 3d7c8d0 maint: remove Puppet.settings stubs 52fba89 maint: test was expecting Catalog.find too few times 8c134b6 maint: broken test not failing due to over-eager exception catching 3e59277 Fix #1757 Change file mode representation to octal 84bf02e Bug #5423: This moves the home directory property before the uid property, thus minimizing room for damage when usermod is in use. 1131ad7 (#4943) Add puppet inspect application e005cc7 maint: Remove bogus mongrel test c908fdb (#5261) Fix #5261 Don't escape Unicode characters in PSON b27e9b4 [#5081] Revert "Fix #4349 - Parsing with ignoreimport=true was always loading site.pp" af6e08c (#5304) Use internal_name rather than real_name for maillist provider 2.6.4 ===== 76890a5 Revert "(#5304) Use internal_name rather than real_name for maillist provider" 19f3879 Disable remote ralsh by default eee1a9c (#5424) Ship auth.conf as part of installing from source 779fea8 (#5304) Use internal_name rather than real_name for maillist provider 83f878e Renamed Reductive to Puppet 2.6.3 ===== 53bb805 Incremented CHANGELOG for 2.6.3 184733c [#5322] (#5322) Remove spec file that adds little value and causes failures 178c2a6 Fix test failures that fixing #4726 exposed. 74b6c09 (#4726) Fix RRD legacy support 8662056 Fix for #4279 -- mount detection on HP-UX fbb096a Fix for #5055 -- adding to_sym to Puppet::Node::Environment b2ff6a5 Fix for #5298 -- Collections need to do type lookup 1ce00dc Step towards [5298] -- cleanup indentation, etc. in AST::Collection 722608b Fixed #5287 - Schedule documentation is incorrect c8b6fb5 Fixed #5296 - test warnings messages d221c05 (#5297) Fix schedule tests that were missing stubs for Time.now f2fd0d1 Fix for #5273 -- user resource constantly resetting password age 544dcf8 Fix #5289 -- Bad copy/paste changes message on test failure 2.6.3rc3 ======== 126681f Updated CHANGELOG for 2.6.3rc3 b15231d Fix for #4299 -- Don't require which ea435a4 Fix #5020 - Prefer finding node name from REST uri over certname a097b93 Fix for #4894 -- retry tests if port is in use ee61b4e Fix for #4955 -- Race condition & memory leak in Puppet::Util f57425d Fix #4921 - race condition in Parser Functions creation 9604f1c Fix #5252 - line number mis-attribution during parsing cc5224c Maint. fix for test broken by 00eedac5 5f7d0fb Fix for #2568 -- Add a dbconnections option to set AR pool size ba4d22b Maint. Removing code for which no CLA has been signed 4a3d5d7 Reimplementation of functionality removed by prior commit 235d641 Refactor for CLA 9ba0c8a Fix #4923 - close process race when truncating existing file cb16d3d Puppet-load: better and safer error reporting 1d26742 Fix #5023 - puppet-load multiple nodes support 00eedac capture stderr from exec resources 4cbceab (#4573) FreeBSD service provider now supports versions <7 and >8 06c8748 Fix #3808 - puppetdoc should use --force-update only if RDoc supports it 6e6712b [#4813] Remove dead code from puppet/rails/host.rb 956296a Fix #4911 - Do not generate doc for standard RDoc parser generated object 4fa24bb Fix #5127 - error when accessing array elements abb8c66 (#5242) Fix schedule specs that fail near daylight savings ec667fd Kludge for #5206 -- port of fix for #3536 to yaml 9a3b584 (#5062) Add envpuppet helper script to ext/ aad7008 [#5225] Fix spec failure that depended on time change 21db472 (#5233) Randomize tmp dir paths 2.6.3rc2 ======== 244213c Updated CHANGELOG for 2.6.3rc2 76ac1f8 Fixed #5112 - Launchd Service broke in 2.6.2 with OS X 10.4 Clients. 776ea2a Fixed #5137 - Removed no longer required TOC references 31118fe Kludge for #5048 -- serialization compatibility with 0.25.x 65ef24e (#4534/#4778) -- Normalize parameterized classes 3b53bfc Fix for #5022 -- Escaped newlines should be elided 2.6.3rc1 ======== e3fc5b9 Updated CHANGELOG and version for 2.6.3rc1 3c56705 Fix for #4832 -- Making PSON handle arbitrary binary data e232770 Minimal fix for #4975 -- only call chage when managing password age rules a090e86 Fix for #4963 -- Use correct commands for password expiry on solaris 2.6.2 ===== 295c3be Updated CHANGELOG for 2.6.2 1d719be Fix for #4945 -- explicitly check os to supress man page installation 55417bc Reversion of 021d534482dd8edb863cb77d668ac3525362a0a6 a6e2bea Fixed #4919 - added parenths to fix error message: 2.6.2rc1 ======== 917c520 Incremented version to 2.6.2 900eae4 Updated CHANGELOG for 2.6.2rc1 1b6094d Fixed documentation typo bdf12fe Fix for #4896 -- stray newline left over from removed diagnostic e7424c6 (#4772) Update SuSE .spec file 0aaa742 Fixes #4792 (Duplicate definition since 2.6.1 upgrade) ea49d13 Improvement to #4025: made spec tests work on all platforms 0b4ce08 Adds #3046 - support for password min/max age e9f9d26 [#4783] (#4783) Fix RRDGraph report generation 34f87cf Add user account expiry to the useradd type and provider a7fb9b1 Fixed #4025 (failure in launchd if certain plists are binary). 2573872 Fix for #4649 -- avoid unsupported features on non-posix systems eb9279c Fix for 4273 -- revert b7e2580ab49ecdb67fc9b522829c005fc3750fbe 53a2bea Fix for #4804 -- escaped backslashes in interpolated strings d12e477 Fixes #4863 (Missing "require 'webrick'" causes nondeterministic spec failures) 574812e (#4860) Add regression tests that would have caught bad params method 68947e7 (#4860) Fix wrong method name.. params seems to be renamed to parameters 021359b Fix for #4644: install.rb works properly on Windows d057b90 Fix #4726 Update puppet rrdtool metric code to support modern rrd ruby bindings 66cf3a9 Fix #4226 - Prepend 'Puppet CA: ' to fqdn for default root ca_name d54352a Port Puppet::SSLCertificates::CA test to rspec effc6b8 Fixes #4852 - error messages involving Whits now mention Classes instead 3f99bd7 Fix #4267 - Create a backup before dropping permissions 6468f4e (#4763) Don't call a method that was removed in Rails 3 activerecord 79d5fde Fixed #4763 - Hardcoded ActiveRecord version 4798df3 Fixes #4822 -- Puppet doc -o option broken 99c1019 [#4798] Puppet doc manifests documentation mode broken 8cd1540 [#4692] undefined variables cause :undef to be passed to functions 06bf566 [#4787] Missing require causing failure bba04e0 Fix for #4746 -- Newline goes at the _end_ of the line 9e17c25 Fix #4743: Allow the audit meta-parameter to accept both 'all', and :all f950061 [#4716] ResourceTypeAPI exposes implementation details that are likely to change 8ff4b9a Fixed #4819 - corrected cron documentation 2b50f30 [#4771] Import of manifests with the same name only happens once 7b8cb74 Fix for #4708 - tagmail should allow . in tagname 6f229ee Minimal fix for #4631 -- set implicit classes as in 0.25.x 021d534 Fixed #3707 - rpm, like dpkg-query exits 1 if the package is not installed. Returning nil in this provider had the effect that on every run, puppet would end up calling yum erase . Returning the correct data structure resolves this. 216f663 Fixed Puppet Doc TOC generation c3cb57c Fixed versioncmp function typo 898a170 Fixed Reductive references in LICENSE file 996f14e Documentation updates for Markdown conversion 2.6.1 ===== cad1e0f Updated CHANGELOG for 2.6.1 14f871d [#4756] addendum for #4756 9bdfe69 Fix for Bug #4756 - Providers no longer respect missing features Restored deleted lines from type.rb and reinstated unit tests 14b3340 Fix for #4736 -- preserve case of defined resources bd973a2 Fix for #4637 --use of namevar missed in refactor 2.6.1rc4 ======== efa834a Updated CHANGELOG for 2.6.1rc4 763e7cb Minimal fix for #4691 -- class name uppercased in $name 4a9c857 Fix for #4693 -- implicit stages should never be serialized fa4d32c Fix for #4646 -- Missing stub 4d55c6e Fix for tests broken by fix for #4489 -- stub standalone b397b69 Fix for #4489 -- apply was using the rest terminus e91a8cc [#4462] uncaught Timeout::Error 4d36a51 Fixed alias metaparam docs error b063635 Skip apt-listbugs and apt-listchanges when installing from puppet e860907 [#4660] Avoid passing rake and autotest args to puppet tests 419e007 Fixed #4706 - logcheck patterns f6c0265 Fixed queue require for #4555 07d0be4 [#4308] Remove puppettest from specs 9e69616 Fixed RSpec warning messages 8d24861 Fixed #4100 - Added http_caching to yumrepo type 8cd266e Added cost parameter to the yumrepo type 0056d41 Fixed extlookup documentation and spacing e783a16 Fix for #4506 -- too much data being serialized f59cfc8 Fixed terminus example documentation 690465e Fix #4615 - vim highlighting fails on slashes and colons. 078e4fd Updated man pages 7548c65 Updated Man page generation since move to Markdown 2.6.1rc3 ======== 8be1929 Updated CHANGELOG for 2.6.1rc3 81a2725 Fix for #4456 -- need to accept some mime-type aliases c318558 Fix for #4489 -- apply was using the rest terminus 491c31d Fix for #4542 -- included classes weren't assigned proper stages 302d657 Fix for #4501 -- reports not generated in standalone 1ea4ccf Start server before agent 4c28079 [#4555] puppet queue tries to call code it hasn't required d1150e0 fix #4528 - treat * as absent 20f4b90 Fix for #4518 -- classes not getting added to compiler.classes 57bb06b [#4545] Remove obsolete 'trac' specs 82b4f04 Maint. -- Fix test failures broken by previous commit 0c30754 Maint. Removing code at the request of the original author 3df0490 [#4298] Puppet apply prints an error if the file to apply doesn't exist 5d4f222 Fixed #4527 correctly for 2.6.1 1b3d782 Updated config.ru example for 2.6.1 2.6.1rc2 ======== 0aa27b5 Updated for 2.6.1rc2 252c9b8 Further RST to Markdown fixes for types, values, tests 1157e8d Updated all types to Markdown output fef8800 Updated reference output to generate valid Markdown 79e0a2e Reformatting documentation from RST to MarkDown (#4509) Signed-off-by: Jes Fraser 62435e5 Rewrote functions documentation to Markdown e4b2aa6 Removed legacy Trac code 8ddea2a Maint. Passenger fix broke a test f43e87b Fix for #4476 -- rails calling yaml internals a23d80a Fixes #4485 -- single quoted strings should not treat \n as new line 8e31b52 Passenger needs HTTP headers values to be string 037bb32 [#4404] Remove requirement for source on Parser::Resource::Param 0e4bc62 [#4364] Fix failing spec due to incorrect loglevel 3a6ca54 Fix #4458 - Do not dump the whole environment when instances can't be found d909827 Fix for #4465 -- earlier "feature" patch broke ldap 47005aa Maint -- tests need to respect RFC-952 6aac8f0 [#4467] Make Puppet Master respect facts_terminus settings 1cba9a7 added md5 support as requested in http://serverfault.com/questions/166199/puppet-md5-sum-of-string 1dfd2b6 [#4381] extlookup shouldn't trigger reparses of .pp files be2b1f3 [#4370] Fixes extlookup precedence getting overwritten between runs 03808fd Fixed #4364 - Reduced audit msg from info to debug 539b57c [#4347] run_mode was colliding with --mode for "puppet doc" 1faebdd [#4423] repair parameterized class instantiation 37568bd [#4423] class { shouldn't get stored on the namespace stack 449315a [#4397]+[#4344] Move type-name resolution out of Puppet::Resource into the AST resources. daa801b [#4344] Temporary fix to stop agent from importing modules 00ebf01 [#4344] Fix for failing templates when module name matches file in local dir. e32320e [#4336] "reportdir" was in the wrong section 0f9672a Fixed #4311 - Typo in defaults.rb f54d843 Fix #4461 - attempt to fix another performance issue 2c21fae Fix for #4300 Solaris svc files need new pid filenames 83c2419 [#4284] Fix failing specs run as root due to missing puppet group 8237f68 [#4242] Fixed (removed) a broken unit test d5ad0fb Removed eventual documentation line ... eventually came 871e6fd Fixed #4368 - Updated clean stored configs ext script for new config sections cb64477 Updated version to 2.6.1 2.6.1rc1 ======== ecf44e4 Updated CHANGELOG for 2.6.1rc1 bdfcac5 Update Red Hat spec file for 2.6.0 9f08e7c Feature: puppet-load - a tool to stress-test master compilation ef9a4a6 Fix #4245 - default insertion of ACL is not thread safe 4065e81 Fix race condition in rack autoloading of request/response 3163932 Fix #4244 - Cached Attributes is not thread safe 7d42f77 JRuby doesn't implement Process.maxgroups 760e418 Fix #4349 - Parsing with ignoreimport=true was always loading site.pp 67bdf89 Fix #4348 - Puppet doc single manifest broken 13c71b9 extlookup() is a builtin d38e522 [#4333] old optparse doesn't support default_argv= 86b0882 Fixed #4326 - Updated SUSE packaging 03313b8 Fix #4319 - source file url sent to the master is invalid ac3a0d2 vim: highlight default parameters in definition/classes be2141a vim: match collected resources. c047c8d vim: added elsif 9569136 Fix for 4314 -- Need to allow '-' in class name for refs 636079f Fixed #4304 - Changed logging level for auto import message 000fd1e Fix for #4303 -- reverting to old escaping in '-strings 1d494a3 Tweak to fix for #4302--dangling ref to known_resource_types 2383050 Fix #4302 - Compilation speed regression compared to 2.6 63ec207 Minimal fix for #4297, with notes for follow-up 7ad7eb1 Fix #4286 - rename puppetdoc global module to __site__ 28bb195 Fixed yumrepo type deprecation wanring ` 067a46d Temporary tweak to tests for #4242 9778f2a [#4242] Fixed recursion due to parents including their children 59a23d6 Fix for #3382 -- Empty classes as graph placeholders 865282a Fixed example config.ru a0a63c3 Fixed network and indirection reference 64386cf Fixed Indirection reference 2.6.0 ===== db0b30d Updated CHANGELOG for 2.6.0 42a475e Fixing #4268 - manifests always imported 06fc40c [#4269] Undef variables interpolate to empty string 1288f8c [#4270] Force inherited classes to load into the correct environment 539d716 [#4287] Fix the undefined evaluate_match error when comparing functions d2da1d4 Tweak to tweak to fix for #4233 -- ":" is valid in type names bbc07f2 Bandaid for #4285 -- :name vs 40e6f02 Tweak to fix for #4233 -- only accept word chars in types 2.6.0rc4 ======== d87a2e3 Updated CHANGELOG for 2.6.0RC4 cf597d7 [#4233] Ruby regexps are not multiline by default, but Resource titles can be multiline d6cbb21 Fix for #4234 -- ruby DSL fails on second resource 4822de3 Fix for #4236 -- Only interpolate $ if followed by a variable b509032 Fix #4238 - if should match undef as '' 8c8c146 Minimal fix for #4243 -- import isn't thread safe d319da4 [#4247] storeconfigs was calling Puppet::Parser::Resource.new with the wrong arguments 9f91540 [#4256] External nodes parameters can now be assigned to nodes 680dd1a Fix for #4257 -- problems resolving ::-prefixed classes 6e07a19 Fix #4262 - Puppetmaster used to log compilation time 5b68afe Fix for #4255 -- misleading diagnostic message dd03ac9 Partial fix for #4278 -- the performance aspects 4ce33fd Fixed #4249 - Updated SUSE packaging specifications 91185c6 New man pages for 2.6.0 1cda7c5 Fixes errant Trac references in documentation 2.6.0rc3 ======== f60b6a0 Updated CHANGELOG for 2.6.0rc3 9df87e9 [#4219] Install misses command_line dir, puppet $app --help fails 0422852 conf/redhat: Consistently pass pidfile option to daemon, killproc, and status 63bf037 conf/redhat: Update conf/init files for single binary f72741f conf/redhat: Rebase rundir-perms patch 793d7b7 [#4213] -o option for setting onetime now works properly 2edf7fe [#3656] Serializing arrays of references 27d5a47 [#4215] Have rundir depend on vardir 06cc552 Fix for #4220 -- modules not implicitly loading their init files 2.6.0rc2 ======== 8747479 Updated CHANGELOG for 2.6.0rc2 fa74020 [#4209] catalog.resources should return resources f5f9a38 Fix for #4210 -- missing require in CA 1c3e844 Minimal fix for #4205 -- incorrect Import loop messages 99d8323 Fix #4206 - import "path/*" tries to import files twice a2115af Alt fix for #4207 -- serialize environments as their names fe4dcd8 [#4208] Missing parameter breaks multithread compilation 2.6.0rc1 ======== e028310 Updated CHANGELOG for 2.6.0rc1 3180b9d Code smell: Two space indentation 5432259 Code smell: Avoid needless decorations 8f15707 Code smell: Don't restate results directly after assignment c3e2353 Code smell: Use &&= for dependent initialization 42a5390 Code smell: Use ||= for conditional initialization a07bbe2 Code smell: Omit needless checks on defined 07b15bf Code smell: Avoid unneeded blocks 8d1fbe4 Code smell: Avoid explicit returns 889158a Code smell: Booleans are first class values. 81e283b Code smell: Line modifiers are preferred to one-line blocks. e8cf063 Code smell: Use string interpolation eefccf2 Code smell: English names for special globals rather than line-noise 184132e Code smell: Use {} for % notation delimiters wherever practical 9ee56f2 Code smell: Inconsistent indentation and related formatting issues 051bd98 Code smell: Miscellaneous oddity removal 77f8599 Code smell: Win32 --> MS_windows 3fbc1d5 Updated GPG rake signing task for new Puppet Labs key 94fa5d5 [#4182] show_diff was broken for streamed file contents 7009704 Fix for #4117 "Storing newly-audited value" messages 9cf9788 Manifests with variables were broken when read from STDIN to puppet apply 835f73c Use the name in the search path for looking for metadata 5bab997 maint:rename resource_type to define in internal dsl 654b564 [#4198] Require 'fileutils' everywhere FileUtils is used a07af2b [#4196] Move the docs into the source directory structure 3c00591 Fix for #4178 - generalize autoloading to include .rb cea2e5b [#3582] Remove assumption that Puppet.settings would return values of a consistent type c58e420 [#4180] Support legacy module structure b4593f2 Update RDoc parser to reflect change of custom plugin and fact locations dda165a Fixed #4180 - Updated old module structure to match correct default 1715f3a [#2730] mount ensure present shouldn't unmount a282cc3 Fixed subscribe example 2353115 Fix for environments in startup script. - Dropped the forced --manifest switch in the suse startup script to allow for environments to re-define this. Otherwise, environments will not work as puppet override configuration with command line arguments. cfca62b Redmine: 2474 - Fix for mount fstype documentation 3ff38df Fix for #4137 -- Oracle needs text for strings > 255 62dbae5 Fix for #2807 Puppet settings available as variables a5fc364 [#4161] RDoc fails to parse some of our ruby syntax b7e2580 [#3169] Adds more debugging to SSL cert verification 70af43f Fix for #4167 -- overriding file permissions in conf file 2c88884 [#4114] Fixes test failures caused by previous 4114 fixes 4a6428b saving work for my unit tests. The redhat one still fails... 1e0d922 [4123] - allows self.instances to correctly report state of services. 8d3ced5 created init provider method self.get_services which accepts an array of filenames to exclude when processing defpath. cdd4382 [#4114] Fix failures in the unit tests e419293 [#4114] Added queueing to the log 4b00c6a [#4110] Wrap Type#retrieve calls for backwards compatibility 5f8a242 Fix for #4120 No namevar running puppet doc -r type 6ac36eb [#2370] Allow OpenBSD to add packages with versions and flavors 45a9f37 [#4108] Changed missing Application constant error a0ea74b [#4149] Don't create two Resource::TypeCollections 7978be5 [#3906] Fixed missing constant Puppet::Rails when using storeconfigs fb6f2aa [#4136] Specs should listen on localhost 6d4be90 [#3961] Part two: --destroy should also be local 0598f35 Fix for #4148 (2.6 is greater than 0.25.x) 5971898 Fix for #4142 stray use of JSON instead of PSON 74e5bdc [#3172] Fix the arguments to Application::Kick.new, which I had broken 4f06e9e Maint: Explicitly put test sqlite files in a temp directory 84a9412 maint: fix stub failure in report_spec.rb 1f48d68 maint: fix stub failures in report_spec.rb bee843a maint: fix stubbing in package_spec.rb 528b915 maint: fix stubs in transaction_spec.rb 37277a5 maint: fix stubbing in catalog_spec.rb ea55e83 Maint: Improve the speed of setting settings. 7c7f6da maint: file_spec heisenbugs d6d726b Heisenbug: settings as catalog trying to create directories e579aab maint: spec_helper should reset settings directories on *every* test 298a764 maint: Remove a heisentest that wasn't testing what it claimed b389392 maint: spec_helper should only get loaded once 3304068 maint: :mutable_defaults to improve spec consistency 08b49c6 [#4090] Fix the run_mode for certs and put tests on the applications to assert their run_mode e318db6 [#4059] fix the specs to correctly mock the Puppet::Resource.new call signature ab3d27c [#4059] Minor errors preventing ralsh from running 59bf5e4 [#2713] Enable ELSIF ebd0311 [#3172] puppet kick can take hostnames as bare arguments 697508d [#4108] Missing constants fail deliberately and with a message 2639a56 [#4092] Changed notify message to defined instead of changed 223157d Fix for #4091 -- require loop in executables 174e02a [#4090] Change how RunMode instances are created so that an object for each RunMode is only created once instead of every time it's called 62e3b61 [#4090] Fix last few tests and renames of mode to run_mode 2a25883 [#4090] Git rid of the idea that run_mode is a configurable option with defaults 75e0662 [#4090] Rename mode (:master, :agent, :user) to run_mode 3cd48d8 [#4089] Replace internal usage of :check with :audit e848d41 [#3961] puppet cert --generate implies ca_location = :local 255628e [#3961] Rename cert's @mode to @cert_mode to reduce confusion b2bd05d maint: Confine a test that depends on sqlite fdc8c35 [#3994-part 3] rename spec tests from *_spec_spec to *_spec.rb 9a94ee2 Fix for test ordering sporadic failure 9ceb454 [#3994-part 2] rename integration tests to *_spec.rb 06dffc1 maint: A test specified that a file wasn't writeable, but was writeable when run as root, which caused the test to fail. Changing the test so that a directory is in place of the writeable file so not even root can write to it. 2baf74e maint: Fixes some noisy specs 0aae5a7 maint: Fixing tests that fail when run as root 0fa10a6 Cleaning up various warnings in specs 2ab123f Removing obsolete nodescope concept 153d7cd Fix #3665 - part 2, node inheritance fixes dd4fa66 Fix failing tests in spec/unit/resources/type.rb 770a8ea Fix #3665 - main class shouldn't be a subscope of itself 76953d8 maint: Fixes broken order-dependent Tidy specs 9afc67a Fix for pre 1.8.7 compatibility in namvar patch d62a391 Breaking require-loop b4af238 Fix for #3985 typo causing warning 9169ef0 Fix: puppet apply trying to use XMLRPC constant af41beb Remove an old test that had been unintentionally reintroduced by a mistake in a conflict resolution 6a8e6fe Tweak an old test due to new error handling. 5f53bfa Restore error handling for value= e817ad1 Fix tests broken by level-violation fix e5478d4 Simplify the newattr method by removing a level violation 4ef40b8 maint: Rework testing of Reports http processor to be self contained edfcbf9 [#3139] Fixed a problem with the value method for parameters 61e978b [#3139] Fixed a broken integration spec in type tidy cf9bcad maint: Fixing test to reflect that host environment assignment now takes an object instead of a string c70c96b Fix some tests broken by changing the call to newattr a72fb14 Fixing oversensitive test 15004f3 maint: Fix failing test that needed more methods stubbed 60932e1 Fixed require warning documentation 6fcb87d Fixed mcx documentation error 15ae389 Documentation fixes f95169b [#4006] Fix test failures caused by reverting ticket 2890 b5572ae Bug 3731. Applied Fix suggested by Doug Warner to always flatten out the array 117e6b6 maint: Have 'rake spec' output in color a7e4fe8 [#3810] Do not create a reports settings block db44a32 Tweak for fix for #1175 to fix test failures 3bd6f11 maint: Fixing a test typo s/stub/stubs/ ebc3e78 maint: Fixing a failing spec by stubbing a method on a stubbed object that should have been stubbed. 3b4d33c remove tests for removed code c8089f1 [#2646] Fixes the change to onetime made in b96cd6c 4bf6950 [#3139] Make newattr idempotent 51b70c0 [#3994] rename the specs to have _spec.rb at the end 9958c80 [#4064] Modify the Rails spec to use the block form of confine af8bd77 [#4064] Modify confine to also allow a message and a block containing the test. 182c003 Fixing #3988 - adding support for watchr 3a44f0e Fix #3932 - Add --charset to puppetdoc for RDoc mode fb5c1d7 Fix #3907 - Hash couldn't be initialized with an empty hash 9592dd8 Fix #3871 - Add the 'in' operator 3696d95 [#3865] External subcommands 0fc41ae [#3802] Replace rug with zypper dc1a977 [#3766] Remove YAML fixups e0e6b64 Provides #3723. Add ability for execs to have several attempts at a successful execution and fix minor bug with logoutput and returns as an array.. c8ca19a [#3674] Make sure that failing to load a feature isn't fatal 2a73b5d [#3674] Part 2: Autoloader load method should propagate failures 7952af5 [#3674] Autoloader should propagate failures f35c59f Fix #3667 - Fix class namespace 938fbe9 Removing obsolete nodescope concept 49cf2f2 Fixing #3651 failing to pop comment stack for some constructs 0dd4201 Fixing #3072 - Resource generation is recursive b96cd6c Fixes #2646. Move onetime option from the agent to global defaults so we can specify it in the config file. 0a21e1b [#2522] authorized keys owner is verified 738802e Fixing #2337 - Adding 'freeze_main' setting 50a626d Fixing #1545 - Adding 'caller_module_name' variable 5d1934b Fixing #1545 - module_name is now a variable bba45f1 [#4055] Confine CouchDB-related specs to the couchdb feature 1c5b67d [#4055] Refactor of abstract Couch terminus, more specs 432db25 [#4055] Add CouchDB terminus for facts 35636e9 [#3921] Fix typo "fact_terminus" -> "facts_terminus" 45ca669 Targeted fix for #3851 c00285c [#3810] Add http reports processor and `reporturl` setting 1d49def [#3804] Fixed one failing spec for RackREST 1e89bff Fixes #3514 - CR/LF line ending in puppet.conf cause silent failure e6709da [#3409] fix test failures from ldap environment patch a7884b4 [#3409] environment is not checked when nodes are in ldap c75b219 Fixes #3395 - CR+LF line endings in manifests cause syntax error 8b127b1 [#3388] Setting host_aliases from [] to [] no longer results in any changes be7112a Fixing #3139 - all properties can now be audited 986298b Working #3139 - Adding Settings.clear Spec#after 32f6a9d Working #3139 - Removing Property#checkable 58cf8d9 Working #3139 - Catalogs default to host_config 8f3e8bb Working #3139 - ResourceHarness does caching d6407f4 Working #3139 - removing obsolete checking in Storage 0b95a85 Working #3139 - scheduling moved to resource harness 4627b8f Improving fix for #1175; tightening thread safety ccc869e Part 2 of fix for #1175 (functions in environments) 7c6b883 [#1621] Composite keys for resources 2396eba Use the 'root' feature rather than directly checking the uid 8128311 fix tests to reflect methods moved from type/file/owner to provider/file/posix 28702a4 variable name error in refactor 19c70b5 Remove over-eager error branch in a complicated If 09881cf Confine out a lib that puppet was failing to load on non-win32 d72fd9d Confine out tests that fail on unix d1b86ec Behavior change for //UNC_Paths broke an old test ba506c1 Resolving conflicts with jes5199:ticket/master/2935-settings-mode f15a324 Fix Exec type 86bd838 Move syslog into a feature fc92408 Adapt Util::Storage specs and avoid trying to lock on directories 58100ed Relax path qualification check on FileServing::Fileset 1c016a1 Implement quoting on the exec commands and repair specs 6a92894 Avoid trying to symlink() on windows 47c9dd1 Implement "forking" for Windows c59d864 Avoid non-win32 signals on win32 runtime bbba9f9 Avoid trying to set uid/gid on windows a90bcb0 Start extracting the owner managment for files into providers b51be28 Expand file type to be able to handle Win32 and UNC paths 17a9ad1 Updated version to 2.6.0 a2e809b Fixed RSpec deprecation notice in lexer tests f054d5b Make specs work on win32 54c4538 Delete tempfiles on win32 97c043f Fix path handling f80b4c7 Print stacktraces if requested 1d98e67 Adapt defaults to Win32 environment ea9bb49 More win32? feature def 1645d8e Define posix and win32 features b3aa3ec Improve error message d67f60f Fix ProviderDpkg specs to avoid any real executions e119b04 Avoid setting the timeout before we know which timeout we should set. d40e6d4 Bug: tidy specs need FileBucket::Dipper a6b52bb Avoid trying to lock on non-files 533ef68 Removing obsolete FCollection stub from Functions bc90df6 Functions are added to a module instead of Scope 17e40e7 Slightly restructuring "Functions" file 9d0a38e [#3921] Add facts_terminus setting to Puppet settings 2874729 [#3921] Remove unnecessary require 'puppet/resource' 58a3d27 Fix for #3949 and related b755f7f Fixed #3912 - Added client certificate name as an internal fact called "clientcert" b5f14c6 {#3866] Rename the method metaclass to singleton_class to avoid the deprecation warnings from Rails ActiveSupport 2b5bd49 Fixing #3822 - checksums will be loaded from filebuckets 94390de foo 3b7aac5 For #3822 - Reducing checksum code duplication ca7b166 Fixed unsupported perlstyle regex and few other minor bugs 85f2565 Signed-off-by: Ross West a4eb5d5 Signed-off-by: Ross West 970fd87 Fixing #3791 - client environment is used cce63d8 Bug #3748 LDAP group membership 4ba3dc2 Fixing #2655 - Adding default parameter values to resources 20a74bc Refactoring tests - replacing stubs with instances b5db33b Fix for 3664: interpolating qualified variables. 9ddee72 Fix #3664 - qualified variable parsing in string interpolation a32381e Feature #2935 remove misleading comment 5937af4 Feature #2935 Unify the codepaths for setting settings b7d387e Feature #2935 Puppet[:mode] and Puppet[:name] are read-only 342298c Bug: Broken codepath in util/settings 6d5566a Feature #2935 settings are in Mode sections instead of executable names 9536723 Feature #2935: method extract require_application c338fef Feature #2935: Test for if we're "puppet cert" cbb2802 Code cleanup: remove "self." from setdefaults 37a5530 Feature #2935 Modes: root? predicate ac7efc8 Feature #2935 Puppet::Mode#master? 5665e39 Feature #2276 Single Executable: Update docstrings fc29049 feature #2276 Single Executable: use new names for settings sections 631552c Feature #2935: Applications should declare modes 8f4d644 Feature #2935: lazify require graph for applications 6b26a7c Feature #2935: Puppet::Mode b65d1af Feature #2276 Single Executable: usage message 76304f8 feature #2276 Single Executable: move CommandLine methods e9627a0 Fixing #2658 - adding backward compatibility for 0.24 61a719f Adding #2658 - Adding support for run stages d13f8ac Fixing #3671 - --compile Catalogs are pretty-printed 89e8745 Fixing #2655 - Adding default parameter values to resources edcf429 Refactoring tests - replacing stubs with instances 3dfb762 Fixing Catalog conversion 0d4fd60 Fixing #1903 - metaparam inheritance is much faster 047ebfe Fixing Parser::Resource param validation 2fae0bd Fixes #1999 - Allows the 'apt' provider to downgrade packages. b10d35d Fixes #3745 Catch unhandled exception in ssh_authorized_key provider 584961a Fixed #3721 - Removed -u option from crontab on HP-UX a15a70c Fixing tests broken by conceptual merge conflicts 5988f76 Fixes #3663 - It should be possible to list signed hosts only 2c153b1 Fixing #448 - relationships have their own syntax 052f98f Fix #3408 - enable puppetd http compression 3eaf69c Fix for conflict between fileserving streams and none-checksums 2cf7222 Fix #3373 - Client side file streaming ee5d7f1 Add master side file content streaming 63c122f Fixing tests broken by Luke's CRL flag patch. 91e6022 Fixes incorrect line in partial CRL fix 379bda0 WIP - trying to fix #3460 3947574 Updated Template documentation link 5fd6e54 Change the main spec to an apply spec 009629f Feature #2276 Single Executable: usage message 5b64d3b feature #2276 Single Executable: optparser should get CommandLine#args instead of ARGV 5683fd9 Feature #2276 Single Executable: Pass a commandline object to the application d038a1d Refactor #3706 Reify eigenclasses of Applications 7656ba7 feature #2276 Single Executable: CommandLine can be instantiated 63e2e56 feature #2276 Single Executable: subcommand method b6e2ce6 feature #2276 Single Executable: help info b073722 feature #2276 Single Executable: help for apply bfad735 feature #2276 Single Executable: rdoc paths on ubuntu 7103378 feature #2276 Single Executable: legacy settings 54c1cc5 feature #2276 Single Executable: "puppet describe" c79b228 feature #2276 Single Executable: "puppet kick" 6bdda8c feature #2276 Single Executable: "puppet cert" f9783fc feature #2276 Single Executable: "puppet master" 1d8bd0d Fix #3552 single executable should display usage 722a27f Fixes #3702: the 'log' report now sends the host name as the log source again. ddd40bb Fix for #3690 failing to calculate error codes d61a69a Fixing #3668 - fixed autoloading classes from modules f66095d Fix #3656 JSON serialization of dependencies f0a0084 Fixes #3582 - Adds dbport configuration option for specifying database port 8b99367 Adding indirector support to Resource Types 748aed9 Fix #3552 single executable should display usage eafde5c Added support for flat packages in the pkgdmg package provider. Added a test in: ./spec/unit/provider/package/pkgdmg.rb c9e3d75 Fix: the rcvar name is not necessarily the same as the service name. (More and more I get the feeling that FreeBSD's rc system is totally unsuitable for Puppet. What about porting Upstart or SMF to FreeBSD ... ?) 861c177 Added proper status command 5f72eb9 Re-included old BSD service provider, made new one default for FreeBSD c3cd24b Rewrote FreeBSD service provider 1250f80 Fixed documentation issues exposed in #3772 211782f Updated CHANGELOG for 0.25.5rc3 7c59acf Renamed all references to Reductive Labs to Puppet Labs e82f5de Fix for #3107 Changing users on AIX 44f1465 Fixing #2864 Added support for AIX System Resource Controller (SRC) - service start stop 02ed8db Fixes #2836. Add hold support to dpkg provider 0f2d3ce Fixes #1223 Add Zypper support for SuSE machines a166d50 Fix for #3399 zone type should handle exclusive IP stacks af521fa Adding #3518 - basic dot format support 9b2b0ec Fix #3551 rake spec fails to run integration specs 6a66d5e Update Red Hat spec file for 0.25.5 46c72bb Updated CHANGELOG for 0.25.5rc2 ee0cc07 Fixing #3533 - Removing all transaction cleanup 11189fb Fix for #2910 -- Tidy/matches is too tricky to use 913b63c Bug #3451: Don't leak the terminus class setting from Puppet::Resource::Catalog's spec a228399 Fix to the fix for #3295 ae52005 Write ssh_authorized_keys as user 8c5e80e Fixing bad test 088c801 Fix for #3558 -- source file reading speedup cd06b87 Fix for #3556 Plussignment value melding 2de7da4 Fixed #3655 - Puppet doesn't find installed packages with portage provider d20d5de Fixed #3672 - Error message on duplicate fileserver mounts incorrect 6ae6821 conf/redhat: Add notifempty to logrotate config 7fc66d5 Fixed stored configuration documentation 14456b4 Fixes #3653 - Changed default factpath value to better reflect plugins in modules f3e466b Partial fix to #2837 - changed warning message to debug 686239f Fix #3555 - fix state of purged lists 6f8a1e9 Updated Rake tasks to no longer load puppet.rb 83a8c68 Fix #3540 - name methods correctly 3d395e8 Fixes #3295 - generate() now sets the working directory to the directory containing the specified command. 0f077c7 Added YARD task b49c60b Update man pages and partial doc fix to #3491 115f37d Fixed #3532 - Typo in lib/puppet/ssl/host.rb 784dd26 Updated version and CHANGELOG to 0.25.5rc1 4a6474c Modify SuSE spec file for 0.25.x and correct shebang lines for puppetd/puppetmasterd 385506f Fixes #3460 - Makes Puppet FHS compliant by moving /var/puppet to /var/lib/puppet b494427 Fix for #3101 (bug in MRI 1.8.7) 966b269 Fixes #3419. OS X 10.6 Ruby doesn't set supplementary groups 49be54e Revert the guts of #2890 e69b7db Fail gracefully on packages that don't have the HOMEPAGE variable set (e.g. dev-lang/php). 83ac6b8 Fixed #3443 - Typo in mount type dfe5c3a Fixes #3135 - darwin doesn't support 'mount -o remount' 2a60e1e Adding :catalog_terminus setting 626945b fixing obsolete comment in puppetd 9fbb69f Adding support for only using cached catalogs 7e1e76e Refactoring Configurer to enable the next feature b28e21a Fixed changelog Rake task e93eab8 Fix #3155 - prevent error when using two matching regex in cascade b883272 Fixed puppetlast typo 67bf142 Fixed README contents to reflect Puppet Labs and docs and wiki changes e35e142 Fixed link typo d40e24c Fixed #3384 - Updated broken link da00f68 Making a Puppet::Module test more resilient 9792915 Fixing yumrepo type to use 'flush' 9ee4c2d Only trying to retrieve passwords on OS X when root 8c31ebe Removing obsolete tests aee9c29 Fixing a warning in the aix package provider 232ad8f Removing tests for code that was removed in the REST refactor 94fddbc Fixing and porting Transaction Report tests 13d141a Fixing Property#change_to_s in rare case failures 66858ef Fix test in parser.rb due to API change 0f254be Fixing Hash functionality with non-constant keys 41aeba4 Removing vistigial method in ASTHash 1821187 Porting/removing group test/unit tests 03532e0 Porting a simple set of tests from test/unit 006e6af Removing obsolete test 1a6e08f Fixing Transaction integration test 410b71c Removing invalid File integration test effa719 Cleaning up content/source code 456447c Protecting spec_helper chmod from failure fa64774 Redeleting puppetmasterd integration test 797f412 Making SshAuthorizedKeys tests less brittle 622bb70 Markus's patch concerning string interpolation 23adec5 Fix tests that use or stub find_by_checksum, which I just changed the signature of. 4ac8e2c The pure-ruby YAML lib doesn't accept parameters e31fe8c Fix a failure in new FileBucket where it was impossible to read from a bucket with a non-default path. 3797c7a Update YAML dir test to match behavior 83d8bda Fix heisenbug in spec/unit/provider/mount/parsed.rb dde69c3 Remove test for old RDoc work-around c5ce824 Fixing various broken Resource::Type tests 6cd6c47 Renaming and fixing puppetrun tests. a27013a Fixing calls to "class_scope" in Scope tests 84d6892 Fixing all 'require' function tests b643413 Removing any mentions of :casesensitive setting fe140a2 Migrating "puppet" executable integration test edef647 Fixing 'puppet' to directly run manifests fff8d04 Fixing syntax warning 7c25317 Moving puppet back to bin a4d1ba0 Puppet::Parser::AST::Leaf#evaluate_match "insensitive" renamed to "sensitive" 404bdfa Repair validate_checksum codepath, even though it is disabled. e895494 Puppet::Parser::Resource.new parameters have changed 94651df Add stub to Puppet::Parser::Collector test to prevent runaway stub failures 40c1fb0 Resolving conflicts with ??? 1059370 Fixing a typo from a regex 3eeebf5 Fixing change printing for content/ensure 47c3ca1 Converted File[checksum] to a parameter not property 44cba9c Adding "checksum?" helper method to Checksums module d05d25c Refactoring File[source] tests somewhat aab2374 Resolving conflicts with luke:tickets/testing/2954 86cf226 Adding virtual and exported resource support to the DSL 9d5ba41 Cleaning up ResourceAPI tests 9060766 s/DSL::ResourceHelper/DSL::ResourceAPI/g 6d2a10b Adding simplistic pure ruby interface e515513 Adding environment support to parser resources 30f49bb Resolving conflicts with ??? b7015d7 Moving the string interpolation parsing to the parser/lexer 07cfdd0 Resolving conflicts with jesse:feature/master/3394 ad148d6 Resolving conflicts with luke:tickets/master/2759 922cf1a Resolving conflicts with ??? 0d70468 Finishing renaming :params to :parameters internally ad93d0e Forcing parent evaluation in resource types 6e4db82 Fixing type/title resource resolution aa659f2 Converging the Resource classes further 5401a7c Adding strictness checking to resources 9c867e6 Fixing most of the broken tests in test/ 274d1c5 Adding tmpfile cleanup to tests 7089446 Removing Resource::Reference classes 4871c90 Adding support for class parameters 4709e9b Removing :paramcheck and :typecheck settings 744295d Allowing Environment.new to take an environment 4f907c6 TypeCollection now supports namespace arrays 2fa0a48 Adding parameter validation to Puppet::Resource aff5992 Fixing failing Environment unit tests 61636e4 Tuning file load order 7a99a1d Removing obsolete Settings comments and require af9a920 Adding an environment helper c8e89cc Changing the interface of Puppet::Resource b7ea180 Partially fixing #2954 - Adding class parameters cbe2c49 Fixing test structure 4bff506 Indirector/FileBucketFile warnings 0917248 REST: Fix a state leak causing test failures 8f9fc30 REST FileBucket: REST barfs on relative paths 23ccefe REST: hide Request object d8e1b27 Feature #3394 REST runner, execution 1603f73 Feature #3394 REST Runner, preparation 16658a0 Feature #3383 Part 2: Remove RAL XMLRPC eda649b Feature #3383 RAL over REST 09b1412 Fix tests on #3347 e5a7800 Feature #3347 REST-ified FileBucket f838389 Fix a failing test in #3115 9acd0b2 Feature #3115 REST-ified status() b581c23 Fix #3229 - use original value in case/selector regex matching 490a03d Cleaning up a test. 576accd Removing unused Checksum::File terminus 58920a0 Converting File terminus to use formats. 37fd6ae Fixing datadir defaults to match new standards bf3359e Adding client and server data dirs b41d535 Adding filename extension support to formats. 7504f1e Resolving conflicts with ??? d0389f4 Renaming Parser::ResourceType to Resource::Type 67ef78d Removing Interpreter class b82b4ef All non-transient parser references are gone 644ad7e Fixing callers to Parser to only pass environment 9f8e0c0 Using the RTC helper to find the known resource types 1705366 Always warning if autoloading a file fails 7bef2e0 Adding helper module for finding known resource types 804105d Moving Rails initialization to Compiler terminus 26b272b Parser now uses Environment resource type collection cb16908 Adding 'known_resource_types' to Environment 201889b Renaming LoadedCode to ResourceTypeCollection 2c2b3c2 Storing per-environment LoadedCode instances 6bf1953 Removing old, never-used DSL code df2d392 Adding support for parsing ruby files b938edf Fixing test structure 847233f Adding []/[]= support to Parser::Resource 6e04fba Fix for #3366 - --tags '' treated as boolean 'true' 33b565a Fix for #3424 and tests to prove it. 4820a1b Fix for #2604 Pure Ruby yaml generation 1c5b3d7 Fixes #3113 - When importing a manifest puppet needs to chill e6a720b Fix for #3412 install.rb should not put "." first in the tmp_dirs b1b3bcf Resolving conflicts with testing ba2a3af Fix 2239 (step five): introduce new Puppet::Transaction#stop_processing? flag and associated check thereof within the resource evaluation code. This should allow for the transaction to bail out of its processing if it finds that a stop has been requested, based on the state of Puppet::Application.stop_requested?. 9cb6841 Fix 2239 (step four): Refactored Puppet::Daemon's stop/restart methods to set status flags appropriately in Puppet::Application, and removed call to now-deprecated @agent.configure_delayed_restart. This should get the restart and stop behavior for daemons and their agents working nicely with the new global process status interface of Puppet::Application. 82f852a Fix 2239 (step three): Refactored Puppet::Agent to base starting/restarting behaviors and predicates on new run-status interface of Puppet::Application. edbe9b6 Fix 2239 (step two): introduce Puppet::Application.controlled_run method to provide simple status-restricted execution of a passed in block; this can replace the process status checks and properly handle delayed restart behavior for Puppet::Agent. 2cf647c Fix 2239 (step one): introduce global settings represeting application run state with methods for setting the state and appropriately-named predicates for querying state, all in the Puppet::Application class itself. To be used by Puppet::Daemon and Puppet::Agent and Puppet::Transaction for better response to TERM, INT, HUP. ce944a5 Fix unit tests in file/target.rb 481ddb8 Name change of event in ral/type/exec.rb 1ebc91e Use a helper function to evaluate a resource since the API has changed bfb1fa5 Allow skipped resources to process events a18b05d Actually invoke the allow_changes? method in ResourceHarness bf2f088 Generated resources' events are actually bound to the resource that generated them. 50ed75b Remove test that tests internal class structures which have changed. 1779079 Remove stale set_trigger 9154aca Since the types stored in resource's @parameters have changed, we need to also change include? method to be more robust. 2a2ab75 Fix test failures due to name changes of event symbols 0a72a98 Remove rollback test, since rollback was removed. 010907b Mark resource/status as failed if they are associated with a failing event. 17bccb0 Restore noop non-behaviours 8465cd0 Resolving conflicts with reinh:feature/master/single_executable 0f768ed Removing now-obsolete user tests 7627441 Fixing most failing test/ tests. 9d9b20f Fixing Configurer interface to transaction report eb0a4b5 Fixing fingerprint tests to work with new log validation f4ef039 Changing REST report integration test to use new interface fdefb64 Fixing "require" function to use new class interface 266bc08 Removing now-obsolete "retrieve" method on package type 67a75db Fixing broken selinux tests 2777e1f Fixing feature validation when passed one item 5aa26d0 Changing method profile for other event queueing ec7ea27 Refactoring event queueing for performance 68ce086 Changing the method profile of EventManager#queue_event 9919b14 Moving Metric management to the reports a9fc134 Removing mention of @changes in Transaction a18769d Cleaning up the report tests a bit 386b3e5 Fixing #2759 - reports now have complete change info fbd5b0a ResourceHarness now doesn't check params with no 'should' 3f6c948 Changing Transaction to use the new ResourceHarness 6051599 Fixing log message when changes fail 149d5ef Fixing some compatibility and old tests c30494f Renaming some methods in Transaction::Change 8d5f052 Adding Transaction::ResourceHarness class 6651aa4 Adding first version of Resource::Status class 4bb35a7 Fixing File type indentation 796d882 Removing last event collection transaction code e838bcc Solidifying the RAL/Event integration. 977595b Refactoring the Change/Event/Property interface 5776fe4 Cleaning up the Log initialization code. 2292b76 Refactoring the RAL interface to logging d93d80a Using Logging module in main Puppet module c6dd180 Adding tests for "Logging" module 242209d Correcting comments and making report timestamp internal a4b77f6 Failing in app/puppet if facts are not found f925475 Fixing file content logs 73f57f2 removing extraneous comment 4be8601 Adding Transaction events to Transaction reports 2aa579b Removing a redundant method in Report 5a8b460 Removing unused code and adding a couple of tests 9a78bee Adding tests for the 'report' log destination f2ed655 Extracting event management into a separate class 329527f Changing SimpleGraph.matching_edges to expect one event f8d7c44 Moving event creation to the resource ee9cff9 Reorganizing Property class to match current style 4212f1c Cleaning up Event creation 8280987 Adding "type" instance method to enhance Demeterness ad90900 Random code cleanup 32d34e9 Moving Ensure property into separate file 3c86666 Moving Parameter utility classes into separate files 2cbd9e8 Switching transactions to callback-based events 6a450c5 removing never-used code 379ac8f Moving log destination code into separate files b2d1728 fixed a couple of typos 7ab29c4 Unit tests for path changes a8245d8 Handle path elements with ticks and spaces 98581ad Fix builtins glob in single executable b4b07f5 Fix failing specs e7bc19a Rename puppet application spec to main c014c29 Renaming the old puppet executable deff92d Find both bin and sbin usage docs, fail gracefully 3c8d012 Fix application name in bin/ralsh be0ecf8 Initial puppet single executable 7a32777 Renaming applications, mostly removing 'puppet' b19a044 Some tests were leaking state when the test failed 5b2802c Typo in method call in test. 6a148e2 Supressing warnings (not really failures) in test/unit 06deee7 Fix test using wrong Puppet util filesetting group 74f5167 Mock user in SUIDManager tests 000d37a Removing resources generate tests 11379c0 Removing old test for service/debian provider 2b8125c Replace test/unit file write test with spec 164f1ce Allow adding single key to hashes fd427a5 Raise an error when appending not a hash to a hash 75c32f9 Fix #2389 - Enhance Puppet DSL with Hashes 9122ac5 Fix #2929 - Allow checksum to be "none" 73c8d0d Fix #3186 - require function set relationship only on the last class c5a4de2 Fixing #3185 Rakefile is loading puppet.rb twice c694c4d Fix #3150 - require function doesn't like ::class syntax 075f3c8 Added time module to tagmail report dfb8082 Fixed the return types were valid, and removed the copy paste error with the exception logic 6e16ea1 Resolving conflicts with ??? bca7e2c Add AIX package management support (installp&nim) b2c9455 Fixing #3148 Settings#without_noop when run with no noop setting 8bafc37 Move scope parenting & class_scope from Compiler to Scope 7403c6e [#3392] Better Rakefile, remove puppetmasterd spec de94f68 Fixing tests in pkg provider 4b55fb0 bug #3407 Part 2 f891ba2 Fixing #3407 Failing tests in spec/unit/node/environment.rb af9c19a Bug #3406 augeas spec fails if there is not a default provider 718a87a Bug #3402 Stub returning invalid type for :noop 88d6cd5 Bug #3401 Spec failed due to missing manditory setting in mock d9920bc Bug #3400 Bad mocks causing failing tests c6f02f2 Fix #3167 Duplicate constant warnings in dpkg.rb 70c71c5 Fixed Rails database tests 46f9d00 Fix #3117 - cert fingerprinting uses a method not available in ruby <= 1.8.6 04842ef Fixed test error message. fcce222 First shot at the OpenSolaris pkg(5) provider 3e9677f Feature #2839 - fingerprint certificate 91c44b4 Fix a few puppetd specs tests d77c9ac Revert "Feature #2839 - fingerprint certificate" 58a81ba Fixing #1054 - transaction reports are always sent 282b4b3 Removing some unneeded validation code from Transaction.new 66a3e6a Removing unused configurer code 2044550 Fix #2894 - exclude .pp files under modules files directories d31ff7e Adapt to method name change since 8971d8 a9fb82b Feature #2839 - fingerprint certificate a967b93 Feature #2395 - revoke when cleaning a certificate with puppetca e26e831 Updated test series 53869e9 Fix #2818 - scope variable assigned with undef are not "undef" 4226e01 Fix for #2959 (calling exit status on a nil report) 8971d8b Fixing #2596 - Node, Class, Definition are not AST 39d4a93 Adding a context method to the Errors module 22c642d Extracting language doc support into a module adc211a Adding module metadata bf40f4f Upgrading rspec for tests to 1.2.9 61d1911 Fix 2841 - Puppetdoc/RDoc parses realize function e63d23e Added tickets/master/2596 to the testing branch 41da962 Feature 2827 Option to disable managing internal files c9f40be Fixed #2568 - Add database option 'dbconnections' 2d137e2 Fixing #1507 - Adding a :ca_name setting 089ac3e Fixing #2617 - using the searched-for REST name 28e1bc6 Always using the CA_name constant instead of "ca" 2d4b795 Fix #1934 - detailed-exitcodes for puppetd 0f61816 Fix #2649 Allow ssl dir creation in --noop mode 53be6f8 Fix #2796 - Fix puppetdoc rdoc selector parsing 391786f Fix #2795 - puppetdoc rdoc reports a better error message b832d81 Fix #2784 - puppetdoc/rdoc didn't parse mono-instruction class content b1deb89 Covers the RDoc Puppet Parser with specs ced5a78 Fix #2376 - Display class/node inheritance in puppetdoc 8d9fbbd Fix #2703 - add 'require' to puppetdoc 41b7c3c Adding an example yaml node script 66a44dd type augeas: add 'incl' and 'lens' parameters c61335f Patch to address feature #2571 to add Oracle support to Puppet 0.25.5 ====== d71bd68 Updated CHANGELOG for 0.25.5 d88b357 Fixes incorrect line in partial CRL fix dec84e5 Fixed documentation issues exposed in #3772 0.25.5rc3 ========= 4daf8c3 Updated CHANGELOG for 0.25.5rc3 9214400 WIP - trying to fix #3460 9d3e98b Minimal footprint fix for #3751 (serialization 0.25.5 <-> 0.24.8) d481340 Updated Template documentation link 5a1a45c Update Red Hat spec file for 0.25.5 0.25.5rc2 ========= 2257605 Updated CHANGELOG for 0.25.5rc2 5258a0a Fixing #3533 - Removing all transaction cleanup bcde541 Fix for #2910 -- Tidy/matches is too tricky to use 5abe571 Bug #3451: Don't leak the terminus class setting from Puppet::Resource::Catalog's spec ebd924c Fix to the fix for #3295 ce233aa Write ssh_authorized_keys as user 6739bab Fix for #3558 -- source file reading speedup b0e3c61 Fix for #3556 Plussignment value melding 8a30495 Fixed #3655 - Puppet doesn't find installed packages with portage provider e4130af Fixed #3672 - Error message on duplicate fileserver mounts incorrect 1275a47 conf/redhat: Add notifempty to logrotate config 134204d Fixed stored configuration documentation 1aa98a6 Fixes #3653 - Changed default factpath value to better reflect plugins in modules 44f6d64 Partial fix to #2837 - changed warning message to debug 3a1b178 Fix #3555 - fix state of purged lists f6046ab Fix for #3577 -- to_yaml parameter in 0.25.5rc1 f351e2d Renamed all references to Reductive Labs to Puppet Labs cf7e696 Updated Rake tasks to no longer load puppet.rb b93924e Fix #3540 - name methods correctly 9bc2f28 Fixes #3295 - generate() now sets the working directory to the directory containing the specified command. 3ee6834 Added YARD task 99818ef Update man pages and partial doc fix to #3491 f988af3 Fixed #3532 - Typo in lib/puppet/ssl/host.rb f0e12e5 Fix #3496 - suppress transaction debug message 0.25.5rc1 ========= 0eea2f5 Updated version and CHANGELOG to 0.25.5rc1 57ae381 Modify SuSE spec file for 0.25.x and correct shebang lines for puppetd/puppetmasterd d90ec79 Fixes #3460 - Makes Puppet FHS compliant by moving /var/puppet to /var/lib/puppet ae0b0bf Fix for #3101 (bug in MRI 1.8.7) 9db066b Fixes #3419. OS X 10.6 Ruby doesn't set supplementary groups 306d082 Revert the guts of #2890 4eea77a Fail gracefully on packages that don't have the HOMEPAGE variable set (e.g. dev-lang/php). f5b8494 Fixed #3443 - Typo in mount type b0ef2c6 Fixes #3135 - darwin doesn't support 'mount -o remount' 7018cf5 Adding :catalog_terminus setting 978ab8a fixing obsolete comment in puppetd 6d13d0d Adding support for only using cached catalogs bc28715 Refactoring Configurer to enable the next feature ba43d7b Fix for #3366 - --tags '' treated as boolean 'true' 5ab5e8a Supressing warnings (not really failures) in test/unit e4df0b0 Fix test using wrong Puppet util filesetting group eeb3d74 Mock user in SUIDManager tests 9ea27db Removing resources generate tests 218e3e9 Removing old test for service/debian provider 1556938 Replace test/unit file write test with spec 2defc00 Fix for #3424 and tests to prove it. 44798b9 Fixed changelog Rake task 5d10f65 Fix #3155 - prevent error when using two matching regex in cascade fbedb99 Fixing #3148 Settings#without_noop when run with no noop setting 389c77b Another trivial follow-up fix for #2604: invalid path to zaml.rb 56b5753 Fix inefficient SimpleGraph#matching_edge 4b2b9eb Fix #3229 - use original value in case/selector regex matching 19863c0 Fix #2929 - Allow checksum to be "none" fd76142 Fixed puppetlast typo 3b4e782 Follow up for #2604, debug msg left behind. e44430b Fix for #2604 Pure Ruby yaml generation 74cd55f Fixes #3113 - When importing a manifest puppet needs to chill 7ec50a7 Fixes #3387 - Handle path elements with ticks and spaces d561a98 Fix for #3412 install.rb should not put "." first in the tmp_dirs 751df45 Fix #3186 - require function set relationship only on the last class a1d216c Fixed the return types were valid, and removed the copy paste error with the exception logic d532e6d Fixing #3185 Rakefile is loading puppet.rb twice 5aa596c Fix #3150 - require function doesn't like ::class syntax 3457b87 Added time module to tagmail report 71653a7 Fixed #3162 - tidy does not remove empty files when "size => 0" is set efd0f76 Fixed #3128 - Updated man pages and moved puppet.conf.8 to puppet.conf.5 ad4f94a Add version number to puppetlast display. 0533cea Forgot fakedata called in spec 6416f91 Fixing #2669 puppetmasterd integration has leaky state 8a3a205 Fix for #2327, check the return types from augeas and fail where appropriate 2ae7516 2047: Add a not_include into match 55f6239 Minor unit test fixes b7b7a1c vim: Improve function matching when functions contain ')' b3c363c vim: puppetDefArguments is contained by puppetDefine f9e05a8 Fix for #3094 (libdir should take ":" delimited path) b473264 Fix #1842 Net::HTTP#enable_post_connection_check doesn't work anymore 9419c2b Fix for #3035 (redhat services use init for source) 10becce Fix for #3077 (unit tests broken in 0.25.1-->0.25.4) 49a7185 Fix for #3085 (user_role_add pulls from same source as useradd) 1f086c2 Fix for #3114 (ruby's arbitrary limit on process groups too low) 0.25.4 ====== c5eef04 Updated CHANGELOG for 0.25.4 0025e13 Partial reversion of patch for #3088 to fix #3104 (Exception misreported) bfc9e45 Updated CHANGELOG for 0.25.4rc3 0.25.4rc3 ========= a91c476 Fix for #3088 (catching Exception also traps SystemExit) 1a263e2 Fix for #3089 (timestamp may now be a time object, not a string) 75634b7 Fix for #3093 (also need to be able to call pkgget_with_cat on class) 94e269c Uncommeniting the fix for #3001 9636b93 Updated CHANGELOG for 0.24.5rc2 0.25.4rc2 ========= d4319a5 Minimal fix for #3001 (failing to fetch metadata on dangling symlink) cdcbdc7 Fixing #2914 - pre/post hooks now work for transactions 67216aa Fix for #3075 (sshkey host_aliases ignored) e4462c0 Updated version and CHANGELOG for 0.25.4 0.25.4rc1 ========= 441879f Revert "Fix #2845 Cron entries using "special" parameter lose their title when changed" f7e1435 Updated rake task to fix gem build issue 49013f0 Updated version and CHANGELOG for 0.25.3 0.25.3 ====== f8c1b08 Reversion of pipe IO patch for testing on #3025 7f25805 Fix #1464 Mount complains about missing fields c99f394 Fix #2845 Cron entries using "special" parameter lose their title when changed 0a7e212 Fix #2887 'service' tests paths too early dd22b71 Replaced ugly gem creation task with slightly less ugly task d0efcc6 Added tasks directory to Rakefile and gem task file lists 8a6d66e Added puppetpackages task description eada68a Updated CHANGELOG for 0.25.2 0.25.2 ====== 6111ba8 Fix for temporary file security whole e7d98cc Fix for #2999 (absent package handling on solaris) 87136b4 Fix typo in documentation: wheter vs. whether. cbc2ef0 Partial rollback of refinements to fix for #2994 fd631b9 Do not close stdout or stderr in execute. f878fe8 Update Red Hat spec file for 0.25.2 4394c48 Updated CHANGELOG for 0.25.2rc3 0.25.2rc3 ========= 13cbf04 ReFix 2675 ending slash in directory should get stripped off 5c6f07b Use a pipe instead of a temp file for command output. 7e64393 Additional fix for #2994 (followed symlinks do not have checksums) 7e2e12b Fix for #2995 (don't fail to load PSON when UTF-8 missing) c84186a Revert "Fix for #2731 (problem communicating with processes in SELinux)" 74d9693 Updated man pages for 0.25.2 7e2b1e9 Fix for #2995 (don't fail to load PSON when UTF-8 missing) d1ff4b3 Fix for #2994 (undefined method "_file" message) 4d81511 fix #2987 - check correct hash entry 0.25.2rc2 ========= 682000b Updated CHANGELOG for 0.25.2rc2 e4bb529 Fix for #2967 (RFC-1123 problem and regression on wildcarded domains) 776be7c Updated CHANGELOG 0.25.2rc1 ========= a73f799 Updated version 26e7486 Fixing failing tests 05e897e Fix for #2881 (ralsh doesn't find individual parsed resources) e04f9e4 Code smell reductions 24654a2 Making provider/host/parser.rb compatible with host_aliases 49530ad Fixing #2964 updated resources cannot be collected until they are exported twice 6ab2453 Fix for #2731 (problem communicating with processes in SELinux) 0e5d264 Fix for #2940 (propogating nil rather than reporting the error) d60ea0e A slide down the RFC-1123 slope towards MS Windows compatibility and chaos b185801 Fixing #2960 Test Failed: 'Puppet::Type::Mount::ProviderParsed when modifying the filesystem tab should write the mount to disk when :flush is called 5e5c8b5 Fixing tests 0cb5e7d Fix for #2943 (Make puppet --apply respect --preferred_serialization_format) 0884035 Fix #2970 spec/unit/provider/ssh_authorized_key/parsed.rb has order dependencies b86decc Fix #2816 MySQL server has gone away 854c065 Fix for #2813 (alias propety v. alias metapram) 813cb58 Fix for #2765 (--no-fqdn regression in puppetrun) e9a0cb7 Fix for #2657 (retain old setting if config has syntax error) 727ee72 Fix #2966 spec/unit/util/queue.rb can't be run twice 8c8e921 Fixing #2963 spec/unit/util/autoload.rb depends on global state ea90daa Fix for #2965 (Chatty SELinux message) fc221ff Fixed RSpec deprecation error 037b99a Fixing #2958 inconsistent errors in spec/integration/bin/puppetmasterd.rb d11c750 Fix for #2951 (SELinux test errors on OS X) 0dc2dba Fix for #2890 (the cached certificates that would not die) 03f37ac Fix for #2950 (parens needed warning) 61fd460 Fix #2924 Test Failed: Puppet::Indirector::FileContent::FileServer when finding files should find plugin file content in the environment specified in the request 2432b23 Fixed test typo f5960ce Closed #2937 - Migrated a number of requires to features 2d88926 Fix for #2869 (SELinux tests failing under some load orderings) 18c5165 Adds partial IPv6 support to authstore 857047d Fix for #2567 (Authstore does unintended/pointless DNS lookups) dddbd28 Enabling steps for #2567 (Authstore does unintended/pointless DNS lookups) ea0a43f Fix 2948 Failing rests in spec/unit/rails.rb b6f4291 Fix #2923 failing tests in spec/unit/ssl/host.rb bf7c108 Fix #2677 Proper OIDs on puppet.schema for LDAP e0488b2 Fix #2808 puppetqd doesn't give an error when no config is given 7b2e2ba Fixing 2851 spec/unit/network/rest_authconfig.rb test descriptions change 1c69af2 Fixing 2855 Inadvertent zlib dependency 3528a7b 2850 spec/unit/application/puppetd.rb generates warnings if run with certain other tests d343af0 2876 spec/unit/indirector/catalog/compiler.rb changes behaviour a7fae47 Fix 2936 Insufficient mocks for webrick in spec/unit/network/http/webrick.rb b96b757 Fix for #887 (safely setting pager to cat for blastwave) d685f44 2633 file and line info on bad params in type/file 4326eb2 Fix for #2817 (links created even when links property set to follow) 118adc1 2875 spurious test failure in spec/integration/file_serving/content.rb 0f63a54 2877 race condition in webrick 4895329 Fix for #2921 (test not checking for what it really wanted) f47fa40 Fix for #2925 (accept higher versions of rspec) c261721 Fix for #2911 (Allow capital letters in selinux contexts) bf7d650 Fix for #2900 (rug output parsing too specific) cccbca4 Fix for #2786 (failed trying to backup directory in recursive purge) 6dfac97 Fixing 2907 rspec crash when spec/unit/application/puppetrun.rb is run after spec/unit/util/ldap/connection.rb 1c8d272 Fixing 2870 Spurious failures in spec/integration/ssl/certificate_request.rb 979440f Fixing 2862 spec/integration/file_serving/metadata.rb fails unless run with other tests e04d299 Fixing 2858 spec/unit/application/puppetmasterd.rb fails if run twice 53a9805 Minimal fix for #2822 9ac1ed6 Fix for #2863 (calling each on uninitialized tag list) bd9e06f Feature 2827 Option to disable managing internal files 8b66998 Fixing 2849 puppet changes sshkey alias every run if it is blank 7136c85 Fixed spellquote function documentation de16fd3 Updated yumrepo type documentation d1fa7cf Fixed --no-ca option in Gentoo also 8b5b4b6 Fixed incorrect command line in Red Hat sysconfig file 623d9c7 Fixing 2886: Failing specs in format_handler feb021f Avoiding rspec bug which causes 'be_all' tests to always pass 228f105 Removed some extraneous files from ext 01c98f6 Fixed #2798 - Correct issue with crontab provider on AIX f7c5ceb Fixing 2725 Error message when permissions incorrect on file server directory 07b94b4 2842 Format debugging should be removed 3abcc89 Fix #2783, take2 - puppetdoc should use environment to get modulepath/manifestdir f5dd6c8 Fix #2831 - puppetdoc doesn't cope with regex node 4a06379 Fixing #2857 (quote nesting error) 9a41c35 Fixing #2791 fs_find should follow symlinks 5629092 Added additional documentation to file mode attribute. dc8812c Fixing 2792 selinux tries to set properties on unsupported filesystes 57632a0 fix #2854 - parse timestamps cb6bc27 fix regex for non-installed packages 9cfe390 Fixing 2812 authorized_keys without comments fail 53b3b86 Fix for ticket #2844 (file recursion generated vs. explicit prefix) 8129caa Fix for ticket #2700 -- check for @explicit_waitforcert in puppetd --test e32f980 Fixed #1806 - supplementary groups are not reset be7ff82 Fix 2768 maillist resource is broken 48beaba simplify and fix portage provider 0ac0ce9 Implement tasks for git-based workflow. bd5dc64 Possible workaround for #2824 (MRI GC bug) c1e47a4 Fixing #2789 puppetrun fails without --tag 106c9a9 Fixing 904 RDoc::usage incompatible with rubygems 5ed2e26 rack: SSL Env vars can only be in Request.env 7f2e5fc Fix #2671, preferred_serialization_format does not complain about invalid values f0eaf20 Fixing #2764 ActiveRecord 2.1 support eaab789 Fix failing tests introduced by #2797 55d8ffa Fix #2810 - adapt tidy to new FileSet api 236bacc conf/redhat: Prevent killproc from removing /usr/sbin/puppetmasterd 41f025c Fixed ldap typo again 6c2daa3 Fix #2783 - Puppetdoc rdoc should use environment to get modulepath/manifestdir 5648666 Add Environment#manifestdir and small refactoring 74a877e Minimal fix for #2821 ("rake spec" is needlessly slow) cd10e6d Added package signing task ca56aa7 Least kludgy patch for #2675 adc0a4e Fix for #2661 and related issues 65f601a Fixing 2806 Specifying multiple tags fails to apply any of them e2c675e Updated generate function documentation to make it clear it runs on the master 53f40bd Fix #2681 Incorrectly duplicating resources 3fdc8ef Ticket #2770 (deserializing Exec[...]s with "\n"s) b172287 Fixing #2767 invoke-rc.d warnings 4013560 Fix #2797 - tags are not inherited by recursed file sub child f05a04e Fix #2784 - puppetdoc/rdoc didn't parse mono-instruction class content 38ec9fc Fix #2796 - Fix puppetdoc rdoc selector parsing 5f7177e Fix #2795 - puppetdoc rdoc reports a better error message 136949d Fixing #2631 show_diff sometimes doesn't happen cb3e5e1 Fix #2787 - Storeconfig doesn't store/update node ip and environment anymore ff23b57 Fix for #2670, Puppet silently ignores catalog_format ee13efa Add docs to Mac OS X package creation script and clean out old docs in the preflight b8470b8 Fix #2757 & CSR 92 (symlinks in recursively managed dirs) 5b750c2 Fix #2769 - default schedule are not defined eca338c Fix for #2772 (webrick test failures) b1c57e9 Al Hoang's patch for #2781, removing obsolete when/: syntax 50e9c98 Follow-on for #2724 - Adding an external node classifier 0.25.1 ====== 2f0b1e5 Updated CHANGELOG 20e5222 Fixing #2689 - file owner warnings are reduced 09fb3f7 Fixing #2752 - "require" loads "include" 6846c32 Fixing some recently broken Scope tests 0043392 Fixed typo in lib/puppet/indirector/ldap.rb 6b254eb Fix #2753 - Do not "global allow" plugins/modules mount if some rules have been parsed ff3a7bc Re-fixed #2750 - Stop disabling the CRL or checking for a disabled CRL 594c774 Revised partial fix for #2661 and related issues 73d04c6 Bug #2534 Raise error if property appears twice 7517572 Bug #1908 cron environment should allow empty vals febe707 Bug #1742 Invalid params to --color outputs 'nil' d383ab8 Use notice() in the versioncmp() docs 9dff716 conf/redhat/*.init: Use -p option to killproc f47a70f Ticket #2665: Regexp exception on ++ in package names b6e4ef3 Fixed #2750 - Set :cacrl to nil instead of 'false' in puppetd 2b57e06 Fix #2691 - Collection AR request should not include params if querying with tags e8bce7a Workaround #2668 - Compress facts so that the request size limit triggers less often e2ce790 Fixed #2737 - The zone provider needs to get acquainted with OpenSolaris aea1e5f Update Red Hat spec file for 0.25.1 fbdded7 Ticket #2685 (Type error in ssh_authorized_keys) 4d9f76a Fix for #2745 fakedata tests not working b4bcfe9 Fix for #2736, target doesn't work for ssh_authorized_keys ae528f6 Ticket #2734 PSON/JSON not serializing classes of a catalog f59f805 Bug #1900 Parsing of quoted $ in stdin 6ba122f Fixing #2735 - node classes are immed. added to classlist 0.25.1rc2 ========= bca3b70 Bundling of pure ruby json lib as "pson" ce46be5 Proposed alternative solution for #2664 (REGEX / DIV lexing) b0518c3 Fix for #2681 (duplicate resource) 8a73082 Fix #2707 config_version fails more helpfully 54ded1b Fixes #1719, this avoids calling the external binary *which* everytime we are looking for a binary 4c3c279 Updated required Facter version in README fcce46a Fixed #2702 - Set :outputdir to "doc" if not specified 3940dfb Fixed #2674 - createpackage.sh: problem finding install.rb 3b548f4 Fix #2699 - Use --masterport for PUPPET_PORT variable a75bf50 This updates the portage provider in three ways: ad86e9e Fixes #2688. Macauthorization provider now handles booleans internally correctly. d891f7a Ticket #2676 (a typo) bfba2cd Fix #2672 - Make sure nodenames keep their underscores when used as classname db67e5f Added rcov exclusion to Rakefile 0.25.1rc1 ========= 6912a7e Incremented version to 0.25.1 fd322da Fixes #1538 - Fixes the yumrepo backtrace in noop mode. 6551e86 Fix #2664 - Mathematic expressions mis-lexed as Regex a595033 Fix for #2654 (error generating error message) a951163 Fix #2642 - Runit provider rework 96b5087 Fix for ticket #2639 (Puppet[:user]/Puppet[:group] vs. 'service') af57483 Fixing #2632 - 'require' works for 0.25 clients d42bda1 Fixing relationship metaparam backward compatibility d53bc16 Adding version information to the catalog for compat 5f25589 Ticket #2626 (unhelpfull error messages) a1d3b04 Fixing #2617 - use the cert name as specified 8987509 Refactored Puppet packaging and gem creation 5c2ba47 FIXES 2616: Remove sync.syncronize and Puppet.info a53a77c Renamed test branch to testing in rake task d054fd9 Fixing #2656 - puppet parseonly tests don't hang cde70cf Fixes #2648. Spurious macauthorization parameter changes dcf0d0d Fix #2652 - Fix SELinux syntax error ba269f2 Fixed #2651 - Corrected install permissions on man page directories. 361c502 Fix #2638 - Allow creating several nodes with matching names 2283605 Added automatically constructed test branch task and file fd2a190 Fix for #2621 (JSON serialization of exec) 577a45b Fix #2622 - Puppetdoc in single manifest to console is broken d2d7070 Fix #2640 - Daemontools and Runit were not creating the enable symlink d21b266 Fix #2627 - regex node name could lead to invalid tag cb90528 Merged fix for #2601 b1554a1 Updated changelog task and CHANGELOG to version aware f5a106d Fix for #2637 (Webrick accpting connections on dead sockets) 19e98f9 Fixed #2608 - install.rb will not run on ruby 1.9.1 due to ftools being deprecated 40cd6d4 Fix for #2605 by falling back to alternative solution to #1963 630407d Make regsubst() function operate on arrays (feature #2491). a45c435 Fix for #2600 (wrong number of arguments under older mongrel) f2bc8aa Fixed #2634 - Added servicegroup_name parameter to serviceescalation type 7404e31 Fixs #2620 authconf interpolation, #2570 0-9 in domain names 4344339 Fix for ticket #2618 (stubbing so redhat tests run under debian) c2e26b9 vim: match regexp language features 1494bd7 Require active_record/version to support ActiveRecord < 2.3 a5c56fc Fixed #2607 - Added Facter dependency for Puppet Gem 0.25.0 ====== b1eddbb Updated and created new CHANGELOG format 994d6e0 Adding tests for the #2599 fix 42ab73f Ticket #2525 don't fail find_manifest on invalid module names a0f0dcc Updated permissions on test files d45812b Refactoring tests to reduce code size, increase coverage, and make Luke happy. aba2f66 This further normalizes the handling of init-style services (including the redhat "service" wrapper script). Removes special case handling of non-zero exit code in redhat (base already did this) and centralizes scattered @resource[:has_____] checks. Tests that proper versions of each are called and one level of fallbacks. fb236a0 Combined fix for #2525, #2552 -- RedHat service issues d40b942 Fixed #2589 - Renamed zfs delete to destroy and added tests 4aa7fce Monkey patch to improve yaml compatibility between ruby versions 1f6c74d Fixed typo in parser test 2e9b706 Updated Red Hat spec file and RH patches for 0.25.0. 19815dd Fixing #2592 - you can escape slashes in regexes ea58478 Fixing #2590 - modulepath is not cached inappropriately 1a3d0c8 Fixed #2593: puppet init script status command not returning proper exit code 8dabc72 Update documentation string to reflect actual intent of Puppet::Node::Facts::Rest b30a3c7 Fixes #2581. Use new 10.6 global launchd overrides file for service status/enabled 7f05469 Fixed Naginator link e589cd3 Fixing #2582 - / no longer autorequires / 3342b73 Fixing #2577 - clarifying and demoting the deprecation notice d397f8d Fixing #2574 - autoloading finds plugins in modules 800a78b The first regex node now matches first 6750aeb Fixing #2563 - multiple regex nodes now work together b728b93 Fixes #724 - false is equivalent to 'ruby -W1' a9d5863 Fix parser error output ee4f6ba Fixing #2551 - fixing content changed logs c8f859e Fix for test isolation portion of Ticket #2511 6fa9271 Fixing #2549 - autoloading of top-level classes works again c752680 Fixing a heisenbug resulting from a race condition ea417d6 Fixing #2460 - puppetmasterd can now read the cert and key a49915a Not using the service user in settings when it's unavailable 14ec838 Explicitly loading all facts in the directory service provider 5ee6602 Adding an 'exists?' delegator from user type to provider 06fcece Switching the owner/group settings to use symbolic values 4eb325a Fixing the yamldir group to be a group instead of user 058514a Moving Setting classes into separate files b0f219a Removing chuser on darwin restriction 7f749cb Fixing a ruby warning in the authstore test c0da3bf Fixing #2558 - propagating recent fileserving changes ff39bc7 Fixes #2550 Handles case where metadata is nil 47dee83 Ticket 2559 -- parseonly ignored specified file a4f6896 Fixed #2562 - Recognize the usecacheonfailure option again Signed-off-by: John A. Barbuto e408d6c Refactoring the Module/Environment co-interface 796ba5c Fixing #1544 - plugins in modules now works again 6bd3627 Adding a global cleanup to the spec_helper 0ef5f22 Removed misguided case sensitivity tests c1967bb Fixes #2513. debian service provider now uses invoke-rc.d to determine enabled? status 7e09247 Fixing fact-missing problem when puppet.conf is reparsed a35e9bf Fix for #2531; adds tests to confirm problem and related cases, notes fixes specific issue by eliminating the specal case for opaque strings which caused them to be strings when everything else was arrays; adds nots and pending tests where FQDN support could be added but stops short of a full refactor. 299eadb Fixed #2530 - Fixed status setting in the SMF provider e6a7e82 Fixed spec typo 75c6e4a Fixes #2493 b62d966 conf/redhat/*.init: Fix condrestart/try-restart e9fbd4c conf/redhat/client.init: Fix #2123, status options on older RHEL 0461a02 Updates to Solaris smf files to reflect new binary locations 55a9cdb Fix #2517 - Stack overflow when CA cert missing 601a2e5 Fix #2516 - Fix format detection when content-type contains charset d86bc88 Fix #2507 - Add missing integration tests aad3b76 Fix #2507 - Exported resources were not correctly collected. 63cb1ad Fixes #2503 c129f2a Fixes #2360 - Removed annoying log message b1ffffa Fixed #2525 - Wrong method being overridden in Red Hat services a88fc4d Fixing more tests broken from missing libraries 9a356ab Fixing ActiveRecord Indirector tests to skip w/out Rails acc5a96 Fixing #2489 - queue integration tests are skipped w/out json 1a5c5b3 Fixing #2508 - removing mention of ActiveRecord 2.3 0cb9072 Fixing #2541 - file cache is more resilient to failure 23948d0 vim: Mark puppetFunction values as contained 79a4339 Add shellquote() function. 79d705f Fixes #2499. Allows execs to specify an array for the returns parameter b611c34 Updated fix for #2481 f385072 Revert "Fxied #2481 - Added status and restart overrides for Red Hat service provider." cc379b6 Fixed #2498 - logcheck update 85a3633 Removed extraneous debugging 0.25.0rc1 ========= bf94de9 Updated two more tests 5b87dba Logs now assume resource params have metadata 1410bed Adding metadata delegation from param to resource 3ab3a5c Removing unnecessary debug output 488e368 Adding integration tests for #2371 (backup refactor) f1406bf Adding many tests for #2371, and slightly refactoring 8f60f0c Fixes for Redmine 2371. cd224c6 Fixes #2464, #2457. Deprecate reportserver for report_server. Add report_port setting. Add tests. 401a9ec Fixing #2484 - "format missing" messages are better f6cc598 Fixes #2483 - Log only copies metadata from RAL objects 7c4c00f Fixed #2486 - Missing require 'monitor' in parser_support.rb ea34ee6 Added R.I.Pienaar's extlookup.rb to the ext directory 36d3f58 Added example conf/puppet-queue.conf 967eb9f Fxied #2481 - Added status and restart overrides for Red Hat service provider. c702f76 rack: SSL Env vars can be in Request.env or ENV ca17b3c rack: don't directly use the forbidden HTTP_CONTENT_TYPE env var (fixes rack specification conformance) a002e58 Removing old filebucket test d8de446 Cleaning up tests for #2469 266aafa default server in remote filebuckets 1f8ef60 Fixes #2444 - Various JSON test failures 11c0fb7 Fixed #2294 - Classes sometimes cannot be found 7e5b562 Adding #2477 - puppet can apply provided catalogs 97274ad Fixing problems my Feature refactor caused 6fb8bf6 Fixing ruby warning in definition test b3545fc Fixed global deprecation error in useradd Unit tests dc24472 Adding a test for the Exec type 58d9587 Speeding a test up through stubbing d4d8372 Fixing a small test by stubbing instead of mocking f7e1c36 Fixing a test broken by the regex features 54a225d Fixing tests broken by caching autoload results 1ce31b4 Migrating Handler base tests from test/ to spec/ cc3f56a Migrating Feature tests to spec 21d1d25 Fixing cron test to match new behaviour 849fa67 Migrating tests to spec and removing an obsolete test 6f458cc Logging the configuration version we're applying ac58e27 Configuration version information is now in logs 6ed0103 Adding support for an external catalog version 39320b8 Cleaning up duplication in another test file 25fae5f Removing duplication in the test structure 36c0662 Simplified Rakefile and moved tasks to tasks/rake directorya b45ccf8 Implement node matching with regexes 58a73b5 Make sure node are referenced by their names 3ebf148 Enhance selector and case statements to match with regexp ef68967 Fix #2033 - Allow regexp in if expression 17e62b1 Add AST::Regex, an AST leaf node representing a regex 4f9545f Add regexes and regex match operators to the parser 0ccd259 Add regex, match and not match token to the lexer 201ae59 Allow variable $0 to $9 to be interpolated, if ephemeral f357a91 Implement ephemeral scope variables d40ef29 Signed-off-by: Eric Sorenson 6d22afb Modifying the REST client error to make server errors more clear 21f477a Fixes #2472. Load Facter facts when ralsh is invoked, plus test. 2e41edb Update CHANGELOG.git ebb5a1f Fixed ci_spec task for RubyGems 1.3.5 b6b903e Fixes #2461. Provide new and old code paths for macosx_productversion_major with deprecation warning 26b0c70 Fixing typo in two tests which caused them to always pass 76fc2b1 Fixing #2440 - catalogs can now be compiled on demand 832b6ff Exiting from app failures instead of raising 4ea3f17 Minimal patch to fix #2290 (/tmp permissions) 08ff9e8 Fix #2467 - rack: suggest putting puppet/lib at beginning of RUBYLIB search path fb60f90 Fix #2465 - Default auth information is confusing with no auth.conf 0ca9b53 Fix #2459 - puppetdoc added namespace classes as RDoc modules instead of classes 18b5d61 Fix #2429 - vim: class/define/node should only be followed by whitespace da828a4 Fix #2448 - fix the broken runit provider and tests 3898436 Fixed #2405 - Mount parameter "dump" doesn't accept all valid values on FreeBSD 9825bec Fixes #2362. Do not validate users/groups when supplied with numeric uid/gids 450a19c Fix #2454 - Definition named after a module don't show in puppetdoc 8551ece Fix #2453 - puppetdoc mixes long class names that look alike e3ee594 Fix #2422 & #2433 - make sure puppetdoc transform AST::Leaf boolean correctly b3b76df Fixing #2296 - overlapping recursions work again 9120712 Fixing mocks to remove warnings eeec8e9 Fixing #2423 - no more strange dependency cycles 7d40f9e Fixing #2443: Adding debugging guidance to dep cycle errors b4facb0 Fixing a test broken by changing the default os x package type b418921 Fixing selinux tests broken in the fix for #1963 719e76b Fixing #2445 - fixing the mount test mock f13f08d Minor fix to URL for LDAP nodes documentation 7c859a7 Fixing #2399 - removing client-side rrd graphs f6d6145 Fixing #2421 - file renaming errors now propagate db82523 Fixes #2438, get major OS X version from Facter and replace Puppet::Error invocations with fail builtin 22145e7 Update install.rb to cope with all OS X versions, not just 10.5 935c463 Fixing #2403 - provider specificity is richer and better d95b687 Fix #2439 - let puppetdoc use loaded_code ef5c4ae Fixed #2436 - Changed ralsh to use Puppet::Type.new and avoid deprecation notice 0c18013 Fixes #2430 - Stock apache2.conf for passenger incorrect c383ceb Make pkgdmg default Darwin provider, make confines consistent on Darwin package providers. 98599c4 Convert to using sbindir for OS X packages, clean out previous executables in bindir c659743 Fix #2425 - make sure client can contact CA server with REST 17205bb Fix #2424 - take 2, make sure default mounts allow every clients f2c55cc Fix #2378 and #2391 tests 8bbd8b4 Fix #2424 - File server can't find module in environment effaf80 Fix small typo in the fix for #2394 a06094e Feature #2378 - Implement "thin_storeconfigs" b2a008e Fix #2391 - Exported resources never make to the storeconfigs db 8f82407 Fix #2261 - Make sure query string parameters are properly escaped c86d44e Fixed #579 - puppet should try to clear solaris 10 services in maintenance state 910a5e2 Fix #1963 - Failing to read /proc/mounts for selinux kills file downloads ba824e9 Fixing #2245 - provider lists are not lost on type reload eb40966 Ruby no longer clobbers puppet autoloading a42e878 deprecate NetInfo providers and examples, remove all NetInfo references and tests. 22f5632 Fixed #2410 - default acl logs as info instead of warn. 65b0137 Adding test for current auth config warning. 74f5ad4 Fixed #2394 - warn once on module mount deprecation. f46a52a Add test for current module mount deprec warning. 858d333 Fixes #2258,#2257,#2256. Maintain correct type for integers/booleans, allow correct values, and fix rule array handling 44f127f Added Markdown mode to puppetdoc to output Markdown. 8a8ce9d Excluded directories from rcov coverage report d152c5e Allow boolean value for boolean cli parameter 911b490 Fix #2364 - Associates the correct comment to the right statement faefd92 Make sure the parser sees the correct line number 869ec27 Fix #2366 - puppetdoc was parsing classes in the wrong order 4c659b7 Added rcov coverage to Spec tests 1fd98b1 Fixes #2367 - Mongrel::HTTPRequest returns a StringIO object 8b09b83 Fix #2082 - puppetca shouldn't list revoked certificates ea66cf6 Fix #2348 - Allow authstore (and REST auth) to match allow/deny against opaque strings 1e83aad Fix #2392 - use Content-Type for REST communication aaca17a Fixed #2293 - Added cron syntax X-Y/Z and '7' for sunday cddc365 Switching to LoadedCode from ASTSet fc1f8cd Adding a special class to handle loaded classes/defines/nodes 325b8e4 Fix #2383, an incompatibility with early ruby 1.8 versions 46112da Fixing #2238 In some cases blank? is not available on String. cdd1662 Fixing #2238 - Deal with nil hash keys from mongrel params 769c8aa Final fix to CI test rakes a6816ff Set ENV['PATH'] to an empty string if non-existent 64a4720 Fix to CI rake tasks 5680cd5 Fixing #2197 - daemontools tests now pass 603b9cf Change the diff default output to "unified" 9bc9b5c Added missing colon to suntab 0f2d70d Fixed #2087 and refactored the code that gets the smf service state 0.25.0beta2 =========== 3f070c1 Using the logging utilities to clean up module warnings feb7f89 Fixing #1064 - Deprecating module 'plugins' directories ccf4e69 Removing deprecated :pluginpath setting 4036de9 Fixing #2094 - filebucket failures are clearer now ed876e0 Refactoring part of the file/filebucket integration bd81c25 Adding tests for file/backup behaviour c45ebfa Fixed pi binary so --meta option works and updated documentation d2080a5 Fixing #2323 - Modules use environments correctly b9e632f Fixed #2102 - Rails feature update fixed for Debian and Ubuntu 1c4ef61 Fixed #2052 - Added -e option to puppet --help output d332333 Fix #2333 - Make sure lexer skip whitespace on non-token 5fbf63c Updated split function and add split function unit tests (courtesy of Thomas Bellman) a585bdd * provider/augeas: strip whitespace and ignore blank lines a94d2de Fixed pi tests 5f7455e Fixed #2222 - Cleanup pi binary options and --help output 134ae3e Fixing #2329 - puppetqd tests now pass de55e19 Cleaning up scope tests a bit e4ae870 Fixing #2336 - qualified variables only throw warnings 607b01e Fix #2246 - take2: make sure we run the rails tag query only when needed 06b919d Fix collector specs which were not working 2945f8d Make sure overriding a tag also produces a tag e142ca6 Removed a unit test which tested munging which is no longer done in the type d8ee6cf Clearn up a parsing error reported by the tests 446557f vim: several improvements + cleanup 9152678 Fixed #2229 - Red Hat init script error b5a8c4d Fix #1907 (or sort) - 'require' puppet function 74730df #2332: Remove trailing slashes from path commands in the plugin 1a89455 Changing the preferred serialization format to json 0de70b7 Switching Queueing to using JSON instead of YAML 7b33b6d Adding JSON support to Catalogs c0bd0aa Providing JSON support to the Resource class c16fd1b Adding a JSON utility module for providing Ruby compat f059c51 Adding JSON support to Puppet::Relationship 7f322b3 Adding a JSON format 7666597 Allowing formats to specify the individual method names to use d40068f Allowing formats to specify the methods they require 024ccf5 Adding a "json" feature c8b382d Fix some tests who were missing some actions f9516d4 Make sure virtual and rails query use tags when tag are searched b5855ec Make sure resources are tagged with the user tag on the server d69fffb Fix #2246 - Array tagged resources can't be collected or exported 6ce0d1e Partial fix for #2329 4f2c066 Removed extra whitespace from end of lines 97e6975 Changed indentation to be more consistent with style guide (4 spaces per level) 41ce18c Changed tabs to spaces without interfering with indentation or alignment f3b4092 Fix #2308 - Mongrel should use X-Forwarded-For 7b0413e Fixes Bug #2324 - Puppetd fails to start without rails 48d5e8c Enhance versioncmp documentation ef56ba5 * provider/augeas: minor spec test cleanup d322329 * provider/augeas: allow escaped whitespace and brackets in paths 9735c50 * provider/augeas: match comparison uses '==' and '!=' again dbfa61b * provider/augeas (process_match): no match results in empty array 386923e * provider/augeas: remove useless checks for nil 171669a * provider/augeas: simplify evaluation in process_get/match 51cc752 * provider/augeas (open_augeas): use Augeas flag names, not ints 4951cdf * provider/augeas: ensure Augeas connection is always closed 0d5a24d * provider/augeas: minor code cleanup cea7bb5 * provider/augeas (parse_commands): use split to split string into lines 95bd826 * provider/augeas: remove trailing whitespace (no functional change) 7c5125b Brought in lutters parse_commands patch and integrated it into the type. This includes reworking the get and match commands as well. This change introduces a few small changes. These are: 6ce8154 Removed --no-chain-reply-to in rake mail_patches task 4ef7bba Removing --no-thread from the mail_patches rake target 508934b Fixing a bunch of warnings fb0ed7a Fixing tests broken by a recent fix to Cacher 650029e Always providing a value for 'exported' on Rails resources f1dba91 Fixing #2230 - exported resources work again 5522eb8 Disabling the catalog cache, so puppetqd is compatible with storeconfigs abbb282 Fixing the rails feature to be compatible with 2.1+ 907b39b Using Message acknowledgement in queueing 42247f0 Fixing #2315 - ca --generate works again d7be033 Fix #2220 - Make sure stat is refreshed while managing Files e4d5966 Added puppet branding to format patch command 00d5139 vim: Remove another mention of 'site' from syntax 9067abd vim: Highlight parameters with 'plusignment' operator 736b0e4 vim: Highlight strings in single quotes ce01c95 vim: Clean up syntax spacing 3af2dbf JRuby OpenSSL implementation is more strict than real ruby one and requires certificate serial number to be strictly positive. 62534a1 Logging when a cached catalog is used. ff5c44f Changing Puppet::Cacher::Expirer#expired? method name e3d4c8e Fixing #2240 - external node failures now log output bc1445b Fixing #2237 - client_yaml dir is always created by puppetd e0c19f9 Fixing #2228 - --logdest works again in puppetd and puppetmasterd ab34cf6 Fixing puppetmasterd tests when missing rack 9d5d0a0 Fixing the Agent so puppetrun actually works server-side b0ef08b Fixing #2248 - --no-client correctly leaves off client b83b159 Fixing #2243 - puppetrun works again 3d2189f Fixed #2304 - Added naggen script to directly generate nagios configuration files from a StoreConfigs Rails database 700ad5b Sync conf/redhat/puppet.spec with Fedora/EPEL 3ec3f91 Fixed #2280 - Detailed exit codes fix f98d49f Fixing #2253 - pluginsync failures propagate correctly d860a2f Fixing a transaction test that had some broken plumbing a728757 Refactoring resource generation slightly 6e824d8 Adding a Spec lib directory and moving tmpfile to it 1d69dbf Extracting a method from eval_resource in Transaction 7650fb2 Not trying to load files that get removed in pluginsyncing 3995e70 Fix #2300 - Update ssh_authorized_key documentation cb4a4d3 Changed version to allow Rake to work. Minor edit to Rakefile 99f666f enable maillist on centos, redhat, fedora e13befa Fixing #2288 - fixing the tests broken by my attr_ttl code a406d58 Fix for #2234: test fails with old Rack version c189b46 Fixing #2273 - file purging works more intuitively 138f19f Caching whether named autoloaded files are missing 415553e Adding caching of file metadata to the autoloader d489a2b Adding modulepath caching to the Autoloader 5f1c228 Adding caching to the Environment class 047ab78 Adding TTL support to attribute caching 6a413d2 Fixed #2666 - Broken docstring formatting 469604f Deprecating factsync - pluginsync should be used instead d39c485 Added spec and unit tests to the Rakefile files list and fixed CI rake tasks e1a7f84 Added install.rb to Rakefile package task e180a91 Fixed #2271 - Fix to puppetd documentation 4bf2980 Protecting Stomp client against internal failures f4cb8f3 Adding some usability bits to puppetqd a18298a Refactoring the stomp client and tests a bit 2771918 Relying on threads rather than sleeping for puppetqd 07ff4be Fixing #2250 - Missing templates throw a helpful error 7ce42da Fixing #2273 - CA location is set correctly in puppetca e1779c7 RackXMLRPC: buffer request contents in memory, as a real string. fb957cc Modules now can find their own paths c608409 Moving file-searching code out of Puppet::Module 83ba0e5 Fixing #2234 - fixing all of the tests broken by my bindaddress fix 4f3a67f Fixing #2221 - pluginsignore should work again 2d580c2 Fix snippets tests failing because of activated storeconfigs 8c718c9 Fix failing test: file.close! and file.path ordering fix 17f2c7d Confine stomp tests to Stomp enabled systems 6a80b76 Fix some master failing tests 172422f Fix bug #2124 - ssh_authorized_key always changes target if target is not defined f945b66 Fixing #2265 - rack is loaded with features rather than manually 5aef915 Added .git to pluginsignore default list of ignores 6db5e8d Cleanup of the Puppet Rakefile and removal of the requirement for the Reductive Build Library 5cc4910 Fix #1409 once again, including test a6af5bf Added split function 0.25.0beta1 =========== 2dd55fc Fixing #2200 - puppetqd expects Daemon to be a class c016062 Removing unneeded test stubs 1a2e1bc Fixing #2195 - the Server class handles bindaddress df3a8f6 Minor fixes to function RST documentation 305fa80 Remove the old 0.24.x rack support, which is now useless cruft d85d73c puppetmasterd can now run as a standard Rack application (config.ru-style) d6be4e1 Add XMLRPC compatibility for Rack 6e01e7a Puppet as a Rack application cc09c1a Use FileCollection to store the pathname part of files d51d87e Add an unmunge capability to type parameters and properties a1c0ae0 Fix #2218 - Ruby YAML bug prevents reloading catalog in puppetd bf054e1 Fixes #2209 - Spec is failing due to a missing require 1c46205 Fix #2207 - type was doing its own tag management leading to subtile bugs 929130b Moved puppetqd binary 36d0418 Fixed #2188 - Added set require to simple_graph.rb 51af239 Fixed puppetqd require and tweaked stomp library error message dc0a997 Fixes #2196 - Add sharedscripts directive to logrotate 4d3b54e Added puppetqd binary to Rakefile 1fee506 Updated version to 0.25.0beta1 0fbff61 Updates to CI tasks aa6dcef Fixing #2183 - checksum buffer size is now 4096b c0b119a Fixing #2187 - Puppet::Resource is expected by Rails support 5ec4f66 Adding an 'Exported' attribute to Puppet::Resource adff2c5 Removing a non-functional and horrible test 606a689 Making sure the cert name is searched first f489c30 Removing an "inspect" method that often failed in testing 93c3892 Removing deprecated concurrency setting usage in rails 6f8900d Always making sure graph edges appear first 3f7cd18 Reverting part of the switch to sets in SimpleGraph bf46db7 Fixing rails feature test 5f6b5c0 Failing to enable storeconfigs if ActiveRecord isn't available 8ed7ae3 Fixing the Rails feature test to require 2.3.x a0c1ede Modifying the Settings#handlearg prototype 284cbeb Fixes #2172 - service provider for gentoo fails with ambiguous suffixes 68ba1f1 SMF import support working and documentation update f1f0a57 Fixes #2145 and #2146 ec478da Fix configurer to retrieve catalog with client certname e623f8a Unify auth/unauthenticated request authorization system 037a4ac Allow REST auth system to restrict an ACL to authenticated or unauthenticated request 3ad7960 Fill REST request node with reverse lookup of IP address c0c8245 Refactor rest authorization to raise exceptions deeper aac996e Add environment support in the REST authorization layer 72e28ae Fix some indirector failing tests dc1cd6f Fix #1875 - Add a REST authorization system 8523376 Enhance authconfig format to support uri paths and regex 22b82ab Add dynamic authorization to authstore 15abe17 Add RSpec unit tests for network rights 86c7977 Add RSpec unit tests for authconfig f04a938 Adding support for specifying a preferred serialization format 6a51f6f Fixing the FormatHandler test to use symbols for format names b249f87 Fixing #2149 - Facts are passed as part of the catalog request 1cde0ae Adding better logging when cached indirection resources are used 828b1ea Fixing #2182 - SimpleGraph#walk is now iterative 9f32172 Fixing #2181 - Using Sets instead of Arrays in SimpleGraph 7a98459 Removing code that was backported and is now not needed d06bd3e Finishing class renames b694b3c Fixing tests that apparently only worked sometimes 93246c0 Removing the old rails tests. 5cb0f76 Fixing some rails tests that sometimes failed bdbf9db Added class_name tags to has_many relationships 2e62507 Adding time debugging for catalog storage to active_record a705809 Adding defaults necessary for queueing 8a67a5c Adding daemonization to puppetqd fcd1390 Adding Queueing README 9b90f34 Using a setting for configuring queueing 444ae9f Adding puppetqd executable. 22ae661 Adding "rubygems" and "stomp" features efe6816 Removing unnecessary parser variables when yaml-dumping a3b1e8c Add queue indirection as an option for catalog storage. bccfcc9 Introduce abstract queue terminus within the indirection system. 7947f10 Introduce queue client "plugin" namespace and interface, with a Stomp client implementation. 80dc837 renaming a method f7ccaaa Adjusted parameter name and puppet tag classes to use new cache accumulator behavior for storeconfigs. 75f1923 Initial implementation of a "cache accumulator" behavior. bab45c4 Saving rails resources as I create them, which saves about 10% 7a91e1f Changing rails value serialization to deal with booleans c30ede5 Adding equality to ResourceReference 589f40f Adding some more fine-grained benchmarks to Rails support 042689e Adding a Rails-specific benchmarking module 89d9139 Adding simplistic param_name/puppet_tag caching ea4e3b2 Adding more time debugging to Rails code, and refactoring a bit 6314745 Refactoring the Rails integration be30a61 Adding a common Settings method for setting values 863c50b Switching to Indirected ActiveRecord a137146 Adding ActiveRecord terminus classes for Catalog b9c95eb Adding ActiveRecord terminus classes for Node and Facts. 8d0e997 Fixing #2180 - Catalogs yaml dump the resource table first 4e0de38 Partially fixing #1765 - node searching supports strict hostname checking c1f562d Removing unused Node code e6b4200 Fixing #1885 - Relationships metaparams do not cascade 50e0f3d Fix #2142 - Convert pkgdmg provider to use plists instead of string scanning for future proofing c1be887 Fixing #2171 - All certificate files are written with default perms e2201d6 Fix #2173 - fix running RSpec test by hand 424c87b Fix #2174 - Fix RSpec rake targets 7ab7d9f Fixing #2112 - Transactions handle conflicting generated resources 84e6c1b Adding another stacktrace for debugging 4793334 Fixing puppet -e; it got broken in the move to Application 7398fa1 Partially fixing #2029 - failed caches doesn't throw an exception 88ff9c6 Fixing #2111 - SimpleGraph only creates valid adjacencies 36594fe Switching to new() in the Puppet::Type.instances() class method edcbab5 Removing duplicate method definition from SimpleGraph d8eaca8 mini daemon to trigger puppetrun on clients without puppet listen mode d2c417e Fix #2113 - Make temp directory 173b5f0 Adding #2122 - you can specify the node to test with puppet-test a677e26 Fixing all tests that were apparently broken in the 0.24.x merge. e016307 Fixing Rakefile; apparently there was a rake or gem incompatibility 62dad7a Fix #2107 - flatten resource references arrays properly cbee426 Fix #2101 - Return to recurse=0 == no recursion behavior 3b4816b Fix #2101 - fix failing test f089e11 Fix #2101 - fix recurselimit == 0 bad behaviour 1b4eae7 Added rake ci:all task 3f61df8 Fixed #2110 - versioncmp broken 830e1b1 CHANGELOG updates 3e0a9cd Moved of puppetd, puppetca, puppetmasterd, puppetrun binary from bin to sbin 6ddebf4 Fixed #2086 - Fixes to make building tarballs easier 33d3624 Fix #1469 - Add an option to recurse only on remote side 77ade43 Forbidding REST clients to set the node or IP 0179e94 Fixing #1557 - Environments are now in REST URIs a497263 Adding explicit optional attribute to indirection requests 3e95499 Removing an unused source file 97975e1 Adding a model accessor to the Request class b6116fe Requests now use default environment when none is specified 15740fe Moving the REST API functions into a module ef4fa68 Using the Handler for the REST api on both sides of the connection 8b13390 Adding REST::Handler methods for converting between indirection and uris edf00db Adding environment support to the REST URI a064ed1 Moving the query_string method to Request ff9c3ca Adding tests for the REST query string usage 87ec08e Fixing #2108 - pi should work again af4455e Fix #1088 - part2 - Add rspec tests b15028f Fix #1088 - Collections overrides b24b9f5 Fixed #2071 - Updated LDAP schema 88aa1bc Fixing tests broken in previous commits f8dea98 Fixing #1949 - relationships now use attributes instead of a label d0fc2f5 Correctly handling numerical REST arguments 90afd48 Not passing file sources on to child files 858480b Correctly handling non-string checksums 27aa210 Removing unnecessary calls to expire() 5329b8a Passing checksums around instead of file contents 71e4919 Moving default fileserving mount creation to the Configuration class 09bee91 Fixing #2028 - Better failures when a cert is found with no key cf1cb14 Moving the clientyamldir setting into the puppetd section 5f73eb5 Fixed #1849 - Ruby 1.9 portability: `when' doesn't like colons, replace with semicolons e40aea3 Fixed metaparameter reference to return str 5df9bad Fixed #2016 - Split metaparameters from types in reference documentation 7e207a1 Fixed #2017 - incorrect require 417b5a5 Fixing #1904 - aliases are no longer inherited by child files c0d5037 Removing or fixing old tests 262937f Correctly handling URI escaping throughout the REST process b800bde Refactoring how the Settings file is parsed d7864be Relying on 'should_parse_config' in the 'puppet' application bd8d097 Providing better indirection authorization errors d3bc1e8 Adding pluginsyncing support to the Indirector 00726ba Moving Request and Fileset integration into Fileset. 058266f Switching the ModuleFiles Indirection terminus to the new Module/Env api 19b8534 Migrating the old FileServer to the new Module/Environment code 1be7c76 Using the Environments to handle a lot of Module searching 2b4469d Environments now use their own modulepath method. 94de526 The 'Environment' class can now calculate its modulepath. 2573ef1 Added support for finding modules from an environment 9c18d5a Adding support for finding all modules in a given path. 4b5ec82 reformatting the environment tests 458642b Supporting multiple paths for searching for files. eec1cad Adding support for merging multiple filesets. bdf3a80 Adding new methods to Puppet::Module. a7be174 Refactoring Puppet::Module a bit. 7ceb437 Only using the checksum cache when we're using a host_config catalog 5fd182d Fixing fileserving to support strings or symbols 7bc41ce Adding clarity to query string handling in REST calls 992231a Some small fixes to provide better debugging and load a library 0304a78 Providing better information when an exception is encountered during network communication 4a7cba3 Stubbing tests that were affecting other tests 3105b5b Fixing a warning in a test c854c59 Fixing a syntactically invalid application test 0f43fd6 Move --version handling to Puppet::Application 156fb81 Move puppetd to the Application Controller paradigm 0c71c5c Move puppetdoc to the Application Controller paradigm e317fa9 Move ralsh to the Application Controller paradigm 81f5438 Move puppetrun to Application Controller paradigm 3390d8d Move pi to the Application Controller paradigm 8265d6e Move puppetmasterd to Puppet::Application af219bf Move puppet to the Application Controller paradigm d51398c Move filebucket to the Application Controller paradigm 9b9e5e8 Move puppetca to the Application Controller paradigm 97e716a Introducing the Application Controller 495ad66 Fixing broken filetype tests resulting from the loss of Type[] 21a714a Fixing some tests that somehow broke in the merge to master 327ee17 Removing a test that was too dependant on order. 487b9b1 Failure to find node facts is now a failure. 610c838 Fixing #1527 - Failing Facter does not hurt Puppet a2eca6c Removing some unused code cb0a083 Using Puppet::Type.new instead of create 84bd528 Actualling syncing facts and plugins d5abdfb Fix #1933 - Inconsistent resource evaluation order in subsequent evaluation runs 9bac833 Adding README.rst file 916abc2 Changing how the Configurer interacts with the cache 5335788 Fixing tests broken during the #1405 fix. 08a5d49 Adding an Agent::Runner class. c7d178d The Agent now uses its lockfile to determine running state c0fcb21 Creating and using a new Puppet::Daemon class 700e823 Not using 'master' client for testing 4b7023e Fixing (and testing) the return of Indirection#save 8dc0005 Adding a 'close_all' method to the Log class. 37d1a7c Removing restart-handling from Configurer bf3c72e Adding temporary class EventManager fc14b81 Splitting the Agent class into Agent and Configurer e8be6dc Removing the Hash default proc from SimpleGraph. 6bb804f Removing the Catalog's @aliases hash default value 502e062 Removing an erroneous configuration call in puppetmasterd 337057a Removing obsolete code and tests for the agent. d53ad31 Converting the catalog as needed f38277f Adding REST support for facts and catalogs. c48525b Adding better error-handling to format rendering b93a642 Resetting SSL cache terminii to nil when only using the ca 212b3e3 Allowing the Indirection cache to be reset to nil 5a83531 Moving the Agent locking code to a module. b672790 Cleaning up SSL instances that can't be saved f78a565 Only caching saved resources when the main save works 5434459 Moving classfile-writing to the Catalog e65d7f1 Refactoring how the Facter integration works 6b4e5f4 Reformatting tests for facts 54faf78 Moving fact and plugin handling into modules 9d76b70 Removing the Agent code that added client-side facts a5c2d7a Adding Puppet client facts to Facter facts. b99c6b5 Clarifying how node names are used during catalog compilation 8b44d6f Reformatting Indirector catalog compiler tests e770e7a Removing ConfigStore code that was never actually used. 37692e5 Renmaing Puppet::Network::Client::Master to Puppet::Agent 15d8768 Revert "Adding the first bits of an Agent class." 63fb514 Revert "This is work that I've decided not to keep" 8f5cbc3 This is work that I've decided not to keep so I'm just applying it here so it continues to show up in the history in case I ever want to look at it again. 25b28c5 Adding a new Agent::Downloader class for downloading files. 1afb821 Adding the first bits of an Agent class. 2afff60 Adding support for skipping cached indirection instances. 361db45 Change the way the tags and params are handled in rails 62cdeaa Add methods to return hash instead of objects to params and tags 3acea41 Rails serialization module to help serialize/unserialize some Puppet Objects 10bf151 Fixing #1913 - 'undef' resource values do not get copied to the db 500ea20 Fixing #1914 - 'undef' relationship metaparameters do not stack 1407865 Revert "Fixed #1916 - Added environment option to puppetd" 8d0086b Fixed #1916 - Added environment option to puppetd a065aeb Fixed #1910 - Updated logcheck regex fa9dc73 Typo fix 7c8094c Fixed #1879 - Added to tidy documentation f40a6b1 Fixed #1881 - Added md5lite explanation 6af3179 Fixed #1877 - Tidy type reference update for use of 0 a5b0a75 Fix autotest on win32 234a035 Fix #1560 fb8f8cd In order for ReST formatting to work properly, newlines and indentation of doc strings must be retained. 69432d6 Fix Bug #1629 1f6dce5 Fix #1835 : Add whitespace/quote parsing to 8142981 Fix #1847 - Force re-examination of all files to generate correct indices d2d3de5 Fix #1829 - Add puppet function versioncmp to compare versions bdee116 Fix #1828 - Scope.number? wasn't strict enough and could produce wrong results d69abfe Fix #1807 - make Puppet::Util::Package.versioncmp a module function 34335b7 Fixed #1840 - Bug fixes and improvements for Emacs puppet-mode.el 3b8a77d Fix #1834 part2 - Fix tests when no rails b6e34b7 Fix #1834 part1 - Fix tempfile failing tests 566bf78 Fixing #1729 - puppetmasterd can now read certs at startup 0cf9dec Canonicalizing Setting section names to symbols. 0fc0674 Fixing all of the test/ tests I broke in previous dev. e4ba3db Deprecating the Puppet::Type.create. b6db545 Deprecating 'Puppet.type'; replacing all instances with Puppet::Type.type 89c25ad Finishing the work to use Puppet::Resource instead of TransObject 1c7f8f6 Adding name/namevar abstraction to Puppet::Resource. e601bab Supporting a nil expirer on cacher objects. f69ac9f Setting resource defaults immediately. 352d7be Refactoring the Settings class to use Puppet::Resource 91ff7c1 TransObject is nearly deprecated now. fae3075 Simplifying the initialization interface for References 14c3c54 Replacing TransObject usage with Puppet::Resource 60062e4 Renaming the "Catalog#to_type" method to "Catalog#to_ral" 6b14000 Using Puppet::Resource to convert parser resources to RAL resources e3b1590 Adding resource convertion to the parser resources c306a17 Adding equality testing to Puppet::Resource::Reference 48a9949 Correcting whitespace and nested describes in Puppet::Resource::Catalog d48fff6 Renaming Puppet::Node::Catalog to Puppet::Resource::Catalog c927ce0 Renaming Puppet::ResourceReference to Puppet::Resource::Reference e88746b Adding Trans{Object,Bucket} backward compatibility to Puppet::Resource 832198f Starting on #1808 - Added a base resource class. 820ff2e Removing the "clear" from the macauthorization tests 89e9ef7 Fix #1483 - protect report terminus_class when testing for REST 435f1e9 Fix #1483 - use REST to transmit reports over the wire 6b30171 Fixing all broken tests. Most of them were broken by fileserving changes. f73e13e Adding more file tests and fixing conflicting tests cc12970 Completely refactoring the tidy type. 720dcbe Cleaning up the tidy type a bit 053d7bf These changes are all about making sure file data is expired when appropriate. a8d9976 Catalogs always consider resource data to be expired if not mid-transaction. 8b08439 Properly cleaning up ssl ca configuration during testing 73fa397 Adding caching support to parameters, and using cached attributes for file source and metadata. 0ecbf79 Adding cached attribute support to resources. 29b9794 Allowing a nil expirer for caching classes. cd09d6b Refactoring the Cacher interface to always require attribute declaration. 14af971 Changing the Cacher.invalidate method to Cacher.expire. 99a0770 Fixing a critical bug in the Cacher module. fe0b818 Fixing tests broken by fileserving and other refactoring. eed37f7 Fixing a test broken by previous refactoring 45c6382 Finishing the refactoring of the resource generation interface. 0840719 Refactoring and clarifying the resource generation methods. cc04646 Refactoring Catalog#add_resource to correctly handle implicit resources. a73cad9 Adding SimpleGraph#leaves, which I apparently did not migrate from PGraph 2a685f9 Removing mention of obsolete edgelist_class from GRATR. 7e20f06 Changing the catalog's relationship graph into a normal graph. 2ba0336 Removing the PGraph class and subsuming it into SimpleGraph. e92c1cc Moving Catalog#write_graph to SimpleGraph, where it belongs. 0149e2e Converting the file 'source' property to a parameter. 35c623e Removing mid-transaction resources from the catalog. f4800e8 Adding a method to Checksums to extract the sum type 44fadd1 Aliasing "must_not" just like we alias "must" a9dbb5d Deduplicating slashes in the fileserving code bb6619a Fixing the augeas type tests to work when augeas is missing e728873 Reducing the number of calls to terminus() to reduce interference with caching aa8d091 Switched all value management in props/params to internal classes. e5b5033 Fixing #1677 - fixing the selinux tests in master. 77d73e0 Changing the meaning of the unused Puppet::Type#parameter method to return an instance rather than a value. 05e1325 Moving a file purging test to rspec 6f7ccff Fixing #1641 - file recursion now only passes original parameters to child resources. a4d4444 Removing obsolete methods and tests: Removing obsolete handleignore method Removing obsolete FileSource class Removing a now-obsolete test/unit test Removing a now-obsolete recursive filebucket test b4f4866 Making it so (once again) files with sources set can still be deleted (which I think is kinda stupid, but apparently people want it). caf15c2 Fixing and migrating more file tests. cccd838 Adding a starting point for spec tests for tidy. 255c9fb Setting puppetmasterd up to serve all indirected classes. 7fdf2bb Retrieving the CA certificate before the client certificate. a00c1f2 Handling the case where a symbol (e.g., :ca) is used for a certificate name. cf3a11c Fixing :bindaddress setting to work with the new server subsystem. a78c971 Fixing CertificateRequest#save to accept arguments. e70c1a0 Fixing forward-compatibility issues resulting from no global resources 4596d2d Fixing a test I broke when fixing a reporting bug 9742c26 Fixing resource aliasing to not use global resource aliasing. I'm not really sure why the 0.24.x-style code got merged in, since master's changes should be more recent. 1b517d2 Adding comments to Puppet::Util::Cacher 7a6d9b1 Removing obselete code from the file type. 1b512a9 Merged fsweetser's selinux patch against HEAD e31df2f Removing files that git wasn't smart enough to remote during a merge. ac5db5e Removing the old, obsolete recursion methods. a9b7f08 As far as I can tell, recursion is working entirely. b69c50c Removing insanely stupid default property behaviour. 45f465b Source recursion is nearly working. 93fc113 Files now use the Indirector to recurse locally. bd1163a Fixing filesets to allow nil ignore values. 5da2606 Recursion using REST seems to almost work. ee1a85d Mostly finishing refactoring file recursion to use REST. 7c68fdb Fixing FileServing::Base so that it can recurse on a single file. ac41987 Fixing the terminus helper so it correctly catches options passed from clients via REST. be4c0e7 The file source is now refactored and uses REST. 44c6a52 Removing mention of an obselete class. 8271424 One third done refactoring file[:source] -- retrieve() is done. 98ac24a Adding a "source" attribute to fileserving instances. 6e43c2d Aliasing RSpec's :should method to :must. 8b45d13 Adding automatic attribute collection to the new fileserving code. 6ed8dfa Adding the content writer to the content class. 92e144b Fixing a test in the module_files terminus 151a54f Causing format selection to fail intelligently if no suitable format can be picked. deda646 Removing the last vestiges of the 'puppetmounts' protocol marker. 30dea68 Adding a 'plural?' method to the Indirection::Request class. 40e76fb Fixing the rest backends for webrick and mongrel so the get the whole request key. 8ea25ef Refactoring how files in FileServing are named. 550e3d6 Finishing the rename of FileBase => Base. 90e7022 Adding weights to network formats, and sorting them based on the weight. 5a195e0 Renaming FileServing::FileBase to FileServing::Base. 3101ea2 Adding a hackish raw format. 89a3738 Adding suitability as a requirement for a format being supported. a0bda85 Removing the yaml conversion code from FileContent. 6335b14 Causing the Indirection to fail if a terminus selection hook does not return a value. 1104edb Correcting whitespace in a test 2f224c9 Spell-correcting a comment bcd40fb Cleaning up an exception. 0a05720 FileServing Configurations now expect unqualified files. 237b7b2 Fixing whitespace in docs of some tests. 91b8252 Fixing the fileserving terminus selection hook. f5ba99f Special-casing 'file' URIs in the indirection requests. a215aba Dividing server/port configuration responsibility between the REST terminus and the indirection request. d174605 Fixing a test that relied on hash ordering. 7034882 Adding parameter and URL support to the REST terminus. c819042 Fixing the String format (fixes #1522). 78bc32d Removing dead-end file work as promised. a5ab52c Adding files temporarily, since I've decided this work is a dead-end. 4f275b6 Fixing #1514 - format tests now work again. 025edc5 puppetd now uses the Indirected SSL. 62202bf Adding 'require' statements as necessary for Puppet::SSL to work. 86a7188 Fixing the SSL::Host#waitforcert method. a31c578 Adding logging when files are removed. 09ee814 Removing now-obsolete the wait-for-cert module. cd314fa Documenting a bit of a test 113d74a Certificates now work over REST. 2cad30a Caching the SSL store for the SSL Host. 93fd55f Enhancing formatting errors with class and format. 6c80e0f Making all certificates only support the plaintext format. c464bf2 Adding wait_for_cert functionality to the ssl host class. c854dbe Adding a plaintext network format. 818599d lazy load latest package definitions with yumhelper 2.2 8457856 Fixing a group test that failed after merging 0.24.x c2c8941 Correctly handling when REST searches return nothing. 186f3cd Removing an obsolete method from the rest indirector 29d704c The REST formats are now fully functional, with yaml and marshal support. c55acee Adding some support for case insensivity in format names. 8033bd4 Moving validation from FormatHandler to Format. 3405841 Moving functionality out of the FormatHandler into the Format class. 43a6911 Searching again works over REST, including full content-type translation. 1064b5b Fixing the format_handler tests so that they clean up after themselves. 167831e Fixing a test I broke while rebasing e78b1a6 Fixing a test to be order-independent. 352e2d0 Adding rudimentary support for directly managing formats. 55e2944 Adding support for rendering and converting multiple instances. 4632cfd All error and format handling works over REST except searching. e3350ca Drastically simplifying the REST implementation tests. b3914c3 Removing an apparently-obsolete hook from the handler 739a871 Adding explicit tests for the HTTP::Handler module. 0ce92f1 The REST terminus now uses the content-type and http result codes. a4170ba Removing a now-obsolete pending test. bf5b086 The REST terminus now provides an Accept header with supported formats. 1f15725 Using the FormatHandler in indirected classes automatically. 0e7e16d Adding a FormatHandler module for managing format conversions. 93eeff5 Fixing the user ldap provider tests 00b7da3 Fixing the new-form version of #1382. c542dc0 Fixing #1168 for REST -- all ssl classes downcase their names. This is a much cleaner fix than the xmlrpc version, thankfully. :) eaa6eab Fixing #1258 -- Removing a Rails idiom. eb5e422 Fixing #1256 -- CA tests now work with no ~/.puppet. 66c36f0 Fixing another failing test -- the new CA tests correctly clear the cache. 447507c Fixing #1245 -- ssh_authorized_keys tests work in master. 6f533d1 Fixing #1247 -- no more clear_cache failures. 3cb0d60 Fixing how the mongrel server sets up xmlrpc handlers. 6efe400 Using the new Cacher class for handling cached data. 68d8d0a Adding a module for handling caching information. e936ef2 Fixing some broken tests. 1cfb021 The CRL is now automatically used or ignored. 0365184 Removing obsolete tests 3303590 The master and client now successfully speak xmlrpc using the new system. 8fd68e3 Adding pidfile management and daemonization to the Server class. dd4d868 Fixing the HttpPool module to get rid of an infinite loop. 57c7534 Adding REST terminuses for the SSL-related indirections. d78b4ba Adding autosigning to the new CA. a822ef9 Moving the CA Interface class to a separate file. 38e2dcf The master is now functionally serving REST and xmlrpc. 6e0d6dd The REST infrastructure now correctly the SSL certificates. 51ce674 Fixing the webrick integration tests to use the newly-functional SSL code. 62f1f5e The Certificate Authority now automatically creates a CRL when appropriate. e57436f The Settings class now clears the 'used' sections when a value is changed. 137e29f Moving some http configuration values to the main defaults section, rather than the puppetd section. a3b8804 The http pool manager now uses new-style certificate management. e596bc5 Fixing some tests that were insufficiently mocking their configurations. 160f9d9 Fixing a critical problem in how CRLs were saved and moving SSL Store responsibilities to the SSL::Host class. ce6d578 The SSL::Host class now uses the CA to generate its certificate when appropriate. 67dc268 The CA now initializes itself. 6356c04 Switched puppetmasterd to use the new-style server plumbing. 4c590df Adding xmlrpc backward compatibility to the new Mongrel code. 31b79fa Adding xmlrpc support to webrick. 7a876ed Fixing some whitespace 7267341 Adding configuration support for XMLRPC handlers. 8c9b04d I think I've now got the Webrick SSL support working. Now I just need to get xmlrpc working alongside REST in both mongrel and webrick. 83519f4 Interim commit, since I want to work but have no network available. 58fb416 Changing the File certificate terminus so that it saves to the :localcacert instead of :cacert. 79ca444 Renaming the 'ca_file' ssl terminus type to 'ca'. a116d10 Temporarily disabling the revoke/verify test in the CA. d87e018 Fixing how the CRL is used for certificate verification. 6c539c0 Fixing puppetca so it uses the :local ca setting. ebdbe48 Added an Interface class to the CA to model puppetca's usage. 934fbba Making the SSL::Host's destroy method a class method, rather than an instance method. d4813f1 Adding the last functionality needed for puppetca to use the Indirector. 809fc77 Finishing the interface between the CA and the CRL. 16056a2 Adding inventory support to the new certificate authority. d498c4a Adding support within the inventory for real certs or Puppet cert wrappers. 67f9d69 Changing the Inventory class to rebuild when the first cert is added, so it's easier to test. 7cca669 Adding a comment to the inventory class. 98db985 Adding an SSl::Inventory class for managing the ssl inventory. 92a7d76 All SSL terminus classes now force the CA information into the right place. fb56dea Switching the SSL::Host class to return Puppet instances. f7e0990 Setting the expiration date of certificate objects to the expiry of the actual cert. 71db9b5 Adding integration tests for a lot of the SSL code. e5c4687 Moving the password file handling into the SSL::Key class. d8bb81e Moving all of the ca-specific settings to the ca_file terminus classes, rather than the normal :file classes. cbe5221 Adding SSL::Host-level support for managing the terminus and cache classes. Also, defaulting to the :file terminus for all of the SSL classes. c5f0eff Fixing the CA so it actually automatically generates its certificate. 3d24b12 The certificate authority now uses a Host instance named 'ca'. daa8cd5 Changing all of the SSL terminus classes to treat CA files specially. 7d2c05e The 'destroy' method for the ssl_file terminus base class now returns false on missing files, rather than failing. 7555af6 Marking a test as pending, because it's not ready yet. c19c9d4 Removing all the cases where the ssl host specifies a terminus. Also, getting rid of some metaprogramming that wasn't really helping. 054e4e4 Making the first pass at using requests instead of specifying the terminus class. The individual ssl classes now work, but the ssl host class doesn't yet. 6900f97 Adding a :to_text method that will convert the contained thing to readable human text. 174b9c9 Actually signing the certificates in the CA. 546ac97 Adding the first attempt at managing the certificate revocation list. c98ad25 Adding a :search method to the ssl_file terminus type and the SSL::Host class. d184b35 Fixing a failing test that had not been updated from previous coding b9d6479 We have a basically functional CA -- it can sign requests and return certificates. There's still plenty more work to do, but I'm probably not much more than a day away from redoing puppetca to use this code. 1efed03 Adding tests for the easy bits of the CertificateFactory. I probably am going to skip the tests for the rest, since the code is unlikely to ever change, and it's going to be a royal pain to test. ee07d0b Adding tests for the certificate serial numbers dc5c73b The certificate authority is now functional and tested. a776a12 refactoring the cert request test a bit 7641bd4 This is a first pass at the certificate authority. The tests are basically entirely absent still, but the structure is all there. 0f46815 It looks like all of the new ssl classes for managing keys, certificates, and requests now work, including talking to the certificate authority. Now we just need the authority itself, along with the necessary REST terminii. 00e35bc Adding he last of the indirection classes for the ssl classes, finally including the certificate requests. 8347b06 The certificate and key are now correctly interacting with the existing cert/key store. Certificate requests are not yet handled, nor are the ca-specific collections. 50f3c18 Removing obsolete indirection classes ec5bdf3 The basics for the certificate and certificate request indirection terminii are done. I need to move most of the test code to a shared behaviour now. bb87464 Fixing a couple of broken tests. b0811ad The new SSL classes basically work, but they're not functionally connected to any kind of indirection. 3970818 Finished the certificate request wrapper class. 4ca6fd3 First stage of cert refactoring: Private keys kind of work. ef7d914 Oops; final fix on the integration test failures resulting from my partial support for ssl in webrick. 0ca0ef6 Fixing whitespace problems. 4640a3d Fixing an integration test of the rest terminus; it was broken by my incomplete cert support in webrick. I just stubbed out the cert usage for now; once all the cert stuff is done we'll need to go back and unstub it. d738f31 Adding the necessary tests for webrick to have logging and ssl. The tests can't be completed until the certificate work is all done. b49fb68 Fixing the tests in test/ that were broken as a result of the move to no global resources. 5e78151 Fixing tests that were failing as a result of the merge, including removing some now-obsolete code and tests from the Settings class. bee9aba Environments are now available as variables in manifests, and specs can be directly executed again. b225e86 Fixing #1017 -- environment-specific modulepath is no longer ignored. 4ede432 Tidied the man page creation function and created "master" branch man pages f335dc3 Updated defaults.rb to fix foru error stopping man page creation - links are not as neat as before but puppet.conf.man file will create neatly now. c751058 Removed remaining elements of old_parse - closing Ticket #990 31e0850 Removed old configuration file behaviour and deprecation warning - closes ticket #990 4165eda More fixes to the testing. cfda651 Another round of test-fixes toward eliminating global resource references. This should have gotten rid of all of them, and now it's just a question of fixing a few hopefully unrelated failing tests. 488c437 Fixing automatic relationships. I was previously looking them up in the relationship graph, which only stores the vertices, not the resource table. d8991ab Updated install.rb to product puppet.conf.man page - updating ticket #198 5a0388f Disabled new man page creation support e5888af Added support for man page creation - requires rst2man.py and writer - closed ticket #198 5bef4a5 Another round of fixes toward making global resources work. 3cc3e0f Lots o' bug-fixes toward getting rid of global resources. We still have about 60 failing tests, but some of them are the failing directory service tests (probably 20 or so), and most are simple fixes to the tests themselves. b7b11bd Fixing a couple of failing tests aed51b4 Fixed puppet logcheck issues 7aa79e2 Revert "Fixed documentation for code option in defaults.rb" f2991a2 Revert "Fixed indentation error in pkgdmg.rb documentation" 754129e Revert "Fixed issue where permissions are incorrectly set on Debian for /var/puppet/run directory" 20628ea Added patch to ext/logcheck/puppet to fix ticket #978 badf977 Fixed indentation error in pkgdmg.rb documentation e6547f0 Fixed documentation for code option in defaults.rb 594a5a3 Fixed issue where permissions are incorrectly set on Debian for /var/puppet/run directory 9736b3c Updated for 0.24.0 99f9047 tweaking spec language; require Puppet::Network::HTTP class since it is referenced by Puppet::Network::Server b38f538 Moving $PUPPET/spec/lib/autotest up to $PUPPET/autotest as something has changed and it can't be found otherwise. e1abfac moving autotest directory to make it possible to run autotest again 0.24.9 ====== 0dee418 Fixed typo in util.rb df33cb0 Updated CHANGELOG a22a088 Updated version to 0.24.9 0aae57f Backport of tmpfile patch from 0.25.2 9a26421 Brought in lutters parse_commands patch and integrated it into the type. This includes reworking the get and match commands as well. This change introduces a few small changes. These are: 916dd60 Fixed rspec gem at version 1.2.2 57b37e5 Add @options to test run call, for compatibility with more recent rspec versions. 0863a79 Adding #2122 - you can specify the node to test with puppet-test a43137c More RST fixes 843cc6e Fixed RST for functions 6160aaf In order for ReST formatting to work properly, newlines and indentation of doc strings must be retained. d125937 Added rake ci:all task c62c193 Updated to version 0.24.8 aa00bde Fixing #1631 - adding /sbin and /usr/sbin to PATH 39deaf3 Fixed #2004 - ssh_authorized_key fails if no target is defined dcf2bf2 Changelog entries for #1629 and #2004 bbcda1d Fix Bug #1629 69a0f7d Fix #1807 - make Puppet::Util::Package.versioncmp a module function 081021a Fix #1829 - Add puppet function versioncmp to compare versions 2b33f80 Fixed install.rb typo 5ab63cd Updated lib install permissions to 0644 0.24.8 ====== 02a503f Updated to version 0.24.8 cbc46de Fixing #1631 - adding /sbin and /usr/sbin to PATH 9eb377a Fixed #2004 - ssh_authorized_key fails if no target is defined 1b3fe82 Changelog entries for #1629 and #2004 8a671e5 Fix Bug #1629 ff5b13a Fix #1807 - make Puppet::Util::Package.versioncmp a module function 991f82c Fix #1829 - Add puppet function versioncmp to compare versions d0bf26e Fixed install.rb typo 4cf7a89 Updated lib install permissions to 0644 2c7e189 Fixes incorrect detail variable in OS X version check, re-patches ralsh to work with Facter values and adds error check for missing password hash files. 73a0757 Fix #1828 - Scope.number? wasn't strict enough and could produce wrong results 0.24.8rc1 ========= 84d6637 Fixed #2000 - No default specified for checksum a3bb201 Fixing change printing when list properties are absent 67fc394 Fixed #2026 - Red Hat ignoring stop method cf64827 Bring in the documentation changes from the master branch 01bc88c Added a force option to ensure the change is always applied, and call augeas twice to reduce the chance that data is lost cedeb79 Backport the fix for #1835 cf48ec0 First cut at the not running if augeas does not change any of the underlieing files 9d36b58 Bug 1948: Added patch by jab to support the correct ins syntax. Updated the test cases as well 61661b1 Fixing #1991 - ldap booleans get converted to booleans d5850dc Refactored a method: extracted about five other methods 1c7c8fe dbfix - fix typo and close another possible inconsistency c55ac3f Fix #2010 - add protection code for some storeconfig corruption a790ee3 Further fix to #1910 9577d3a Fixing #2013 - prefetching had a mismatch between type and title 719a8df Fixed to rake tests for reductivelabs build ac87600 Fixed report reference page 0c16426 Fixing broken 0.24.x tests in test/. 23066c1 Fixing every failing test I can find on the build server. ec56ddf This script fixes the most common issues with inconsistent storeconfigs database (including duplicate resources record, duplicate param_values records, dangling records...). c052ff8 Make puppetd --waitforcert option behave as documented: e2b4062 Adding a performance optimization to the FileCollection. fa6494b Using the FileCollection where appropriate. 373d505 Adding a FileCollection and a lookup module for it. 0e46786 Fixed #1963 - Failing to read /proc/mounts for selinux kills file downloads 4170238 Fixed #2025 - gentoo service provider handle only default init level 8c010e0 Fixed #1910 - updated logcheck 7504b04 Updated useradd.rb managehome confine to include other RH-like distributions f07d928 Use Puppet.debug instead of own debug flag 25a3f59 Fixing #558 - File checksums no longer refer to 'nosum' d758f45 Fixing #1871 once and for all - contents are never printed c0f4943 Minor fix to launchd tests 24d48e6 Fix #1972 - ActiveRecord fixes resulted in broken tests 446989b Fix spec test for launchd service provider to work with new service status method and add two new status tests. 3ef5849 Fixing a test I broke in commit:"897539e857b0da9145f15648b6aa2ef124ec1a19". 72bd378 Removing a no-longer-valid test. 682dd8b Fixing password validation to support symbols. 44f97aa Only backing up within parsedfile when managing files 04af7b4 Fixing a syntax error in the up2date provider 1070b3c Fixing a test broken by a log demotion ab84756 Cleaned up variable names to be more sane, clarified error messages and fixed incorrect use of 'value' variable rather than 'member'. 7f41857 Provide dscl -url output support for OS X 10.4 clients using the directoryservice provider. 0bc3c07 Fix launchd service provider so it is backwards compatible with OS X 10.4 as well 2561c8e Updated Augeas type code 7d72186 Removed site from Puppet VIM syntax 1bc7404 Fixed #1831 - Added sprintf function 336b645 Fixed #1830 - Added regsubst function 2a85551 Bug 1948: Add logic and testing for the command parsing logic 2218611 Updated up2date and service confines to add support for Oracle EL and VM 39a8b28 Fixing #1964 - Facts get loaded from plugins 7cf085c Adding tests for Puppet::Indirector::Facts::Facter.loadfacts 70ea39a Adding a post-processor for Nagios names. 4dfa034 Revert "Refixing #1420 - _naginator_name is only used for services" d5a193a Fixing #1541 - ParsedFile only backs up files once per transaction 53f15b9 Removing the apparently obsolete netinfo filetype. 4e89156 Migrated FileType tests to spec, and fleshed them out a bit. cc4d658 Bug #1948: Added patch by jab to support the correct ins syntax. Updated the test cases as well 5e35166 Fixing #961 - closing the http connection after every xmlrpc call af3f3ae Refactoring the XMLRPC::Client error-handling f0ac3ae Fixed #1959 - Added column protection for environment schema migration 319822a Fixing #1869 - autoloaded files should never leak exceptions 6b0c1b9 Fixing #1543 - Nagios parse errors no longer kill Puppet 7fd5c7e Moving the transaction specs to the right path efb5cc5 Refixing #1420 - _naginator_name is only used for services 32c2be9 Fixed #1884 - exported defines are collected by the exporting host 0e49159 Cleaning up the AST::Resource code a bit b22d148 Fix #1691 - Realize fails with array of Resource References 6331bfc Fix #1682 - Resource titles are not flattened as they should 7e036eb Fix #1922 - Functions squash all arguments into a single hash 535fa89 Fixed #1538 - Yumrepo sets permissions wrongly on files in /etc/yum.repos.d f7b04df Fixed #1936 - Added /* */ support to the vim file 671d73c Prefetching, and thus purging, Nagios resources now works *only* if you use the default configuration file locations. 063871f Adding some basic tests for the Naginator provider base class 897539e Removing a redundant instance prefect call. 012efe3 Fixing #1912 - gid still works with no 'should' value. a9f34af Fixing the Rakefile to use 'git format-patch'. db05c00 Fixing #1920 - user passwords no longer allow ':' aa219e7 Adding README.rst file 1d3f117 Added Reductive Labs build library f01882d Change the way the tags and params are handled in rails b7ab54c Add methods to return hash instead of objects to params and tags 5c64435 Rails serialization module to help serialize/unserialize some Puppet Objects b27fccd Fixed #1852 - Correct behaviour when no SELinux bindings 7403330 Updated Red Hat spec file 0.24.7 0.24.7rc1 ========= 84d6637 Fixed #2000 - No default specified for checksum a3bb201 Fixing change printing when list properties are absent 67fc394 Fixed #2026 - Red Hat ignoring stop method cf64827 Bring in the documentation changes from the master branch 01bc88c Added a force option to ensure the change is always applied, and call augeas twice to reduce the chance that data is lost cedeb79 Backport the fix for #1835 cf48ec0 First cut at the not running if augeas does not change any of the underlieing files 9d36b58 Bug 1948: Added patch by jab to support the correct ins syntax. Updated the test cases as well 61661b1 Fixing #1991 - ldap booleans get converted to booleans d5850dc Refactored a method: extracted about five other methods 1c7c8fe dbfix - fix typo and close another possible inconsistency c55ac3f Fix #2010 - add protection code for some storeconfig corruption a790ee3 Further fix to #1910 9577d3a Fixing #2013 - prefetching had a mismatch between type and title 719a8df Fixed to rake tests for reductivelabs build ac87600 Fixed report reference page 0c16426 Fixing broken 0.24.x tests in test/. 23066c1 Fixing every failing test I can find on the build server. ec56ddf This script fixes the most common issues with inconsistent storeconfigs database (including duplicate resources record, duplicate param_values records, dangling records...). c052ff8 Make puppetd --waitforcert option behave as documented: e2b4062 Adding a performance optimization to the FileCollection. fa6494b Using the FileCollection where appropriate. 373d505 Adding a FileCollection and a lookup module for it. 0e46786 Fixed #1963 - Failing to read /proc/mounts for selinux kills file downloads 4170238 Fixed #2025 - gentoo service provider handle only default init level 8c010e0 Fixed #1910 - updated logcheck 7504b04 Updated useradd.rb managehome confine to include other RH-like distributions f07d928 Use Puppet.debug instead of own debug flag 25a3f59 Fixing #558 - File checksums no longer refer to 'nosum' d758f45 Fixing #1871 once and for all - contents are never printed c0f4943 Minor fix to launchd tests 24d48e6 Fix #1972 - ActiveRecord fixes resulted in broken tests 446989b Fix spec test for launchd service provider to work with new service status method and add two new status tests. 3ef5849 Fixing a test I broke in commit:"897539e857b0da9145f15648b6aa2ef124ec1a19". 72bd378 Removing a no-longer-valid test. 682dd8b Fixing password validation to support symbols. 44f97aa Only backing up within parsedfile when managing files 04af7b4 Fixing a syntax error in the up2date provider 1070b3c Fixing a test broken by a log demotion ab84756 Cleaned up variable names to be more sane, clarified error messages and fixed incorrect use of 'value' variable rather than 'member'. 7f41857 Provide dscl -url output support for OS X 10.4 clients using the directoryservice provider. 0bc3c07 Fix launchd service provider so it is backwards compatible with OS X 10.4 as well 2561c8e Updated Augeas type code 7d72186 Removed site from Puppet VIM syntax 1bc7404 Fixed #1831 - Added sprintf function 336b645 Fixed #1830 - Added regsubst function 2a85551 Bug 1948: Add logic and testing for the command parsing logic 2218611 Updated up2date and service confines to add support for Oracle EL and VM 39a8b28 Fixing #1964 - Facts get loaded from plugins 7cf085c Adding tests for Puppet::Indirector::Facts::Facter.loadfacts 70ea39a Adding a post-processor for Nagios names. 4dfa034 Revert "Refixing #1420 - _naginator_name is only used for services" d5a193a Fixing #1541 - ParsedFile only backs up files once per transaction 53f15b9 Removing the apparently obsolete netinfo filetype. 4e89156 Migrated FileType tests to spec, and fleshed them out a bit. cc4d658 Bug #1948: Added patch by jab to support the correct ins syntax. Updated the test cases as well 5e35166 Fixing #961 - closing the http connection after every xmlrpc call af3f3ae Refactoring the XMLRPC::Client error-handling f0ac3ae Fixed #1959 - Added column protection for environment schema migration 319822a Fixing #1869 - autoloaded files should never leak exceptions 6b0c1b9 Fixing #1543 - Nagios parse errors no longer kill Puppet 7fd5c7e Moving the transaction specs to the right path efb5cc5 Refixing #1420 - _naginator_name is only used for services 32c2be9 Fixed #1884 - exported defines are collected by the exporting host 0e49159 Cleaning up the AST::Resource code a bit b22d148 Fix #1691 - Realize fails with array of Resource References 6331bfc Fix #1682 - Resource titles are not flattened as they should 7e036eb Fix #1922 - Functions squash all arguments into a single hash 535fa89 Fixed #1538 - Yumrepo sets permissions wrongly on files in /etc/yum.repos.d f7b04df Fixed #1936 - Added /* */ support to the vim file 671d73c Prefetching, and thus purging, Nagios resources now works *only* if you use the default configuration file locations. 063871f Adding some basic tests for the Naginator provider base class 897539e Removing a redundant instance prefect call. 012efe3 Fixing #1912 - gid still works with no 'should' value. a9f34af Fixing the Rakefile to use 'git format-patch'. db05c00 Fixing #1920 - user passwords no longer allow ':' aa219e7 Adding README.rst file 1d3f117 Added Reductive Labs build library f01882d Change the way the tags and params are handled in rails b7ab54c Add methods to return hash instead of objects to params and tags 5c64435 Rails serialization module to help serialize/unserialize some Puppet Objects b27fccd Fixed #1852 - Correct behaviour when no SELinux bindings 7403330 Updated Red Hat spec file 0.24.7 8befc18 Updated to version 0.24.7 cf19bd8 Not using a temporary file when locking files for writing. b966ea0 Modifying the corruption-checking test. 1f34bca Issue 1804 VDev with the same devices should be in sync 6d5a129 Documentation fixes 45144a1 Fixing #1812 (hopefully) - adding read and write locks to yaml. 2385a78 Preparing to fix #1812 - Moving locking code to a module 2961b83 Fix #1815 - puppetdoc --all crash on resource override e5c36fd Fix ZFS autorequire test da71ad5 Add a unique name to objects so we can determine uniqueness when read back in 4418b34 Fix launchd service test on non-OSX platforms 4b2bdf9 Fix the spec tests to work on other platforms, do the confine around OS X versions more sanely 544a3e1 remove unnecessary mk_resource_methods call 50ac03a CHANGELOG updates a0a6d2c Add a unique name to objects so we can determine uniqueness when read back in 68ffd46 Bug #1803 Zfs should auto require the ancestor file systems 7e2da7e Refactor #1802 Use 'zfs get -H -o value' instead of parsing output for value 8616d74 Fixing #1800 - tidy now correctly ignores missing files and directories 6075d10 Fixing #1794 - returning sync when it is already initialized 18fe5c3 Fixing #1750 again - All of the properties and now :ensure check replace? b22303e Fix rake abort when there is a matching confine 0caa9c5 spec tests for type and provider and some code cleanup to adhere to DRY 0f2fc88 Finished work on rules creation and deletion ed49153 new better way of doing stdin 05e05bb finished rights flush, working on rules 1e37230 macauthorization type 4ed73ef reset macauthorization tree. Initial checkin of new type/provider 5d32cd9 add NetInfo deprecation notice to user and group providers, make the directoryservice user provider the default, remove default for darwin from NetInfo providers 99ab940 Warn that the NetInfo nameservice provider is deprecated. Use directoryservice instead c4412ec add some more sanity checks around stdin 7de82c3 add support for stdin to Puppet::Util.execute edef064 Make ralsh behave more sanely for non-existent objects and property values 9384a4a Added git changelog task c398db1 Bug #1780 Fixing meaningless test 278bfe8 Fixing mcx test failures (only happened sometimes). c4812b8 Need to stub out the defaultprovider call for non Mac platforms b444e43 remove extraneous comments 49d4d01 Trim down the after block clears to try to make the tests work for the build servers 65d6b49 Updated mcx type and provider with comprehensive spec tests. fd128d6 Fixing a package test to be *much* faster cdcbc5b Fixing splaytime tests 6a4c0d5 Removing debugging from the "resources" type 0c6a151 Fixing a test that fails depending on test execution order 968f5cc Relicense under GPLv2+ 9ab3afb Hopefully fixing #1703 - using a mutex around the sending of the tagmails 3fe9cc7 Fix #1741 - fix some failing tests on some ruby versions. 3570c71 Fix #1788 - allow rspec rake to run only some tests 1b3a7d8 Fixing the AST constant warnings, using a variable instead of a constant 091b8bf Fixing #1785 - selinux tests no longer break other tests c005dcf Ticket 1780 - Solaris RBAC roles should be autorequired 3eff225 Feature 1696 Add support for branded zones fa9820b Bug #1778 - Solaris RBAC profiles should maintain order f6fa4f7 Bug # 1680 Now you can set the hashed passwords on solaris 0a40668 Feature #1783 - Add ZFS support 047e5d0 Handle password when user is created 88edf66 == is not = a219c88 Solaris doesn't have a native tool to set hashed passwords 9329c95 type/mcx.rb Feature #1026 - MCX Type 83b3a1e Simplify launchd service provider and add tests 65a6074 Fixed #1695 - Solaris 10 zone provider doesn't properly handle unknown zone attributes in newer releases 0171e25 Fixing #1749 - Splay now hopefully behaves "better" for small values. 607958c Fix #1741 - Add inline_template function cc45c43 Fix #1741 - refactor TemplateWrapper, test for template function d8c741f Fix #1741 - Puppet::Parser::Functions rmfunctions and unit test 3c4efa7 Fixes #1773 - no longer check for absolute paths 3a39509 make sure only types that have passwords search for the password a45c6b1 fix bug with numeric uid/gid in directoryservice provider. doc string cleanups 1f52795 Documentation fix for runit provider 81a91a7 Documentation fix for daemontools provider 4f67a7c Fixed #1776 - Trivial fix for gentoo service provider 2764ab4 Rename migration so it's still applied 965c08d Slight denormalisation to store a host's environment as a first class object in the database Fixes: #1392 5742966 Fixing #1743 - defined types get catalogs too. 31ec3e6 Adjusted CI tasks exit codes 3421954 Fixing #1755 - handling fully qualified classes correctly. a1ac9a5 Added Rake :ci namespace and CI tasks d978668 Lots of DirectoryService work. New Computer Type. Users now use password hashes. Groups now support setting members as attributes of the group for OS X. 86ce934 launchd service provider 97a8177 Refactoring the thread-safety in Puppet::Util a bit. 78bced1 Fixing #1683 - accessing and changing settings is now thread-safe. 83cebb5 Partially fixing #1772 - fixing selinux tests broken by removal of extraneous 'stat' in :file. a839fe2 Partially fixing #1772 - fixing tidy code I broke. 5bd27c8 Partially fixing #1772 - broken 'resources' tests. a3140b2 Manually setting an env var to mark autotest enabled so we see color bbad983 Removing the included testing gems; you must now install them yourself. b415848 Fixing #1708 - user groups specified as names are now detected correctly. 9ed382d Fixed #1767 - Minor fix to emacs mode 27a750d Revert "Fixing #1755 - File modes (and other strange properties) will now display correctly" eb0d32a Fixing #1764 - a property's 'sync' method is never considered a no-op. e9f858a Refactoring the file/owner property to be simpler and cleaner. ed4c405 Fixing #1755 - File modes (and other strange properties) will now display correctly in ralsh and generated manifests. c65f2b5 Fixed #1668 - puppetca can't clean unsigned certs 1ad33cc Fix #1759 - Comparison operator was using string comparison for numbers c96d250 Fixed #1711 - fileserver test fails due to incorrect mocking 8523a48 Fixed #1751 - Mac OS X DirectoryService nameservice provider support for plist output and password hash fil d32d7f3 Fixed #1752 - Add an optional argument to Puppet::Util.execute to determine whether stderr and stdout are combined in the output 4396740 Fix the init service type to cope with an array for defpath and if defpath does not exist 3c870d8 Added versionable feature to the RPM provider f62d04d Fixing broken tests resulting from the fix to #1747 030c791 Moved RRD feature from util/metric.rb to feature/base.rb dc192b0 Manifest documentation generation 2c05a0a Move function existance test to parser evaluation 064fb00 Add a doc attribute to AST nodes and fill it with the last seen comments 724a6f6 RSpec tests for the doc system (covers AST.doc, lexer and parser) b8ed667 Fixed #1735 and #1747 - Fixes to confine system 6be5ac8 CHANGELOG updates 0ca5025 Fixed #1718 - Added preseed to apt uninstall and purge 01976ca Include spec directory in packages c98f7a5 Fixing the provider's confine subsystem so the logs are more useful. 6426a29 Removed extra 'end' from yum.rb 1e81739 Fix bug #1746: Sync SELinux file attributes after file contents created/modified cebadd9 Fix bug #1681: Add filesystem type check to test for per-file SELinux context support 60455e7 Quiet debug when no default SELinux context found for one of the components 71a9e60 Fixes relating to transition to native SELinux bindings 3a5dcab Refactoring of SELinux functions to use native Ruby SELinux interface d5e19f1 Fixed #1739 - Added uninstall functionality to yum provider bf5be00 Fix #1737 - part2 - Fix display of "options" e032034 Fix #1737 - ssh_authorized_keys should be able to parse options containing commas e33d087 Fix #1740 - Daemontools and Runit is not ReST compliant dfc0554 Fixed #1730 - Edited file/ensure.rb docs for clarity 6d7b5ef Fixes #1672 - unsafe crontab handling in Solaris Signed-off-by: Martin Englund 083077d Fixing the augeas type tests to work when augeas is missing 0a3d34d Fixes #1714 - yumhelper handling with yum 2.2.x is broken 7b70e85 Fixed #1721 - puppet.conf documentation incorrectly lists signals that affect the daemons 781a685 Fixing a test I broke when fixing a reporting bug f063517 Added unit tests for the augeas type and provider 2d37f09 Fix #1402 - Allow multiline comments 9f30306 Fix #857 - Multiple class of the same name don't append code 649a9e0 Fixed augeas examples in type 56f3be6 Fixed #1710 - Spurious output in test run 4806c51 Fixing #1669 - The dump parameter can now be changed on mounts. c7ccc4b Fix #1682 - ASTArray should flatten product of evaluation of its children c906afd Fixing #1667 - regex automatic value documentation is now readable. 9fd1756 Split Augeas up into a provider and a type. e542f8c Fixed #1692 - k5login fails to set mode when file is created 57e791b Fixing #1660 - Adding specifically supported values for tidy recursion. 42cac73 Fixing #1698 - all logs again show up in the report. 6ab4f1b Fixed #1661 - Type reference: tidy should specify manditory parameters 2459106 Removing all mention of EPM, RPM, or Sun packages. 9ecbd63 Fixed #1104 - Classes and nodes should set $name variables bc8cdb1 Beginning provider split, need help on the voodoo 6539f55 Updated Red Hat spec file for 0.24.6 and removed conf/debian directory. cacafeb Added augeas type and feature 0.24.6 ====== b2c1149 Updated to version 0.24.6 5ba54d2 Updated to version 0.24.6 22024bc Improve the inline documentation for SELinux types and parameters f216237 Fixes #1663 - added Symbol check and additional test 81c3b72 Fix SELinux test to succeed when Puppet debug mode is enabled f7516a7 Fix regression caused by switch to Puppet's execute() functions c09d0cc Solaris RBAC Attributes 6d05cbc Fix #936 - Allow trailing comma in array definition ec2b461 Fix #1115 - part2 - fix tests and add all_tags 356d8ca Fixed #1662 - Configuration Reference still references 'section' b53509b Fixed #1460 - enhance redhat puppetmaster init.d script to easy start puppetmaster as a mongrel cluster 8a4e2e9 Fixed #1663 - Regression relating to facter fact naming from 0.24.5 e6f99f9 Fix #636 - Allow extraneous comma in function argument list a74ec60 Fixing tests I broke when trying to fix the Providers reference. d4df361 Use fully qualified paths when calling binaries, adjust chcon call to use Puppet's execute() function. dedf0cd Setting SELinux contexts with chcon should not dereference symbolic links 7f5ded1 Fixed #1646 - service puppet status does not work as non-root on redhat system 00d5fe4 Fix #1115 - Allow checking tags/classes from ERb templates f5fb2d3 Fixed #1649 - OS X package creation script should be more selective about cleaning out prior versions b0fd2e0 Fixing #1647 - puppetdoc's 'providers' report works again. 157c0dd Fix 1642 (always warning) and improve unit tests to cover when to warn and not 65eafb7 lazy load latest package definitions with yumhelper 2.2 eff6ce0 Revert "Added last part of #1633 patch - update to util/metrics.rb" c5d1a4f Added last part of #1633 patch - update to util/metrics.rb 0fff7d7 Fixing some tests that were broken in 2fba85af 2afbd0d Fixing a test that was failing as a result of the fix to #1491 b0c01da Adding an additional option for the fix in ff36832e, skipping missing cert dirs aea5582 Removing a gid test for users, since it is a bad test and has mostly been replaced in rspec 65d3040 Fixing a test that was broken in ee579641 b08002e Fixing some tests that were broken in the fix for #1633 2bf0ba5 Fixing a test that was failing because i-have-no-idea 952ebb8 Fixing a test that was failing because of the change to retrieve() in ee579641 a5fe87f Fixing a file source test that was failing because missing sources is now a failure 53b7d42 Fixing the broken tests resulting from the fix for #1551. 5ec6b07 Adding warnings when example groups are skipped. 54abe70 Moving some test/ package tests to rspec integration tests and disabling a gem test that hangs forever for me. 85d3ae0 Cleanup selboolean and selmodule unit tests to pass on non-SELinux systems a562ce5 Add unit test coverage for Puppet::Util::SELinux and fix problems found by tests 2b4aa0c Fixed #1639 - uninitialized constant Puppet::Type::User::ProviderUseradd 4265825 Fix #1636 - part2 - correct some client errors. 9c31db9 Add failing test for plugin with file and recurse 2853447 Fix several small regressions in plugins mount 2153bae Fixing #1640 - file groups now no longer get set on every run (this was a regression caused by other work I did). 80e5c11 Incremented CHANGELOG to 0.24.6 996ac46 Fix scenario when SELinux support tools exist, but SELinux is disabled d803096 Add new set of unit tests for selmodule type a3f34f9 Removal of redundant lines from unit test 307260f Remove old selboolean unit tests and fix permissions on new tests 2ebd34e Rewrote seboolean unit tests to provide better coverage 4df51ea New and improved tests for file type SELinux contexts 253d4df Fix regression when templatedir doesn't exist. c7a6ef2 Fix #1202 - Collection attribute matching doesn't parse arrays 3281f2b Fixed #1633 - Added support for --detailed-exits to bin/puppet which causes puppet to produce different exit codes depending on whether there were changes or failures in the transaction. 0b1e60f Adding an array indexer method to Puppet::Util::Metric as requested in #1633. 765db30 Adding partial spec tests for Puppet::Util::Metric. fb14e91 Fixed #1473 - Rescue Timeout::Error in xmlrpc clients 7275d7c Fxied #1354 - yum provider problems with RHEL 3 0c297be Fix #1109 - allow empty if or else branches 5268487 Fixed documentation, typo and added CHANGELOG entry 990e8e3 Fix #1530: Correctly parse ssh type 1 keys 06edac4 Fixed additional environments tests 79bb1f2 Rspec Tests for #381. Moved part of the old resource reference tests to rspec. 750e9ab Fix #381 - Allow multiple resource overrides or references 782181e Minor test fix for #1614 614326a Fixing #1098 - Multiline strings now correctly increment the line count 1c6d57e Doing some simple refactorings on Puppet::Log a774443 Fixing #1089 - Log messages are now tagged with the log level, making it easier to match messages in the 'tagmail' report. db7f108 Adding rspec tests for the Puppet::Util::Log class. d2c8998 Fixed #981 - Removed 'Adding aliases' info message d098a90 Fix failing tests dependent on /etc/user_attr file existing 6bcfd9f Fixing #947 - pluginsync no longer fails poorly when no plugins exist 67136f1 Fixing the Node class to no longer validate environments since #1614 removed that validation. a4110a3 Add SELinux context reset after file writes in Puppet::Util::FileType 250239e Add new support for :selrange SELinux file property c831482 Add detected defaults for existing SELinux file properties 772e9b2 Refactor SELinux commands to utility module 8153181 Clean up of SELinux rspec tests so all pass e77ddc1 Merged fsweetser's selinux patch against HEAD 7272d49 Fixed #1613 - The client environment will be substituted when looking up settings. 1a9b567 Fixing #1614 - Environments no longer have to be listed out. 397c841 Fixed #1628 - Changed node search to use certname rather than Facter hostname 9d174c4 Updated puppet binary documentation 6a0b334 Fixed error message typo 655f378 Adding a rake task for sending emails to the dev list d39bab9 Fixing package provider tests to use the new Transaction::Change interface e32256a Migrating the apt and dpkg tests to rspec. ddda80a Update change log with RBAC roles d1abb86 Add role support to user type and an implemention 2fba85a Some small clarifying refactors and change to objectadd to allow subclasses of with a uid not need to be a single class us use modify 4a863c3 Adding user_attr util to parse attributes on solaris 93f952a Fixed #1586 - Specifying "fully qualified" package names in Gentoo 8620775 Fixed #791 - You should now be able to create and find a user/group in one transaction. 63ad845 Refactoring and adding tests to the file group property. 7da4152 Modified the group and zone resource types to no longer call 'currentpropvalues' as a means of setting all values to absent. There should be no behaviour change from this change. ee57964 Modified the behaviour of resource-level 'retrieve' -- it only calls 'retrieve' on each property if the resource exists. 0fb4693 Updating changelog for #1622 2afc4f5 Adding tests for the user retrieve method 679fede Removing commented code from the user type from about 2005 2480654 The Netinfo and DirectoryService providers can now create user and group simultaneously. 4c998fe Fixing #1622 - The user type only looks up groups when necessary. 6bc56ae Aliasing the rspec 'should' method to 'must' so it does not conflict with the RAL 'should' method. b9c75cd Rewriting the user tests, in preparation for enhancing them 99de920 Fixed #1620 - Add 'sles' to Puppet confines when 'suse' is used 4cf9710 Add parser for arbitrary expressions cfa230a Add arithmetic operators to AST 850e0ba Add not operator to AST 9cdecfe Add comparison operators (< > == != <= >=) to AST 8372dc4 Add boolean operators to AST e6698c2 Add warning and forcibly set to :md5 fixing #1564 af8c706 Fix metadata class for cases when checksum_type set 860bdb1 Fixed #1603 - Added support for running Puppet inside a Rack application b2f0d87 Fix ticket 1596 in new fileset code, use tmpdir in fileserver tests. a30ecf2 Make fileserver use fileset for recursion and handle dangling links by ignoring them fixing #1544 3b80763 Add tests for FileServer::Mount list for #1544 3749267 Fixed #1610 - Raise "Filebucketed" messages to Notice priority f792b64 Added a number of confines to package providers 074abd4 Fixed #1609 - Added confines for the Gentoo, FreeBSD and SMF (Solaris) service providers 2da6d19 Fixed #1608 - Added ubuntu to defaultfor for apt provider aa629ec Fixed #1607 - Added ubuntu to defaultfor for Debian service provider 774c0f9 Fixed #1588 - Fixed puppetca --clean --all 98e79f8 Fixed #1472 -- defined, exported resources in the current compile now get expanded correctly. 0040bc8 Fixed #1045 - Multiple metaparams all get added to resources. 8d5ded0 Removing some code in Parameter that is unnecessary. 5fbdc49 Fixed #1595 - Internally, Property#retrieve is no longer called when no 'should' value is available for a resource. c16a5ae Only apply splay the first run 27f0c7d fix failing hpux user specs 7a3a38f Add rspec unit test for the append operator 16793d2 Add an append (+=) variable operator: 7f8abbd Bug #1550 - Rework to avoid regressing rspec tests, add new rspec tests for templatedir as a path 0905734 Allow a templatedir to be colon separated. 11b0848 Fixed #1500 - puppetrun host regression 3b1d6e2 Fixed #1579 and #1580 - errors in the Puppet RPM spec file 77f4fb6 Fixed #1521 -- ldap user and group are now used with the default connection when available. a1a670b Fixed #1572 -- file purging now fails if remote sources do not exist. dd4f654 Fixing #1576 - moving all of the Puppet::Type code back into type.rb. 923fd89 Fixed issues with file descriptors leaking into subprocesses cab5d85 Fixed #1571 - Puppet::Util::binary returns incorrect results a7306e1 Fixed #1553 - Puppet and Facter cannot both install the plist module into two different locations 758505b Fixed #1568 - createpackage.sh 7ce902d Adjusted hpuxuseradd user provider to confine to HP-UX and fixed HP-UX user provider path regression 8f1336f Fixed #1566 - changed password property of the user type d4d3213 Fixed debug messages in package type - thanks to Todd Zullinger for this fix b88df5a Sync with latest Fedora/EPEL specfile 0705dfb Fixes #1455 - Adds HP-UX support for user type e15d316 Fixes #1551 puppetmaster.freshness xmlrpc call returns incorrect type 8fe0338 Fixes #1554 - Fix exception for undefined hostname 81cc9bf Fixed #1533 - changed permissions for man directory 41dc1fa Runit service provider aae0793 Daemontools service provider 29ae879 Fixes tests broken by 95aa085 b50e718 Fixed #1488 - Moved individual functions out of functions.rb into the lib/puppet/parser/functions directory. New functions should be created in this directory. 5fb5091 Fixed #1457 - case insensitive match for error ded94f7 Removed spec color option for buildbot 415663b Added simple rake task for running unit tests 557be9d Added spec Rake task 0d118a5 Fix leaking LoadedFile when adding templates to be watched 67387e2 Fixed #1506 - Removed storeconfig duplicate indexes 7accb89 id column is autogenerated by rails as a primary key, there is no need to create an additional index on this column. This changeset contains the new schema and a migration. c5fb092 Removed reference to namespaces from --genconfig documentation 1729de1 Updates to ext/puppetlast to support multiple hosts b6609ee Fixed #1508 - Add HP-UX package provider. 3e482a2 Updating the authors list for the gem spec 2ec4e29 Fix #1502 - abysmal storeconfig performance - part2 9272df4 Fix #1052 - abysmal storeconfig performance - part1 f48a0ae Fix #1510 - storeconfig fails with rails 2.1 b1ad596 Add the -P/--ping option to puppetrun, fixes #1501 6676b6b Fixes #1274 - allow class names to start with numbers d02f95c Fixed #1394 - Added stored configuration clearing script to /ext fb8cc53 Fixed #1442 - replaced use of Facter for report titling with certname 18dda20 Fixed $1456 - add proxy configuration to yum repo ab4cb6a Fixing #1447 -- Replacing Puppet::PackageError with Puppet::Error. 8a0cb16 Added tests for TemplateWrapper's use of Scope#to_hash. 3ae7eca Fixing an ldap connectivity test d3393b4 Added CHANEGLOG entry for removal of interface type bfcdfe8 fix terrible error with overwriting permissions 0147570 Fixed #1457 - removed confine warning fecdfbc A working script to create an OS X pkg out of the Puppet repository 6a2e71d Fixed #1441 - Updated console colours 404450a Add testing for the changes to resolve redmine #1427, where Kernel methods shadow variables that the puppet template should otherwise see. Specific changes: 03c76de Expose all puppet variables as instance member variables of the template wrapper. This helps resolve redmine #1427, by providing a safe mechanism to access variables. 13069ec Ensure that we consistently use either string #{} interpolation or String.% interpolation, not both, to avoid issues where a #{} interpolated value contains a % character. 469c5fe Feature #1476: Allow specification of --bindir --sbindir --sitelibdir --mandir --destdir in install.rb 2a3d195 Specs for yaml indirector .search - I'm still learning! c97389d Made puppetlast work on 0.24.5 by using the YAML indirector 5c53617 Added a search method to the YAML indirector. 482489a Revert "Fixing puppetlast to make it work with 0.24.5 / 0.25." 0bbac8d Fixes #1417 - whitespace in ssh_auth_key provider a772110 Sync with latest Fedora/EPEL specfile 97987a7 Feature #1241 : Improve performance of group lookups fe99828 Bug #1448: Puppet CA incorrectly writes out all certs to inventory .txt on each certificate signing 971af69 Fixing puppetlast to make it work with 0.24.5 / 0.25. d91d806 Updating the authors list for the gem spec 0.24.5 ====== ce964ec Updated to version 0.24.5 a7df4eb Updated to version 0.24.5 4dffaf3 Reverting the version so my release process works aac7dd1 Incremented versions bfcd626 Fixes #1445 and #1426 8f5800f Fixes #1445 and #1426 ff36832 Fixing the renaming code to skip missing directories. 8c2478b Fixing puppet_module -- it needed the same node interface change. d9aa5ab Fixing a cert test to pass on Darwin. 686ba4d Revert "Merging fsweetser's selinux patch against 0.24.4" f16da42 Merging fsweetser's selinux patch against 0.24.4 a47fed4 'Fix' broken tests related to missing source raising Issue 1437 In two cases, I removed the assertion that caused the failure. In one case, I changed the assertion to expect an exception. 238b8d7 Fixing #1438 -- mongrel and module tests now pass. ebb219e Fixed all of the fileserving termini so they use indirection requests. d8937ac You can now select the encoding format when transferring the catalog, with 'yaml' still being the default but 'marshal' being an option. This is because testing has shown drastic performance differences between the two, with up to 70% of compile time being spent in YAML code. Use the 'catalog_format' setting to choose your format, and the setting must be set on the client. a0fa09f Revert "Fixed #1201 - all external node attributes are converted to strings." 8f8ce60 Fixed #1431 - Provider confines must now specify similar tests in one call. 7fa7251 The mongrel-related tests now run without mongrel. bdbd992 Updated /spec/unit/rails.rb test de6aec6 Fix Ticket 1426 - services on redhat are restarted again 0a0fcaf Fixed #1414 - Return code from waitpid now right shifted 8 bits 61b9bcd Added Changelog entry for new auth_key type 65b9869 Further moves from the examples directory and ext directory 4ce7159 Fail instead of log when rescuing remote file connections 4c5293b Fix #1409, Move path expansion from the type into the provider 8043655 Fixing #1408 - --loadclasses works again. 605d760 Moved debian to conf and updated examples directory d25c2b2 Fixed #1407 - allowdupe is now a boolean group parameter. 9eb9aff Fixed #1368 - updated Red Hat init scripts c7dc73f Fixing the user ldap provider tests edf99c5 Added message referencing ReductveLabs build library 6ff9246 Fixed #1396 - Added sha1 function from DavidS to core 19b76c7 Fixing #1401 - integration tests now work regardless of the yamldir. 667fac1 Fixed #1226 - Gems can now specify source repositories. 21d4957 Correct whitespace 4762b52 Moving the gem test to the non-ral directory 71f4b02 Importing Sam Quigley's work to enhance gem support for sources. c751e4e Fixed #1272 - ldap group names will be converted to GIDs. 0922c3b Fixed #1399 - the ldap user provider knows it can manage passwords. 196494a Fixed #1231 - Exceptions during startup should now be clear. 9d69b3f Testing and simplifying the Transaction::Change#backward method. 61ec332 Removing the Transaction::Change#transaction accessor. 2863df2 Refactoring the Transaction::Event class. a37a784 Adding tests for the Transaction::Event class 31ffeab Adding tests to the Transaction::Change class. 8865bdf file object creation should fail if source is not present 73c06c0 Renaming Puppet::Event to Puppet::Transaction::Event 84b5665 Updated test/ral/type/sshkey.rb test 5ef8979 Removed debugging from lib/puppet/util/ldap/connection.rb 6124c69 Renaming the Puppet::PropertyChange class to Puppet::Transaction::Change. ba12d30 Fixed #1232 - the rundir no longer specifies a user/group, and there are now client- and server-specific yaml directories. be169da Removing all of the code related to the interface type. 04ecb74 Doing what I can to fix #1128, but just in preparation for removing 'interface'. 2279acd Adding changes to config print that were missed in fix for 1183 a87885a Fixing the "describe" in the redhat interface specs bd3f8e3 Fixed 1240 - puppet will function more like puppetd if graphing or reporting are enabled. 7a6ae29 Add a missing test for exercising the last untested line of lib/puppet/type/ssh_authorized_key.rb 731d0f2 Minor documentation updates for ssh_authorized_key type c825c99 Fixing the ldap node terminus to merge facts with the right name. 4b6b22e Adding logging when a node's facts can't be found daf0d9d Backporting a test that was failing in master, and fixing it 38540d5 Fixing the ldap node integration test so it cleans up e03c1be Fixing #1382 - existing uppercase certs, keys, et al will be renamed. 5156230 Use generate instead of autorequire in the ssh_authorized_key type based on Luke's comments d3a8125 Fixed #1006 - puppetrun --class works again. I added the class membership testing to the Ldap node terminus, and added tests, c1e010f Fixing the Node::Ldap.search method to use an indirection request. 4d22a95 Switching the ldap terminus to use Util::Ldap::Connection. b47d4e1 Added a 'search' method to the ldap node terminus. a1d1abd Adding an 'instance' class method to ldap connections. ee9d002 Fixed #1114 - Facts in plugin directories should now be autoloaded, as long as you're using Facter 1.5. f1d5903 Fixing #1388 - the package test no longer uses 'require'. 8c5c949 ssh_authorized_key: autorequire, default permissions and cleanup 5a283d6 Fixing #1374 - Using Puppet::Type.type() in tests 17afb8a Fixes #1195 - Updated Gentoo init scripts 00182ff Fixed #707 - special '@reboot'-style cron jobs work again. c83b23d Updated CHANGELOG for two missed commits 2380fcd Fixed #1012 - templates in the templatedir are preferred to module templates. 4d95364 Fixed #1221 - aliases to titles now work for resources. 24ca81f Fixed #1360 -- allowdupe works with groups again. 955a8ff Removed test/util/loadedfile.rb tests which fixes #1370 9c1ab14 Fixed #1371 - Updated bin/puppet to use Node.find aedfa2b Fixed #1369 - the init service provider now supports HP-UX. 422dea0 issue 1183 d3a4d9a Updated Rakefile fixes #1367 5f600dd Fixing #1168 (for 0.24.x) -- automatically downcasing the fqdn. Also requiring that passed in certnames be downcased; the setting system isn't currently flexible enough to automatically downcase it for the user. ac7f596 Fixed #1201 - all external node attributes are converted to strings. 6658463 Updating the changelog for the changes to node lookups. 1f19453 Removing the Node.find_by_name method. 51e1ba8 The LDAP Node terminus now searches for the fqdn, short name, and default. fb4e843 Refactoring the 'find' method a bit in the LDAP Node terminus. 317af36 Removing the now-obsolete Node.node_facts method. b7bd427 Converting the Node.node_names class method into an instance method. 75c94e3 Removing an obsolete, unimplemented test 98e38a6 Adds support for keepconfig for the dpkg provider fixes #234 4d70449 Fix bug in test, add more specs and small refactor 86f8ff4 Removed the unless condition in query, because the issue is a stale cached value and added comments that query will now always do so. 4539b1c Issue 1215 7b2c310 Adding another note about the save_object stub. d816614 Fixing #1362 -- I had previously removed a stub point needed for testing. 9b1301c Removing a duplicate call left over from debugging 087480b Replacing all two-space indents with four-space 3980323 Adding ruby interpreter lines to the tests missing them. 9fe2b03 Adding execute bits to every test currently missing them. fb5f09b Fixing how the Indirector::Request sets its options. 4b29a5e Fixing how the indirection tests for whether the request has node info. 6764af3 Change description of spec to make baby jesus happy 946081b Try again bdc578a Applying the fixes recommended by David Schmitt to the inline documentation of the ResourceTemplate class. 886c984 Updating the docs for ResourceTemplate. 29c840a Adding a class for using templates directly within resources (i.e., client-side templates). This would really only be used for composite resources that pass the results of the template on to generated resources. 1205881 The mongrel and webrick REST handlers now extract certificate information. e8044f9 Adding to the indirection request support for authentication information. dbd9b40 Updated fix for ticket #1271 cf3b98e Applied patch for ticket #1271 9943da6 Further Emacs puppet-mode fixes 3c2e69f Fixed Rakefile to install non-.rb files to fix #1266 ad3803f Fixes for install.rb running of tests that fixes #1267 65c1889 Fixing #1242 -- lack of storeconfigs only produces warning, not exception. 8a22b59 Fixing #1265 -- the ca/client tests now all pass again. 02411f5 Always using the cert name to store yaml files, which fixes #1178. 89100c4 Moving the majority of the pkgdmg docs to the wiki, fixing #1264. 9decf35 Put function in ticket #311 in correct location 6c3e7e1 Reverted function - "Added cron random function fixing ticket #311" c173264 Refactoring warnings.rb for tests. e6837a4 Fixing an inaccurate test so the tests will run correctly in all branches. 054a811 Removing extra debugging bdcf5db Fixing tests that are broken when running as root under OSX 10.5 d55a755 Added warnings test and cleaning up trailing whitespace. c0f78b4 Fixed a bug in my tests which caused them to fail when run against the master branch. d54338f Added cron random function fixing ticket #311 6ea494f Pushed patch fixing #1235 c370104 Fixing the node/catalog so that it can convert from parser catalogs to RAL catalogs. bd51a53 Fixing transaction support for prefetching generated resources. 4434072 The ldap user/group providers now work when no users/groups are in ldap yet. 419f244 Adding support for settings within the existing Facter provider confines. 3e13bd5 Intermediate commit so I can move on to other things. ee4be4f Removing an unused file. Closes #1229. b8ce6a1 Mocking Facter in an integration test, so it works with no networking 77ee4ec Refactoring how the provider confine tests work, again. 0820819 Minor cosmetic changes to cleanup some style elements and get rid of some cruft. 6a6a1d9 Another refactor based on feedback from Luke. This includes adding an accessor for @@state to make testing a bit cleaner. ee04129 Refactored tests based on feedback from Luke. d7f25ff Rewritten tests for Puppet::Util::Storage. c5da401 Add unit tests for Puppet::Util::Storage 8008bbc Modified the 'factpath' setting to automatically configure Facter to load facts there if a new enough version of Facter is used. a02c6bb Fixing a mock in the redhat interface test. 390db80 Updated puppetd documentation which fixes ticket #1227 2d6a914 Fix for latest method in rpm provider (fixes #1224) 38545d9 Crontab provider: fix a parse error when a line begins with a space character a1409d7 Moving all confine code out of the Provider class, and fixing #1197. 995991d Switching the Provider class to use the new Confiner class. c9757a6 Moving the 'confine' handling to separate classes. ac79a79 Duh, fixing all of the paths being loaded for spec in the moved tests. d02334f Moving all tests that are in 'ral' up a level. e7bef08 Fixing the user test. 158d3df Added the ability to add arbitrary attributes to ldap. 83ef1b0 Fix for #1219 b500689 adding more autotest docs c61fc02 Adding autotest info to the ext/ directory. 59b9958 Correcting whitespace in the templatewrapper code. 49dde11 Adding has_variable? support, fixing ticket #1177 d8cc1c5 adding execute bits to tests 5e2a4b5 updating the changelog for the ldap providers 17e8158 Adding ldap providers for the user and group type. c56e9a6 Fixing another test that wrote to ~ 954aad4 Fix Emacs mode indentation of multiple nested blocks f52e343 Enhancements to syntax highlighting and indentation for Emacs 9bf21a6 Use our own count-matches for Emacs 21 compatibility 20e60b1 Applying patch by martin to fix #1207. 270c007 Clarifying the exception when there's a syntax error but a valid parser. f3fa589 Fixing a test that wrote to ~. ae842ea Fix for urpmi provider that fixes #1217 da4cdd2 Fix for ticket #1218 - changed to appropriate variable name 88ec3d8 Cosmetic fix 67dc261 Removed "none" as a valid type attribute value, it was useless anyway db8a46c New native ssh_authorized_key type 2b185af Add values for dump parameter for the mount type closing #1212 69fc802 Update to man pages, fix to ralsh help text and fix for #1211 c57e194 Fixing an error message to be more clear 5a2bbad fix bindir/sbindir defaults on OS X 10.5 fff6ad9 Fix for ticket #1209 b2a3db9 Fixed #1196 - added /sbin/service support for the redhat service provider + some doco fixes 62ca726 Fixed some tests broken by #1176 82b9f61 Added puppetlast script to ext directory a35450b Pushed patch for #1176 - configtimeout fix 57fd88b Pushed patch for ticket #1191 - adding globbing support to ports provider b5640a1 Pushed patch for ticket #1187 - freebsd pkg_add support 0a5d8a6 Fixed #1195 - support for gentoo openrc 4599791 Pushed schema patch for #1193 eac14f6 Fixed #1189 and added support for --all to puppetca --clean d9846fc Fixishing some pending tests, including filling in the connection information. cb617f2 Making the changes necessary to get the REST support to work with the current state of the indirection work, including using a request object and an expiration date. a6a397b The 'destroy' method in the indirection now returns the results of destroying, so they can return true or false. 04aba52 fill out specs for network_* methods; refactor lowest-level network hooks a0804ae adding rest_connection_details helper to Indirector::REST -- will need to be overridden to lookup the real connection details aed1375 make sure unit indirector specs are working with #save; fill out network_put pending specs 75bf05d removed a debugging helper from the Indirector::Rest#save method 9187a34 updating mongrel/webrick unit tests to match integration-tested version of REST save functionality 93bc1a9 adding REST save support, with integration tests. A handful of unit tests in that area now need to be updated. 99b295b disabling caching for Puppet::Indirector::Indirection as it was causing hella problems with testing save without caching; judging my luke's blog this is going to be rewritten somehow anyway 1befd1d work-in-progress; playing with refactoring network_* methods inside Indirector::REST f28f20b Added support for destroy/DELETE over REST (including units & integrations on both webrick & mongrel). 0797440 updating search integration specs to include webrick e8caf13 making search work over REST, w/ unit & integration specs b750482 unit specs and implementation for Indirector::REST#search method a7f2dd4 placeholders for integration specs on final REST methods cebb677 ensure that we only run the mongrel specs when mongrel is available as a feature dab9deb bringing Indirector::REST specs to mongrel-land as well. 1e0f19b Make mongrel happy like WEBrick. d24c03c exceptions on remote end now properly passed to local end via REST and re-raised (integration-tested) 7a73434 Much larger commit than I would like to land at once. This is all REST-related code. Two specs are failing related to how Mongrel is initialized for REST; will fix those shortly. a1c4579 a trivial integration test to test whether the RESTful indirection terminus has a remote shot at working; will need to be upgraded to actually be useful 7d51146 fixing Puppet::Node::REST class name to work with autoloader inflection (Puppet::Node::Rest), so we can do Puppet::Node.terminus_class = :rest e86fde2 This is the first version where mongrel and webrick are reliably startable and stoppable via Puppet::Network::Server. c2f8c69 the indirector will not serve xmlrpc (this is the responsibility of the legacy networking code; it was a mistake to include stubbed support for it in the new code); removing 13c40e9 removing obsolete TODO comment 2cdd0f8 puppet-compliant indentation b49fd49 Resources now return the 'should' value for properties from the [] accessor method (they previously threw an exception when this method was used with properties). This shouldn't have any affect functionally; it just makes the method equivalent to 'should' for properties, but it works for all attribute types now. 4aaad26 Modified the 'master' handler to use the Catalog class to compile node configurations, rather than using the Configuration handler, which was never used directly. I removed the Configuration handler as a result. 2925ad1 Fixed #1184 -- definitions now autoload correctly all of the time. 376628d Removed the code from the client that tries to avoid recompiling the catalog. The client will now always recompile, assuming it can reach the server. It will still use the cached config if there's a failure. 3718b64 Fixing #1173 -- classes and definitions can now have the same name as a directory with no failures. d91b6d8 Fixing #1173 -- classes and definitions can now have the same name as a directory with no failures. 738889b Fixing the expire method (it wasn't using a request internally), and fixing the Facts class so it auto-expires any associated cached nodes when facts are saved. f285f1a Moved the request creation into the Indirection class instead of the Indirector module. Also, added an 'expire' method to the indirector, so there's an easy way to expire cached instances. d420701 Making the log messages around caching better. d82ac98 Fixing the executables to use the new indirection api. 7774d9c Ported the rest of the indirection terminuses over to expecting requests instead of having a random interface. bf728d2 Intermediate commit. 644d6ba Fixing some tests that were failing because new base types were added to Naginator, but no new related resource types were added. 768315b Adding the ability for indirection requests to be created with instances instead of just keys. 38f0f48 Fixing an errant comment 69a321f Fixing the tests that were failing because of the use of the indirection request object. f9881ed Adding a Request class to the Indirection layer. This class is currently only used internally by the Indirection instances, but I expect that I will soon be pushing it to all of the terminus types. 4032a27 Fixing the integration tests related to the destroy fix. Yay. 0bd5799 Fixing one other test that was failing because of the change to Indirection#destroy. 941177a Changing how destroy works, just a bit -- it now accepts the name of the instance to be destroyed, rather than the instance itself. c6729d1 Reworking the caching layer to use TTLs instead of versions based on timestamps. This just modifies the indirection class itself, there is still some work to do to remove version code from other classes. 8e1e06f Removing unused code from the file_serving/metadata class. 1458123 Adding an envelope module to handle indirected instance expiration. bd858df Changing the default environment to production. 80f8b80 Adding validation to the user type to confirm that the group list does not contain any commas. This seems to be a common problem. 92765ea Making a test executable 7295626 Used stubs to decouple our code behavior from the behavior of the underlying filesystem, as well as removing the need to sleep (which caused the tests to take a long time). 911c7fb Additional fix for emacs syntax for ticket #1160 c13486e Revert "Additional fix to emacs for ticket #1160" bb65226 Additional fix to emacs for ticket #1160 6f1c469 Extend workaround from 56aad69f8cdf8b0b08fdb7985014986223fa4455 to not only fix UIDs but also GIDs e621985 Changed some non-standard Ruby locations to env ruby shebangs 2036d22 Fixes debian service enabled/disable issue as detailed in #1161. 1c02749 Committed patch from #1160 335972e Pushed patch to fix #1174 6f32e95 Adding the report reference back; I don't really know why I removed it, since the information in it isn't anywhere else. f927b97 Updates to rrdgraph documentation e51d05c Better fix for #1020 4a39d64 Revert "Added updated fix for #1020" 2cac600 Fixed duplicate oid for parentnode and environment in schema - addresses #1170 eae5cee Fixing a duplicate word in the mount docs 4f8df98 Added updated fix for #1020 aa830b9 Adding 0.24.4 header to the changelog 4c63b69 Add a bunch of directives, allows a full parse of stanford's huge nagios config Also reformatted a bit 9d30b26 Fixes #1148 - replaces #!/usr/bin/ruby with #!/usr/bin/env ruby. 874a02f Added check_puppet.rb Nagios check plugin (See #1162) 491a696 I think this will include the man pages in the build but overall the Rakefile needs a rewrite 9cf7150 Added some more tests for loadedfile, based off the old unit tests. 077312a Added rspec tests for loadedfile 0.24.4 ====== 3a8053a Updated to version 0.24.4 d3e4ed7 Updated to version 0.24.4 55a9009 Pass source to pkg_add via the PKG_PATH environment variable if 6a53519 Fixing #571 -- provider suitability is now checked at resource evaluation time, rather than resource instantiation time. This means that you don't catch your "errors" as early, but it also means you should be able to realistically configure a whole host in one run. 528bbf1 Fixing a couple of tests. 017f673 Moved the configuration of the Node cache to the puppetmasterd executable, since it otherwise causes caches to be used in all cases, which we don't want (e.g., bin/puppet was using them). bd3f6ec Disabled man page creation by default and updated CHANGELOG 4bfc4ef Modifying the way ensure is handled so that it supports having it be a parameter. This is only useful if you want a composite resource that just generates other resources and passes the value on. d93e1b4 Fixing #1138 -- the yamldir is automatically created by the server now that it's in the :puppetmasterd section rather than a separate :yaml section. 273c7ec Disabling http keep-alive as a means of preventing #1010. There is now a constant in Puppet::Network::HttpPool that will disable or enable this feature, but note that we determined that it can cause corruption, especially in file serving (but it's client-side corruption). 6aa6fdb Applying patch by Ryan McBride to fix OpenBSD package matching. The actual problem was caused by the fix to #1001. 5a31959 Added man pages and man page creation logic to install.rb e5b16b2 Ported #198 man page creation functionality to 0.24.x branch 18320b8 Found all instances of methods where split() is used without any local variables and added a local variable -- see http://snurl.com/21zf8. My own testing showed that this caused memory growth to level off at a reasonable level. Note that the link above says the problem is only with class methods, but my own testing showed that it's any method that meets these criteria. This is not a functional change, but should hopefully be the last nail in the coffin of #1131. f6325dc Found an array that leaked pretty quickly between reparsing files, thanks to work by Adam Jacob and Arjuna Christenson (the finding, not the leak). I'm going to act like this fixes #1131, at least for now, but I doubt it does, since that shows general memory growth over time, whereas the leak here should go away as soon as files are reparsed (because the parser is holding the reference to the leaking array). 25b81b3 Fixing a test I broke with my fix to #1147 4f400d4 Fixed #1147: Cached nodes are correctly considered out of date if the node facts have been updated (thus causing node facts to again be available in manifests, for those cases where they were not). 54bedb2 tweak the (already applied) patch in 388cf7c3df7ce26e953949ed6fe63d76cbbb3691 to resolve #1137; also, add tests which detect the problem. a240969 Applying patch by wyvern to fix #1142. e00065a * puppet/ext/emacs/puppet-mode.el (puppet-indent-line): Clean up the code somewhat after commit 738d275f41f3eaf015800021dd2dfe6c42a1ae79, as promised. 5f3ed8d * puppet/ext/emacs/puppet-mode.el (puppet-indent-line): Be more sophisticated about what we do at the beginning of the buffer, so that the first expression after an block-opening statement that happens to begin at the beginning of the buffer gets indented correctly. This may need some cleanup, but I wanted to get the correct behavior committed first. d1d408c Fix bug mentioned in commit f814e23eab140ad01df4a4a3b187fcbf20da02be: 7514057 * ext/emacs/puppet-mode.el (puppet-comment-line-p, puppet-in-array): New helper functions. (puppet-indent-line): Rewrite to handle three more situations: indent elements in an array, indent single-line blocks, and ignore previous comment content when indenting non-comment lines. 40a389a * ext/emacs/puppet-mode.el: Untabify, in preparation for substantive changes. 0c45a5a Adding another commit for #1136 -- Consolidated the setting of the loglevel and destination to just one call, rather than the multiple calls that remained. 4ce1d37 Fixed ports documentation error c75cc42 Added more detail about the requirement for ruby-libshadow for useradd password management 1dc6dc2 Final fix to #1136 - further changes to --test setting e714156 Second fix to #1136 - fixed --test problem 2155fe1 Fix for ticket #1136 --verbose cancels out --debug 4cc18ed Applied patch in #1134 2795ba4 fixing another failing test a40e9b7 Fixing some tests that only failed under certain circumstances (mostly, when loaded with other files, or when loaded from rake or autotest rather than separately). 7d35ae8 Refactoring how the catalog creation handles errors. Previously, if there were an error creating a resource, the error would propagate leaving any previously created resources still in memory. 1b3c85b Removing extra debugging 2d90468 Fixing a unit test for node integration e81fc58 Settings now (again?) do not use a section more than once, which should make the system a bit more efficient. fca467d Removing explicit requires of types and providers, because they were conflicting with Puppet's autoloading. This is clearly a sign that our autoloading is silly, if Ruby's own loading easily makes it unhappy. 34129d9 Removing obsolete code from the fileserving handler. This was obsoleted in 0.24.2. f62eec8 updating resource references in the docs d0554db Hopefully *finally* fixed the "already being managed" problem (#1036). The problem only cropped up if there was a failure when trying to manage the system -- this would cause the setting-based resources not to get cleaned up. 13c6de3 Adding a rake taks for updating the trac docs 0.24.3 ====== 0e26a07 Updated to version 0.24.3 990638c Updated to version 0.24.3 18ed28b Updating changelog for 0.24.3 ab72048 Removing a Settings.use that is unnecessary bba0b43 Downgrading the "Using cache" message from the indirection to debug 1dc0e24 Modified the ldap node terminus to also use the facts version as the version for a node, which should similarly encourage the use of the yaml cache. (Related to #1130) 4a45a1d Caching node information in yaml (I figured caching in memory will cause ever-larger memory growth), and changing the external node terminus to use the version of the facts as their version. This will usually result in the cached node information being used, instead of always hitting the external node app during file serving. Note that if the facts aren't changed by the client, then this will result in the cached node being used, but at this point, the client always updates its facts. (#1130) f3a304c Modifying the yaml terminus base class to use the timestamp of the yaml file as the version of the object. 8b29368 Adding a filebucket test to puppet-test da77cb6 Adding a test for local compiling 405802e Using the indirected facts rather than master.getfacts, so no factsync is used 388cf7c Regression in :node_name functionality 872ced7 Flat file now does writing to a tempfile. 4956323 Fixing #1132 -- host names can now have dashes anywhere. (Patch by freiheit.) ecb873d Fixing #1118 -- downloading plugins and facts now ignores noop. Note that this changes the behaviour a bit -- the resource's noop setting always beats the global setting (previously, whichever was true would win). e2370b3 Fixing the service-stop on debian, using the patch provided by DavidS e8029cc Fixing the "tidy" type to use an option hash for specifying its parent class c955f61 updating changelog for already-closed tickets eecc22c Cache the same type we check for, hopefully fixes #1116 f1216f8 Revert "Cache the same type we check for, hopefully fixes #1116" ca0b62a Cache the same type we check for, hopefully fixes #1116 35214eb Fixing the rest of #1113: External node commands can specify an environment and Puppet will now use it. 2261032 Partially fixing #1113: LDAP nodes now support environments, and the schema has been updated accordingly. 4c0f6c8 Fix for 1094 647f5b4 Always duplicating resource defaults in the parser, so that stacked metaparameter values do not result in all resources that receive a given default also getting those stacked values. ee8fac6 Changed name of method for clarity per code review 8192475 Ticket #1041 The CA serial file was getting owned by root because it was using a different method to write to file 4c47656 Applies patches from #1111 and #1112 443db20 Fix tests depending on the Puppet[:localcert] file existing using stubs 8627139 Updating version number 3b5daf7 Revert "Fixes #1099 - use of -m option with -d option for home directories" 0ae58a9 Fixes #1099 - use of -m option with -d option for home directories 0.24.2 ====== f019cac Updated to version 0.24.2 bfdac69 Updated to version 0.24.2 6faed12 updating changelog for 0.24.2 ee88c58 Applying patch by DavidS to fix #1083. a7339ec Fixing a few tests e008b02 Fixing #1110 -- transactions now always make sure their tags are arrays. 65b7267 Fixing the fact that resources that model defined resources were getting finished multiple times, which meant they got multiple copies of metaparams. 4c3fa78 Fixing a few more loading order issues. 857814a Fixing tests that did not work with Rails 2. 7ca0ad6 Fixing a test that changed the environment for all later tests, thus breaking some of them. 9b07758 * Tweaks for puppetshow UI cleanup 0139889 * Add migration for "created_at" (hobo expects it) * Tweaks for puppetshow interface cleanup * Delete unused tagging lib and puppet_class model 43aea83 renaming ral/types to ral/type in the tests 879ee22 Fixing #1062 by moving the yamldir setting to its own yaml section. This should keep the yamldir from being created on clients. fd1573f Fixed #1047 -- Puppet's parser no longer changes the order in which statements are evaluated, which means that case statements can now set variables that are used by other variables. 9d6e926 Fixed #1063 -- the master correctly logs syntax errors when reparsing during a single run. abd688e Fixing #1092 by no longer using the resource reference to look resources up, which means there's no concern about not finding the resource, which is where the nil was coming from. We now just iterate over the vertices. 29aafb4 Fixing an integration test so it cleans up after itself 82b02b9 Fixing #1101 -- puppetrun works again. dd17d4c Fixing #1093 -- 0.23.2 clients are again compatible with 0.24.x servers. :ignore links is now equivalent to :manage links. c0b5352 testing automatic commit emails 614ab9f Adding a 'control' parameter to services, for those service types that need a control variable to enable/disable. bb8051b Removed the loglevels from the valid values for 'logoutput' in the Exec resource type -- the log levels are specified using the 'loglevel' parameter, not 'logoutput'. This never worked, or at least hasn't for ages, and now the docs are just correct. ff4f65a replacing tabs with spaces in the redhat interface provider f3db79e Fixing a typo in the mailalias resource type 4e55999 Removing the validation on package sources, since some platforms (e.g., hpux) do not have a well-formed requirement for the source. 42bfdf2 Fixing #1085, I think -- I was not returning a resource if the class had already been evaluated, but this was only being run into in corner cases -- mostly where one class included another class, I assume. 1258512 Fixing #1084 -- the node catalog asks the individual resources whether they're isomorphic, and they in turn ask the resource types (or default to true for defined resource types). 9a33487 adding a comment to the namespaceauth.conf file 04892ee Adding an example namespaceauth.conf f0975df Trac #1038: not a fix, just an attempt at improving the situation. c8b320e Corrected #1040 fix - this should now be right - trace was after raise 07cd482 Making a couple of other small fixes, requiring classes that were not being required in the right order. ff97059 Somewhat refactored fileserving so that it no longer caches any objects, nor does it use Puppet's RAL resources. In the process, I fixed #894 (you can now copy links) and refactored other classes as necessary. Mostly it was fixing tests. 939c952 Fixes ticket #1080 f184228 Fixes ticket #1079 - added . support for tags 9b6e501 Fixing a test that was failing when a user-specific fileserver.conf actually exists. 5d35bc5 Fixes #1078 and includes new test 7976015 Removing a test I never migrated from test/unit. 279a0c5 Fixing a test that was actually reading in keys from the filesystem and failed when those keys were unreadable. 098a69c updating checksum for #1010 fix b06767e Quashed commit of my fixes for #1010. 5e18b8d Hasstatus in the init service provider; it was just doing a boolean check, rather than comparing to :true. 60f18c2 Fixed minor documentation error 39a6756 Fixed #1073 - moved show_diff and other logic post config parse f006e17 Fixed test for #1040 1f0ea5a Second attempt fix address ticket #1040 39f9818 Removing some extraneous debugging from a test. d82bfd8 Attempt to fix #1040 - catching errors in compilation e830f28 Fixed #1018 -- resources now have their namevars added as aliases in the resource catalog, just like they were added in the resource classes. 60dd569 Fixed #1037 -- remote unreadable files no longer have the permission denied exceptions caught, thus forbidding them from being replaced with 'nil'. 2de4654 converting parser ast node specs from setup/teardown to before/after 9927efb converting parser ast host class specs from setup/teardown to before/after c86c1da converting node catalog specs from setup/teardown to before/after 61cdc2b converting indirector yaml specs from setup/teardown to before/after f702096 converting facter indirector specs from setup/teardown to before/after 516e5b6 converting indirector checksum file specs from setup/teardown to before/after d260b7e converting parser compilerspecs from setup/teardown to before/after 1913134 converting mount provider specs from setup/teardown to before/after 6781e10 converting indirector terminus specs from setup/teardown to before/after dba64dd converting file serving configuration specs from setup/teardown to before/after b4c8f99 converting indirector ldap node specs from setup/teardown to before/after 3cb1118 converting indirector direct file server specs from setup/teardown to before/after 9e632bc converting parsed mount provider specs from setup/teardown to before/after becafab converting mount type specs from setup/teardown to before/after 034336b converting indirector file specs from setup/teardown to before/after 12f139c converting package type specs from setup/teardown to before/after b8d5ce0 converting fileserving/configuration/parser specs from setup/teardown to before/after eb0bdcb converting indirector/module_files specs from setup/teardown to before/after 22d6f9f converting ral/types/schedule specs away from setup/teardown d04567a converting indirection specs away from setup/teardown to rspec compatible before/after usage aa14ce7 moving setup() methods to before :each, so that the tests will run with rspec, as opposed to just rake (which calls them directly with ruby, as opposed to any spec binary) f9f32c4 reordering spec binaries to prefer the local vendor/gems/rspec/bin/spec option d11cd39 Fixing a failing test that resulted from a change in how checksums are created. 62d7616 Fixing the directory service provider's behaviour when there's no value for a given attribute. f087df0 Fixed ticket #1072 - Debian directory updates 0eede76 Fixed Ticket 1009 - problem with plist xml parser. We do not need the plist parser for pkgdmg. 458cb23 Fixed ticket #1070 - puppetrun configuration parse problem 2e41803 Fixed ticket #1069 - removed remaining references to multiple configuration files 10d4d0e Fixed ticket #1065 - Solaris SMF manifests 8fa4120 Fixed ticket #1068 - Minor documentation fix 30128bd Really minor change to user creation in Leopard. 6013b25 Refactoring the incremental checksum generation slightly based on the code in type/file/checksum.rb. aebd303 Enhancing the stand-alone checksums utility module with the rest of the checksums we're likely to use, and adding tests, which I somehow missed when I wrote this file. df3fbc7 Fixed #1060 - Debian service removal and addition 5ef8a3e Changing portage to use Puppet::Error instead of Puppet::PackageError, fixing #1059. c4f7c51 Fixing comment -- ticket #1027 instead of #1064 8920557 Fixing #1064 -- providers et al are now autoloaded from modules even when Autoload#loadall is used. 4829711 removing "lib" deprecation notice from autoloader f8afe13 Fixed #1043 -- autoloading now searches the plugins directory in each module, in addition to the lib directory. The 'lib' directory is also deprecated, but supported for now to give people a chance to convert. fe02591 Fixed #1003 -- Applying DavidS's patch to fix searching for tags in sql. 9b1bfc1 Fixed #992 -- Puppet is now compatible with gems 1.0.1. 0cfa1d2 Fixed #968 again, this time with tests -- parseonly works, including not compiling the configurations, and also storeconfigs is no longer required during parse-testing. 8367fdf Renaming the 'pfile' and 'pfilebucket' files to plain 'file' and 'filebucket'. This should have been done years ago. a42c3ae 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. d406353 Removing the last vestiges of GRATR from the PGraph class 068b61e Removing obsolete references (they're in the indirection reference), and adding error handling to puppetdoc. 98dbfa2 Loading the mocha gem from the puppettest.rb file. 12fa0fa Fixing the Rakefile so all tests run in one task instead of multiple. cb5def4 'rake' within the spec dir works now, anyway, which is a good start. Autotest still doesn't work, though. eb74033 Fixing the puppet_rspec autotest plugin to use the modern interface 1b90f7f Trying to upgrade rspec, but not having much luck. bcb9b56 Copying over Rick's work from the master branch supporting autotest and cleaning up the rspec support. 3af6827 Adding an inflection util class. 7e45553 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. c8da318 Moving the ast node tests to rspec (which I could have *sworn* I did this weekend). In the process, I fixed a couple of bugs related to differentiating between nodes and classes, and then cleaned up quite a few error messages. 8b2fae0 Removing the last remaining vestiges of GRATR -- removing the bangs from 'add_vertex!' and 'add_edge!'. cf21ade Switching the Node catalog to use the Tagging module instead of its own tag methods. 744cd45 Added a 'tagged?' method to the Tagging module. d21416b Switching the Node Catalog to using a separate method for validating that a given resource is unique within the catalog. This no longer allows any duplication, even with Execs. fd0c5cb Changing the name of the Compile class to Compiler, since it's stupid to have a class named after a verb. 5ebaa89 Refactoring the interface between the Compile class and the AST::Node class to match that to the definitions and AST classes. e247b56 Changing some methods in the Compile class to be more internally consistent (switched store_resource to add_resource, and store_override to add_override). 6a4cf6c 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. 3b740ff Converting the Compile class to use a Node::Catalog instance as its resource container, instead of having its own behaviour around resource uniqueness. 194e730 Moving all of the tests for Puppet::Parser::Compile to rspec, so I can refactor the class to more heavily rely on a Node::Catalog instead of doing its own resource container management. fb4bdc0 More AST refactoring -- each of the code wrapping classes just returns a resource from its evaluate() method, and all of the work is done in the evaluate_code method. This makes the code cleaner, because it means 1) evaluate() has the same prototype as all of the other AST classes, 2) evaluate() is no longer called indirectly through the Parser Resource class, and 3) the classes themselves are responsible for creating the resources, rather than it being done in the Compile class. 5a0e34b Refactoring the AST classes just a bit. I realized that all of the evaluate() methods only ever accepted a scope, and sometimes one other option, so I switched them all to use named arguments instead of a hash. 82720d5 Removing some obsolete code from the AST base class dbaffae Ceasing autoloading ast files; loading them manually instead 7c500da Stubbing Facter during the snippet tests, so they are faster and work with no network 084d0fb Adding more information to dependencies that do not resolve b293763 Applying patch by Jay to fix #989 -- missing crl files are correctly ignored, and you now use 'false' instead of 'none' to explicitly ignore them. 2931723 Fixing the Settings class so that it correctly handles file values that are false. f7b0ca9 Fixed #1052 - fixed gentoo service management b3f67ec Fix ticket 974. My original "fix" wasn't. This actually fixes the problem by using a regular expression that matches only up to the first square bracket. 8f0d87d Added :env parameter for backwards-compatibility, with warning about deprecation. :env parameter sets new :environment parameter. Changed instances of :env to :environment for consistency with other types. Added tests for new parameters. This cimmit fixes ticket 1007. 139ff33 Fujin's patch for ticket #1007 - consistent use of 'environment' instead of 'env' aedd59c fix bug 974 - filenames with opening bracket characters generate exceptions b8036a9 Updating the docs for the cron type 28a8577 Added hostname test for hosts type 16df87c Updated fix for ticket #151 and added a test ed0c745 Fixing #1017 -- environment-specific modulepath is no longer ignored. (Cherry-picked from master.) ade9f3c Store a resource before adding relations to it otherwise activerecord will complain. This fixes #933 047ec54 Fixed tickt #1034 - doco typo 6ff9423 Significantly refactoring the lexer, including adding Token and TokenList classes for managing how the tokens work. 11799b3 Fixed #1001 348aa3e Fixed #1028 - examples incorrect for 0.24.x 974fcdb Removed womble-specific Debian build section 321b8fd Fixed #1006 - changed ldapnodes to node_terminus ee6ddc9 Removing tons of unnecessary calls to "nil?" from the lexer. 7a4935f Fixing a couple of tests, one related to recent tagging changes and one that somehow slipped through when I removed the GRATR code. 9a290bb Second attempt to fix ticket #151 - host type now validates IP addresses and hostnames/FQDNs 4a7fcfc Revert "Fixes ticket #151 - host type now validates IP addresses and hostnames/FQDNs - the regex for the latter is quite complex but I have found it bullet-proof in the past" b561ae6 Fix bug #997, only evaluate non-virtual definitions 1ccc9c3 Fixes ticket #151 - host type now validates IP addresses and hostnames/FQDNs - the regex for the latter is quite complex but I have found it bullet-proof in the past d7a89b4 Fixed #1019 - made libshadow available for non-Linux users 8a649ff I think I've finally fixed #959, by having the Settings class skip any resources that are already in memory. 52eba77 Fixing #794 -- consolidating the gentoo configuration files. f43be56 Removing the line that marked fink as the default package manager on darwin. f98be4a 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. 2cbab2c Fixing #1008 -- Puppet no longer throws an exception when you've pointed a file at a source that doesn't exist and you specify 'ensure'. f5674cd Fixing #995 -- puppetd no longer dies at startup if the server is not running. 7a9aae8 Wrapping the Resolv call in the mongrel server so if it fails it doesn't kill the server. 9161ae8 Applying a fix for #998 -- I used a patch equivalent to bartv's, although I could not use his commit because it was against the 'master' branch instead of 0.24.x. 046a326 Fixing #977 -- rundir is again set to 1777. 4618140 Updating docs for ssh. 7ee4746 Adding a parse test to puppet-test. 35145f3 Fixed ticket #1005 - added additional logcheck lines b24ac77 Fixes ticket #1004 - documentation fixes for ralsh and puppetrun 1ff9d65 Updated documentation for builtin cron type; added information about range and step syntaxes. f15696c Updated tagmail documentation fixing ticket #996 e3d4ea8 Fixes ticket #993 - tagmail with smtpserver specified does not add To/From/Subject header 40addcd Fixing #982 -- I have completely removed the GRATR graph library from the system, and implemented my own topsort method. 927dff4 Fixing #971 -- classes can once again be included multiple times. 117926c Fixing the unit tests for nagios_maker; I could swear I'd already done this but I must not have committed it. a7bca7e Removing the requirement in the parsed mount provider that the fstab file exist. 1bdf3f8 Fixed #984 - Added Debian to reponsefile doco b1f13af Fixed #980 - minor wiki formatting error in nagios_maker.rb 2f9c13b Fixed ticket #979 - code configuration option doco 039dc8d Fixed ticket #979 - pkgdmg.rb documentation 1154c42 Fixed ticket #978 - logcheck/puppet 33e319a Added builtin support for all Nagios resource types. I use Naginator to parse and generate the files, with ParsedFile to handle record management and the like. 68cde4f Removing the one-off naginator provider for nagios_command. 348f257 Adding the metaprogramming to create the Nagios types and Naginator providers. This is basically all of the code that's necessary to create all of the needed Nagios types. 4e8bc40 Fixing the inability to manage '/' directly. It was a result of stripping extra and trailing slashes. 9b1d036 Adding the first round of Nagios code. There are no tests here, but at least a single Nagios type is functional. Now I need to do some metaprogramming so this works for all nagios types, and add tests for the whole thing. 20367c6 Updated for 0.24.1 20d430d Adding 0.24.1 tag to the changelog. 0.24.1 ====== 4fa6546 Updated to version 0.24.1 d17fb7a Updated to version 0.24.1 40439da Updating an exception message a bit. e2fc425 Attempting to fix #952 -- catching any exceptions thrown during a run. c59ff62 Further fixes toward #965. Turned out that the previous fix caused other problems. 4d28b10 Updating the failure when the CRL is missing, so it's clear how to solve the problem. e4446b6 Fixing parseonly with a modified version of jay's patch from #968. bc0616e Updating filetype detection for vim, and changing the filestructure for vim files. (#900 and #963) 927cb24 Fixing #967 -- default resources no longer conflict with managed resources. c998a25 Adding a --print option to puppetca that just prints the full-text version of a certificate, and --verify, which uses the external openssl command to verify the cert against the CA cert (I could not find an option to the builtin Ruby libraries to do this). 9c32c9c Removing the ability to disable http-keep alive, since it didn't work and is now unnecessary. 553b2ad Entirely refactoring http keep-alive. There's now a central module responsible for managing the http pool (Puppet::Network::HttpPool), and it also handles setting certificate information. This gets rid of what were otherwise long chains of method calls, and it makes the code paths much clearer. 92b0ebc Fixing #967 -- relationships now work when running 0.23.x clients against 0.24.0 servers. 1ada24d Fixing some tests that were failing with the recent ruby that has ssl issues. c22a584 Uninstalling packages through 'ensure => absent' works again for the rpm and yum providers. 8f5989a Updated for 0.24.0-2 cc2d532 Updated for 0.24.0 933b1df Fixing #961 -- closing existing, open connections when a new connection is requested, and closing all connections at the end of each run. e0dab9a Updating changelog to reflect the fact that we no longer warn about explicit plugin mounts. 4d3a368 Remove the warning about an explicit plugins mount. 178093f Fixing the Rakefile to include the yumhelper.py file in the file list. 0.24.0 ====== 6b02bd5 Updated to version 0.24.0 e92f1cc Updated to version 0.24.0 22daebe Adding changelog update for misspiggy/0.24.0 e0f5444 Fixing the webrick test to provide a correct host name so the stupid ssl checks pass during the test. 106f319 Changing the statefile to only being managed by clients, not by puppetmasterd. 4ebb8d0 Hopefully finally fixing #959. Loading the stored cache resulted in the resource duplication, based on how the settings are used, so I added a test to only use those settings if the directories do not exist. 690e287 This should be the last fix for exported resources. Hosts were keeping the export bit on all resources, even when they'd collected another host's resources, which caused a duplicate copy that was still exported. f1169ee Not using the main section when running the store report, since it is unneeded and can cause conflicts within puppetmasterd ce5cab1 Removing extraneous debugging from the schedule resource type. cb0c4ee Renaming 'configuration' to 'catalog', fixing #954. 7ac3bd7 Renaming the 'null' terminus type to 'plain', as requested in #960. a21ee00 Copying the fact-loading code from the network client to the Facter terminus until I have a better solution. This problem was discovered becomes of #958. 1bbaf18 Applying patch by whaymond to fix #955. d9200a0 Adding what is hopefully the last commit for #896. Here's the changelog: 74db777 Removing the 'addpath' commands from the freebsd service provider, since it's no longer needed or even valid. b19a0c9 Removing the recently-commited paludis provider, because it breaks autoloading as indicated in #956. 02b64ab Applying patch by josb in #884 to provide pattern matching in the tidy type. 584127c Applying patch by raj in #881. 4ee5ab8 Applying patch for portage package support from thansen in #895. ed642ac Replacing freebsd service provider with the one provided by raj in #880. 117f005 Adding paludis package support as provided by KillerFox in #741. 3248c93 Fixing #937 -- I had not ported the dot methods at all, and I had to make a few small changes to make them work. a8bf74b Fixing #946. b70f00a Fixing some further failing tests resulting from the fix for 862d1f7 Adding an Indirection reference, along with the work necessary to support it. da77e4a Updating the changelog with external node info. f127d04 Fixing #951 -- external nodes work again, but you have to set the 'node_terminus' setting to 'exec'. 7a4ae08 Fixing the rest of #948. My previous work was sufficient, except that I was not passing the interpolated value in to the hook, which meant the libdir was set to something like $vardir/lib. 3790ce1 Fixing part of #948 -- per-setting hooks are now called when the configuration file is parsed. The bug is still there, but I'm getting closer. b852c2f Fixing #941 -- calling pkg_info instead of info in the openbsd package provider. ae33e57 Fixing #923 (again). The host storage method was not correctly searching for the host, so it was creating a new host on each run, which is what was causing the conflict. 9ad7d1a Adding basic unit tests for type/user by DavidS from #948. 038b9c8 Fixing #923. Resources that are collected on the local host are no longer marked as not exported. 5886d37 Applying patch by whaymond_home to further fix part of #896. 072b03e simplify PluginsMount a012849 Updated tests for http_enable_post_connection_check configuration setting. 4d4abd3 Better test to match the behavior of the code. 24cacdb Fixed test case for http_enable_post_connection_check f94d6d3 As per lutter; augmented fix for #896 to be configurable and defaulting to validate the server certificate, honoring CVE-2007-5162. 8eecbe5 Fixing another failing test I somehow missed in my last big commit 88304cc Renaming @model to @resource in a provider 75647ee Fixing a couple of tests that were failing on a different platform or with a different version of ruby 811fefa Fixing #892 -- filesystem mounts are no longer remounted. dedc56a Fixing #527 (rewrote service tests), #766 (services only restart when they are running), and #918 (service tests fail when hddtemp is not installed). 421b3fc Another backward compatibility patch, this time helping with a new server and old client bbf8a8b Making a few changes to the transportable class to enhance backward compatibility 11ae473 Theoretically, this patch is to fix #917 (which it does), but there were enough problems fixing it that I decided something more drastic needed to be done. 8127397 Fixing puppetca so it passes its tests, duh. Apparently wyvern's patch broke things a bit and I was stupid enough not to run the tess right before accepting. 2282046 Adding a top-level ResourceReference class that everything else can use to canonize how we refer to resources. Finally. c6d1746 Fixing the first half of #917 -- the ResourcReference AST code now correctly finds the resource. It's getting lost in the configuration translation, though, so I need to fix that, too. 6c1d8d3 Applying fix to xmlrpc client tests by Matt Palmer 6b2c0d8 Fixing the error message as requested in #893. 1b2142b Applying patches from #823 by wyvern c7cd7ec Fixing the markup on the pkgdmg provider so it is a bit better 1e6ba6f Fixing #781, from what I can tell. I'm leaving it with no tests for now, since it's a very small chunk of code and it's *insanely* difficult to test this kind problem. 5d30ea9 Fixing #810 -- I catch the error and prefix it with something a bit more useful. 4e52ffc Fixing #796 -- the fileserver can now start with no configuration file (it creates both default mount points if it does) and puppetmasterd no longer requires the configuration file to exist. 168fa5f Fixing the asuser method in Puppet::Util::SUIDManager so that it correctly just yields if you're not root. It also no longer tries to set :uid or :gid; just :euid and :egid, and it once again sets :egid before it sets :euid, which is important because you usually can't change your group after you've changed your user id. 0ef6b95 Fixing #931 by keeping track in configurations of what transportable resources get converted to, so different names don't throw it off. a38b415 Fixing #927 -- rewriting the test to actually test what it's supposed to be doing and to skip the whole network connect thing. 7ff8ea5 Fixing the persistent and periodic schedule test failures by rewriting the schedule tests entirely. 18b4c3a Fixing #924 -- clearing the configuration cache before and after the test. 2cb1199 Fixing the breakage that I caused when I added the 'declared_feature?' method to provider features. 2d19ee2 Fixing #920 -- I have replaced the existing mount test with an rspec version. It's not perfect, in that it only tests the :ensure state, but that's where 90% of the behaviour is. c3dde68 Fixing #919 -- installed packages used for testing are just ignored, rather than throwing a failure. 47890f9 Fixing a test that was erroneously testing for the wrong feature 12ebbe2 Rewriting the tests for the package resource type, fixing #930. fc7f1b4 Fixing #921, mostly by just deleting the existing test. I had already migrated all of the tests into rspec but forgot about these tests -- they were only in the rails/ subdir because people kept not running the parser/ tests after modifying the Rails code. 9311bdd Applying patch by trombik from #756. b575d15 Integrating Matt Palmer's patch to provide a 'plugins' mount, fixing #891. The patch was ported to the current code by David Schmitt, I applied the rest of Matt's patches, and I then fixed all of the code so that the tests passed. 36c947e Fix #896 - Always disable DNS checking of certificate when making https connections. 3fb8e2e Applying the rest of Matt Palmer's patches related to providing a plugin mount. 7eb09ab Implementing the test for setting the Rails log level. 676efa7 Incorporating patch 20071030034736-6856b-6004090b3968cdbf7d366a03ee1c44e2160a3fe0.patch from womble, and rewriting and significantly enhancing the unit tests for the Puppet::Parser::Collector class; it should have full coverage now. There are no integration tests for it, so there's still no guarantee that it works at all, but hey, we're a lot better off than we were. 7f1b2d6 change up rails specs again with Luke's help 8de1412 Integrating most of Matt Palmer's from http://theshed.hezmatt.org/mattshacks/puppet/_patches/puppet-0.23.2/. a88891a Fixed #906 - Augmented Cert DN regexp check to work with Pound and Apache. c19d08a mock all use of Puppet[] in Puppet::Rails.database_arguments e69e0c3 fix spacing b435f04 fix socket argument to AR and add rails spec e53693e Hopefully fixing #698 -- fixing the markup for the pkgdmg package provider 7c36ae9 Adding patch 20071030035457-6856b-bd1c45ed5ecd753b2cb4f05347061f7245cc175a.patch from womble -- Force removal of directories during pluginsync 880a8e2 Adding patch 20071020020745-6856b-dbc63ff3f137a4039fb997b9978202d52f621e8c.patch from womble -- Fix some residual instances of /var/run fever 696f1fb Adding patch 20071020015958-6856b-69efa7868cf3df3f2a2da6fcfc3b794bbb532c7f.patch from womble -- Remove rundir from puppet.conf, and add a NEWS entry to document these changes 7a95017 Adding part of patch 20071020011907-6856b-05b59120fdb90ab4a5842f91613247b07206a4ba.patch from womble -- Fix for Debian#447314, by fiddling with /var/run/puppet. This does not accept the whole patch, because the change needs to be tested around other platforms. 38b970a Adding patch 20070927050018-6856b-7fa134180aceb9cee2e667630345f5f8467a9d0c.patch from womble -- Catch more retryable errors in the XMLRPC call wrapper 276034f Adding patch 20070927042000-6856b-38a0c82fd0a0d950937e7fe5a38b9901743402b3.patch from womble -- Recycle the connection more aggressively, to stop problems with clients that are newly requesting certificates 3bf7031 Adding patch 20070926235454-6856b-079fc12a9b63d59afd59aa205bc8bfeb350b097a.patch from womble -- Recycle the connection if we're presented with an EPIPE 0ebd99e Adding patch 20070926214630-6856b-edd313b08555033710c90a94d9d8beaf889d6cf4.patch from womble -- Fix spelling mistake in debian control files 7ed1c17 Adding patch 20070913032650-6856b-b1cca1c249415c6076ffcecb9df1525a728457c7.patch from womble -- Fix annoying database deletion error for ParamValue objects. 28430c4 Adding patch 20070913032546-6856b-0de200e8450920e7f712c54bf287ae43c7fda8af.patch from womble -- Only set dbuser if explicitly asked for d7b381b Adding patch 20070913011122-6856b-98bf03f09c8e19679390d73fdddc2e4d1273f698.patch from womble -- Add changelog entries for the pulled pgsql patches a7d75d3 Adding patch 20070913010926-6856b-eb64be3b5169b7af674388124b406a1db7470880.patch from womble -- More restrictive permissions on some puppet-related directories 407734f Adding patch 20070913005414-6856b-db5ea77e10ec6869ad01a4bd6483912c337f3a70.patch from womble -- NEWS for the ssldir transition 1486d39 Applying patch 20070913004017-6856b-cdbbba99de0b33b64874205a27833b5114fcc6b9.patch by womble -- Allow empty config settings 03c8ffd Adding patch 20070913003810-6856b-cdc8b2e8c6c46eb8d6d073f86291a0fc5a59f429.patch from womble -- Only set the hostname and password if we want them; this allows pgsql ident auth to work it's magic 035fa38 Adding patch 20070905004837-6856b-2e7b8d8595ee0883537620c46424a4bf6174dc6a.patch from womble -- Add an attr_accessor for @http#ca_file, since older versions of libopenssl-ruby only provides ca_file=, not ca_file 63b205a Adding patch 20070831053637-6856b-dd0fddab681485ce7cea0b57336d0c48fa33f7f8.patch from womble; updates changelog 72c0e7b Adding the debian directory via patch 20070831052721-6856b-b90bb56a4ed37ea420f10352a0a366068cddc7e4.patch from womble 7efe24f Fixing #882 -- I just added a quick hook to the Master handler again; we need a better long-term solution, though. 56aad69 Patching a bit for #804 by making the maximum much higher UID and making it tunable, but it has not gone away yet. a525ab5 Fixing a couple of tests that were failing because of the environment changes. 6d74ddd Accepting a modified form of the patch from #885 by immerda. b745f04 Fixing #886 -- the problem was the I had changed the base class for Parameter, which apparently lost the 'nodoc' method for that class. dbe70a1 Added calls to endgrent/endpwent in util/posix.rb to fix #791. 7f504b0 Applying patch from #896 by whaymond_home, adding more certname aliases. 1cb40ec Obviating targets in interfaces; they now just generate a warning. eee9f5e Adding more tests to the redhat interface provider. It no longer uses the :target parameter (which I'll be removing in the next commit). 1a4e4fb Rewriting the sunos interface provider to manually parse and generate, rather than using ParsedFile. This should fix #777, and has from what I can tell. 8cbe8bd Adding unit tests for the sunos interface provider. 3d2e1a5 Adding some unit tests for the interface type before i go messing around with it cca613d Fixing the first part of #787. Not all collections were being evaluated on the first pass because they were being deleted from the collections list during evaluation, which caused some to get skipped. This commit fixes that problem, which helps in the trivial cases where the collections are in the same scope. I expect it's still broken for more complicated usages. 96b3cde Applying patch from #834, apparently fixing a problem with bringing up alias interfaces. 9472eef Removing the bootproto and broadcast attributes from the redhat interface provider, since they are not needed a7a46af fixing the path to the spec helper in the exec test 3d31dc8 Fixing #762. The main problem was that I accepted the patch in #744 which broke the templates. In the process, I also added test code for the redhat interface provider and rewrote how parsing worked to make it more testable. 8ecdfc2 Moving the exec test into the types/ directory 94e63ad Fixing the last failing test relating to the environment changes 7fe5bfc Fixing the exec spec so it works when non-root and is a bit cleaner 8cc07ad Using the Environment class to determine the default environment, rather than plenty of different places having the logic of how to determine the default environment. 53008e5 The Puppet settings instance now validates environments when you set an environment. 9e5fc76 Fixing #911 and #912 -- there's a default environment (development) and you have to specify the valid environments for your site. cc88441 Removing the manual ssldir setting by David in 59626cb3907d36e4fd762277daa76f523faf0908 1bf3999 Fixing a failing test from my fix for #446 -- I had changed the behaviour of Resource#override_parameter unintentionally. I've corrected the comments so it's clear why the original behaviour was there. 3f0b250 Fixing a few test suites that passed when run as part of the suite, but failed when run individually. The problem was that I changed lib/puppettest/support/utils to have a separate module and I didn't test the individual files. 4bd7b6f Fixing #896 by applying DerekW's patches, with slight modifications to fit coding style. 8ad2732 Fixing #446. I ended up largely not using porridge's patch, but only because the code (and my coding style, to some extent) has changed so much in the last few months. Also, added specs. 1b78f57 Add Exec{ logoutput=> on_failure } 2b14f62 Reverting the changes I'd made toward removing the global resources. These are commits: 9cf477b Applying fix by Jeff McCune from #905 edc4b1d Fixing a SimpleGraph unit test so it doesn't depend on hashing. c19835c Fixed most failing tests, but there are still over thirty failing. 4afbaa6 fix #903: add patch from hrvojehr 32d9afc tests for #903: fail when no logoutput is generated on failure 9290cc8 Modifying how default resources are created; they are now added to the configuration by the master client, rather than by the creating types. ffb4c2d This commit is the first run at removing all global references to resources. It deprecates the class-level [] and []= methods, used for so long to provide closure behaviour but now unnecessary with the node configuration's ability to function as a resource container. b65fb83 Fixing a parser test -- really, just removing tests that belong with the AST classes rather than in the parser. 72510bf Fixing #800 by refactoring how configurations are retrieved from the server. The real problem was getting all of the validation done before any caching, which required a good bit more refactoring than I expected. dd7caa7 Moving some compile tests to the spec/ directory, and switching the node scope to no longer be lazy evaluation, just like I switched 'main'. When I made all of these classes and nodes lazy evaluated, I should have decoupled my real goal (using resources to evaluate them) from the idea of lazy-evaluating them, and this basically does that. 47a2605 Changing the 'main' class to no longer be lazy-evaluated. It was getting evaluated after node classes, which caused even stranger ordering issues. a4e8f1c Adding a memory terminus for facts, which is really only used for testing 3851415 fix dependency on $HOME, which causes massive failures when running without environment 59626cb fix failing CA test, when testing with incomplete setup (no ssldir, no DNS) a6ad326 fix the underlying dependency on the environment in the cron type d48ee3e fix crontests depending on ENV[USER] by using Etc.getpwuid(Process.uid) instead 8fe892d fix a testfailure when running spec tests as root 445c29c fix #872: improve property(:content).insync? 5726412 tests for #872: check interaction between "replace" and "content" 61ef289 fix #815: add :main to all use() for :reporting and :metrics 418bc21 remove obsolete runners variable a535cbb Commenting out the time debugging I was using 3f583dc Adding unit tests for the module that handles the logic around mounting and unmounting. This includes a fix for bug #761, which required a different regex for Solaris. 8f04446 Fixing the 'mount' tests so that they no longer modify the local system and they run fine as non-root users. ba19989 Switching the class resource evaluation to only happen when using :include, not (for example) when evaluating node classes. cf75168 Classes once again get evaluated immediately when the 'include' function is used, instead of being lazy-evaluated. 4441052 fix #891: create a plugins mount which collects all modules' plugins/ subdirs dfe774f Switching the base class for the Relationship class. It was previously using the GRATR::Edge class, which had wonky overrides that dramatically slowed down sorting (its hash mechanism hashed the source and target so that edges with the same source/target got the same hash, which we actually don't want any more). 4194526 fix #760: property_fix has to be called after creating a symlink b250416 fix #731: add exported=true to collect_exported 1ffcce0 Splitting the puppetd tests into two tests. It is still not a very good test, but I do not know of a good way to test this, really. 065a1d0 Switching the graph base class from GRATR::Digraph to Puppet::SimpleGraph, which should dramatically enhance performance. It should be largely functionally equivalent, with the only difference being that edges are no longer deduplicated. 3f21e93 Adding a new graphing base class, because the GRATR stuff is just too slow. This class has just about no iteration, and vertex deletation is dramatically (as in, 1000x) faster). Here are the results of some very simplistic graph operations: ef99495 Caching the 'parent' value, which resulted in a drastic performance increase. 826efe8 The configurations should now be functional again -- file recursion was previously not working, because the relationship graph was setting itself as a resource's primary configuration, which caused it to try creating its own relationship graph. db293cf Fixing a bit of indentation and commenting in the xmlrpc/client file 956daa5 This won't be perfect by any stretch, but put in a moderately reasonable autotest config file. c7b36b7 One significant step closer to getting autotest running properly on the Puppet specs. Created a spec/lib/monkey_patches/ directory for holding patches to RSpec functionality. Extraced 'confine' and 'runnable?' support from the local copy of RSpec (spec/lib/spec/) and now load them from the monkey_patches/ directory. Fixed a bad include in one of the specs. Made it possible for the gem-installed spec binary (which autotest calls) to be used with Puppet. Imported the Autotest::Rspec class, created a PuppetRspec autotest class, added a discovery.rb file for autotest to pick these up. 6585835 Adding patch from #879 by tim d03f68e Changing the test/ classes so that they work from the main test/ dir or from their own working dir, like the specs do. This was just a question of changing how their libraries are loaded. c0a07ac File serving should work now, both recursive and single files, across modules, local file system, and the traditional file server. 54fc80d Exceptions on requests are now captured, exceptions are serialized, and exception text is passed back via REST. e7bfe0b Finish serializing successful results (via calls to to_yaml, etc.) for REST handlers. Refactor request building in REST handler specs. d28a904 REST handlers now properly returning 200 status on success. 1746751 Adding post- hooks for :find and :search in the indirection class. 09f9c3c Adding the calls to the authorization hooks in the Indirection. b874751 Renaming the FileServing TerminusSelector module to IndirectionHooks, because I'm going to add some hooks for transforming returned objects. de5d91e Renaming the :local termini for metadata and content to :file. 7fa99b0 Link handling is now in the file serving classes. This was done by putting all of the functionality in the Content and Metadata class (actually, in a new base class for them). 688fcdf Adding searchability to the fileserving termini, using the new Fileset class. 393a3e8 Adding a Fileset class for managing sets of files. This is the new server-side for file recursion, and I'll next be hooking it to the fileserving 'search' methods. This is basically a mechanism for abstracting that search functionality into a single class. b2b8f75 Adding authorization hooks to the file_server and module_files indirection terminus types. Both hooks use the fileserver configuration, but the module_files hook only uses the 'modules' mount. 8f827ff Renaming the 'mounts' terminus to 'file_server', and renaming tests accordingly. 08099b7 File serving now works. I've tested a couple of ways to use it, and added integration tests at the most important hook points. 264331b Partial work done for ssl certificates. ec39672 Adding this test stub that's been sitting around in my repository for a while. fc60751 I've now split the file-serving termini into two separate types (in addition to Rest): A local terminus that just uses direct file paths, and a mounts terminus that uses the file server to figure out what the path should be. 64c6700 Fixing all of the classes that I just renamed, and adding the TerminusSelector module to the File Metadata indirection. 56b83fe Renaming the file serving indirection termini to match the standards I set in the TerminusSelector. 33d7dc0 I'm working on making file serving work in the indirector now, so I added two abilities to the indirections: Models can specify a module to extend the indirection instance with, and indirections will use a :select_terminus method, if it's available, to select the terminus to use for finding. (It's currently only used for finding, not destroying or saving.) 8156185 Renaming the file_serving/{content,metadata} indirections so that they make more sense in the REST API, and creating stub tests for the indirection termini. Now it's on to create the rest of the tests for them. 2718b63 This is the first mostly functional commit of the new file serving structure. The next step is to hook it up to the indirection so we can start writing integration tests to see if we can actually serve up files. e1dd5dd Adding spec stubs for authorization in the indirection e69a50a Fix test which is conditional on mongrel installation. 8bf5196 Oops, forgot this file in my last commit. d0bd48c Adding the first pass at modifying file serving to work with indirection. I've split the fileserver handler into four pieces: Mount (which so far I've just copied wholesale), Configuration (responsible for reading the configuration file and determining what's allowed), Metadata (retrieves information about the files), and Content (retrieves the actual file content). d2b891f More specs, fleshing out the returns from REST e5921c5 getting more fine-grained with the response specs -- the target is always moving. 705f76f Argument passing now supported on {webrick,mongrel}+REST. ce34968 Make the actual runtime be more robust when mongrel is not installed. 6cd0f37 Make it possible to run all tests even if mongrel isn't installed. Shouldn't "confine" produce some output when running spec? Who knows. 216dd8c Refactoring, argument processing for model methods. abbc824 Tweak to move model lookup functionality into the Handler base class where it belongs. Robustifying the request sanitization a bit more. 2a497ff Refactored to use a Handler base class for server+protocol handlers. Finally eliminated dependency on Puppet.start, etc., from WEBrick HTTP server class. {webrick,mongrel}+REST now support request handling uniformly; need encode/decode next. 6ab78f6 Inlined the controller, eliminating a class. Mongrel+REST has the right bits for request handling prior to the encode/decode/exception-handling bits. Refactored to make the common logic extractable to a base class. b8c877c Registration now built for {webrick,mongrel} REST handlers. 3c370b3 Going back to each server+protocol object being responsible for only one indirection, as the REST vs. XMLRPC models are different enough that the object must register itself on initialization and handle the request when it comes in. c06edda First pass through initializers of {mongrel, webrick} REST handlers; hooks into Indirection to look up models from indirected names. ab4c7fa Minor tweaks to make the ::Server initialization a bit more robust. Fail on unknown HTTP Server types; fail fast. 099c546 Finish front end of delegation to server+protocol helper classes ("handlers"). b1d6223 Bringing in initial handlers for server+protocol pairs. a815f78 Reorganizing the file structure for indirection terminus types. ba95202 Partial support for building Handlers for all handler-protocol pairs. ef8ebe0 Implementing address & port support for new webrick server. c34efbc Hooking up address/port support for the various servers w/ specs. Still need to start up a webrick server w/ address + port (this is far too incestuous with Puppet lib & Puppet.start at the moment). 9a179ec trivial: WEBRick -> WEBrick, to be more consistent with how the WEBrick ruby classes are named. e56406f Implementing listening state tracking for webrick and mongrel. ec71e05 More unit specs for mongrel and webrick; more code to make them work, yo. 31384fe Pushing functionality down to webrick/mongrel classes now; cleanup in the base server / http server classes + specs. 694f98b Fixing failing tests, including making the debian service provider test work on non-Debian platforms. 29feac0 Translating the report handler to an indirected model. I've provided backward compatibility with the old handler. 74d77f7 Adding version handling through most of the indirection work. This counts as the first commit where configuration compiling actually uses the caching correctly according to the application model. e90191a more stuff for the interim commit 10039b9 interim checkin of network stuffs 512096a Fixing some small spec failures resulting from test fixes. The problem was in how TransObjects were converted to RAL resources. (Committed while flying over Arkansas.) d24c1cc All tests should now pass again. ec58355 Fixed #819. Applied patch provided by matsuu. 7ac7872 Fixed #822. Applied patch provided by DavidS. fc9c850 Adding support for versions and freshness-checking to the indirection layers. This should hopefully enable the different application models we need in our different executables. 1befcc4 Homing in on a clean separation of concerns for a low-coupling, high-cohesion "server" model that will handle REST and/or XMLRPC on webrick and/or mongrel. 5c32c8e Somewhat better documentation of the :absent field feature in fileparsing. d055cbc Make it apparent that absent fields in a record have a value of :absent, which is different from what appears in a line. b6dc1ae Trivial tweak on HTTPServer module file a7d220b Moving the webrick/mongrel "servers" over to HTTPServer module instead of Server. Using Server as the master class for client connections. Server (former RESTServer) will instantiate the appropriate subclass based upon Puppet configurator setting. There are now tests broken in the network section which I can't seem to figure out yet. Not a happy place to be. cdaad28 Fixing error thrown when the end of the file is encountered unexpectedly 7d7e428 Removing obsolete comment f084d83 Another round of test-fixing around the changes I made to the configuration system. 'puppet' itself still works, even with -e, but I expect that puppetd and puppetmasterd are broken, and there are still quite a few broken tests because the default fact store can't write but that's the default behaviour for a networked configuration master. 9c58c47 Adding a :code setting for specifying code to run instead of a manifest, and removing all of the ambiguity around whether an interpreter gets its own file specified or uses the central setting. d35cd94 Making "null" the default node source, so nodes are at least created easily 0e336bf This commit is focused on getting the 'puppet' executable to work. As a result, it involves a lot of integration-level testing, and a lot of small design changes to make the code actually work. 1fa5912 Adding the integration tests to the Rakefile for spec, fixing the integration tests, and extending the Classmethods for the indirector so that indirected classes can set the terminus class and cache class. a93db87 Adding another test to the ldap node source -- we make sure we throw an appropriate exception if a parent is specified but we cannot find it. 9984a35 Fixing some terminology so some ldap tests are easier to read. 6acde71 Switching the indirection from using settings for configuration to requiring explicit configuration. This means that if you as an application developer want to use a different indirection terminus then you have to specify it; something like: 8ba3a70 Fixed #838. Applied patch provided by DavidS to add more robust update functionality to the dpkg provider. f41c843 Fixed #837. Added freebsd service provider by trombik. 533ce4b Fixed #855, but I didn't add any tests. 19ad238 Fixed #827. Applied a form of the patch provided by porridge and wrote a test. 29accba Minor tweaks. 2412199 Allow for multiple REST servers to be active; some terminology changes in spec; fleshing out more behavior, implementing. 102ad23 Added .listening to REST server, handle listen states and transitions. 187d910 Spec'd a reset() method for clearing out known routes. Uses the unregister method so that any hooks there will be run. Probably a violation of YAGNI, but I'm willing to suffer it :-) fd841b3 Updating first portion of the Network RESTServer spec with example code, getting the added examples to pass. 9236179 Attempting to reproduce and fix #829 by applying patch by Paul. I could not get a test to show the exception, nor could I figure out how the coding bug could have actually had an impact, but it's an innocent-enough fix, so I'm fine applying it. e5c623e Fixing tests for the Configuration object, since I added the any_failed? test to Transactions. 938f051 Fixing #817, mostly using the patch by DavidS. I could not directly use the patch because I have refactored too much. fd11603 Removing the Id tags from all of the files bb3b3ce I finally tracked down the problem that was causing providers to sometimes suddenly disappear and thus tests to fail -- Kernel.require was not loading the normal ruby path (e.g., 'puppet/type/cron'), so if someone else loaded that then it would replace the in-memory type with a new one, but that new one couldn't load its own providers, because the Kernel would ignore the providers, thinking they were already loaded. 782bc4d Fixing the yaml path so that it is correctly created for puppetmasterd. 7c8fc8b Fixed #854. d4afe39 Fixing #813 -- empty configurations again work. 5d50ca7 Fixing #814 -- when files are missing, the exceptions should now be more reasonable. 1be1db9 Updated CHANGELOG. 0b8893b Fixed #832. Added the '--no-daemonize' option to puppetd and puppetmasterd. The default behavior of 'verbose' and 'debug' no longer cause puppetd and puppetmasterd to not daemonize. b45a7ca Adding more behaviours to the Puppet::Module spec, and fixing some bugs in the process. 3f90ddb Interpreting "hidden" class from spec drafts as a REST Controller. This name, functionality, and/or location in the tree is subject to change, but it's down now somewhere so we can move forward on it. 861c21d Added partial spec for the serving of REST information, as well as some client-side REST terminus behavior. 8722e43 Use external helper script to talk to yum; should avoid any more trouble with "yum list". Fixes trac #836 1174d99 Fixed a failing test where we presumed that non-string Fact values would have type preserved across a P::N::Client.master.facts call, which is not true. 7fe18e3 Fixed a test which was secretly sneaking off and pulling certs from ~ if they were there: Added set_mygroup method, removed duplicate setme method. Included PuppetTest in the XMLRPC servlect test. fa643e6 Adding more indirection termini, mostly focused on caching information in yaml. 938b918 Adding cache support to indirection. If you have a '_cache' setting, then the indirection will use the value there as the name of the cache. 06ad6a3 Updated the configuration doc to more clearly explain where puppet.conf is expected to be by default. c8d02bd Fixing ralsh to use a configuration instead of a component ffaa8ce Demoting the "file does not exist" log to debug from notice c3c3e51 Fixing a small problem with the mailman type f8ab62b Renamed princs to principals in the k5login type. 6079348 Added k5login type written by Digant Kasundra. This is for ticket #759. 2e33061 I changed the Terminus class to switch camelcase to underscore-separated words, e.g., FactStore becomes fact_store. d6fd60c Removing obsolete fact stores and node sources. The functionality has been moved into the indirector. cdc8ea6 Taking a first stab at moving configuration compiling into the indirection system. There are still quite a few unanswered questions, the two most notable being embodied in unimplemented tests in the Configuration Code terminus. c40da33 Adding a "memory" node terminus, which will be used by 'puppet' and the Cfengine 'module_puppet', since they need to set up the node specially with classes and other weird things. 1e7c648 Fixing the spec for the checksum terminus to match the changed design in the previous commit. 048464f Adding my first integration test, verifying that checksum interaction behaves as I expect when interacting with the file terminus. 84146d0 Adding the first version of checksum support, which will acquire the behaviour of FileBuckets. 3a18348 Renaming the 'Puppet::Util::Config' class to 'Puppet::Util::Settings'. This is to clear up confusion caused by the fact that we now have a 'Configuration' class to model host configurations, or any set of resources as a "configuration". e552c83 Adding the base file terminus. This will, at the least, be used as the back end for filebuckets and the certificate authority. 86dde63 All tests now pass in this configuration branch, which means it's time to merge it back into the indirection branch. 60cd6a7 The structure for handling resource generation is now in place, which means I'm over the hump in developing this branch. a666995 Adding the last tests for the ldap node terminus. I managed to forget the tests around the main find() method. ebe7290 All indirections are working, and they have all been migrated over to the new organization. Where we would have previously had an 'ldap' node terminus at puppet/indirector/node/ldap.rb, we would not have it at puppet/indirector/ldap/node.rb, and it would be a subclass of puppet/indirector/ldap.rb. b9dc6cb It looks like the new indirection setup is complete. 02275f0 Adding automatic association between terminus subclasses and the indirection they're working with. It looks like I'll be moving terminus registration to the indirection rather than the top-level Indirector. da0555d Adding the first top-level terminus (yaml). It works and is tested, so now it is time to migrate the Facts YAML Terminus to use the / file structure instead of /. 0a48e5f Moving the Puppet::Indirector::Terminus class into its own file and adding a spec for it. 7e2ff4b Adding a couple more tests to the indirector, talking about terminus registration. I am about to change how loading is handled, so that individual termini are declared as normal Ruby classes. 7740cd4 The indirector specs now all pass. I think I need to add a few more specs, though. 4e8b671 The unit tests for the newly-resurrected indirection class now work; all we need do is fix the indirector module tests. 8212f88 Fixing all existing spec tests so that they now pass given the redesign that Rick implemented. This was mostly a question of fixing the method names and the mocks. 944cd0e Whitespace and comment commit. 46d6906 An intermediate commit so I can start working on a different branch. The file recursion code actually works for the first time in a painful while, but there are still some quirks and design issues to resolve, particularly around creating implicit resources that then fail (i.e., the behaviour of the create_implicit_resource method in Configuration). e90a51f More spec and indirector updates. 129cce8 Finally, some progress. Closing the loops and delegating registered class calls out to the actual Terminus. a6c4041 Reworking the Indirector code. Continuing to fight the classgen and instance_loader "utilities". 9fa2628 This is basically another intermediate commit. I feel like I've gone too far down the rabbit hole to turn back now, but the code is clearly getting more centralized around the Configuration class, which is the goal. 19e0493 Updates to indirection stuffs. Making a better spec and migrating to it. b3c8cdb Configurations now set a "configuration" instance variable in resources that are inside a configuration, so the resources can interact with the configuration to get things like relationships. f17f19d The whole system now uses Configuration objects instead of ever converting the Transportable objects into a tree of components and then converting that into a graph. This is a significant step, and drastically simplifies the model of how to use a configuration. The old code might have looked something like this: f014d73 Partial fix for #772. The SIGHUP now produces a EOPNOTSUPP instead of NameError. 3ccf483 Removing the completely obsolete passwd2puppet and the obsolete component.rb 3632926 Moving the resource container behaviour to the Configuration object, rather than the base PGraph class. I expect I will just do away with PGraph, but for now, I am at least going to keep configuration-related code in that class. 43f22a2 Adding a to_graph method to TransBuckets, so that the buckets can directly generate a graph, rather than having to first convert to RAL types and then have them convert to a graph. This allows us to make it so components do not need a @children array at all. This was all done because I am having the "already a parent of" problem again, and I have gotten far enough that it is relatively easy to just make this problem go away once and for all. a6fe700 Another intermediate commit. The node and fact classes are now functional and are used instead of the network handlers, which have been removed. There are some failing tests as a result, but I want to get this code committed before I massage the rest of the system to make it work again. 1459c50 Adding setup/teardown hooks to rspec, so we can use test/unit methods 3b3065b Refactoring the feature support so it loads libraries when a feature is asked about, rather than when it is defined. 65c1501 The Node handler is now obsolete. Node searching is handled through the indirector. I have not yet added the tests for the node handlers themselves, which is next. 1638089 Fixed #797. Removed the warning message about specifying 'enable' or 'ensure' when initializing a service. 6f9a444 Fixed #784 by applying patch by vvidic. 5aa4440 Doing an intermediate commit so rick can look at the work I have done so far. bb69a1f Renaming the instance loader method to "instance_load". It was previously autoload, which could class with Kernel.autoload. 6a105c4 Fixed hdiutil syntax for ticket 812 19a748b Removed TYPE token, replacing it with CLASSREF token, in the grammar and lexer. Updated CLASSREF token regex in the lexer. ca9c48d Removing extraneous logging from the node handler 041393d Fixed #774, which fixed fully qualified collection statements 6700adc *Finally* fixing the tests that were failing around users and groups. The problem was that the autoload tests were somehow clearing all loaded classes, including the providers. This is fixed now. 9af79f1 Fixing some failed tests. Mostly cleanup. Next is to make all of the user tests pass again, dammit. 50874b2 Fixing a path test. I have now made the path stuff a lot cleaner, but it apparently broke this test. ca57c79 Fixing #801 -- resources that have changes when running in noop mode do not record that they were checked, so that they will be scheduled on the next run. This is a somewhat murky solution, but considering that no one had submitted this bug before, I expect it will not hit many people. caad11a Fixing some broken tests in the master client, and adding a test for #800 but it is unfortunately disabled because we cannot realistically fix it using the current design. It will be easy after the REST refactor, though. 7abc78a Fixing #795 -- configuration elements now make sure all file paths are fully qualified by prepending the wd to unqualified path names. 4212f9c Fixing #802 -- tags are now applied before parent classes are evaluated, so parent classes can use tagged() to test if a node is a member of a subclass. 4104bd3 Fixing #807. The exception handling should more closely resemble how it used to be done. b7f4244 Renaming some ast resource classes and files so they make a lot more sense. 653c151 Fixing #806. Resources correctly look up their fully qualified definition type, just like resource references do, which causes the resource and reference to again agree on the full name of a given defined type. 40e3b37 A small change to the indirector, moving it to a module instead of a class. I still do not really know how i will use it, though. a5539cd Adding my indirector class before i rewrite it. I am probably not going to keep any of this, but i wanted to store a copy before i got much further. b0a9475 Flipped the switch so that compiles now return a Configuration instance instead of pre-extracting the configuration. 11b127b Successfully modified all tests and code so that all language tests pass again. This is the majority of the work necessary to make the separate "configuration" object work. 3b2efd2 We now have a real configuration object, as a subclass of GRATR::Digraph, that has a resource graph including resources for the container objects like classes and nodes. It is apparently functional, but I have not gone through all of the other tests to fix them yet. That is next. 0faf76e More refactoring. I have removed a few more extraneous methods from Scope, mostly just pointing directly to the compile, and I have begun (but commented out) the move to having resources to model each of the classes and nodes, in addition to the definitions. This will, again, enable a real Configuration object, and it will enable class versioning and similar features. 9d70b97 Removing the Scope#setresource method, since it was essentially redundant. The work is done in either AST::ResourceDef#evaluate or Compile#store_resource. b021587 Doing a small amount of refactoring, toward being able to use Parser resources to evaluate classes and nodes, not just definitions. This will hopefully simplify some of the parsing work, and it will enable the use of a Configuration object that more completely models a configuration. 25f6d7c Deleting old documentation that somehow made it back into the tree in the switch to git, and refactoring the evaluate_classes method on the compile object so I can use resources as intermediaries, thus making classes do late-binding evaluation. 62806bb Renaming the file containing all of the configuration defaults to "defaults.rb", since I am going to create a separate "configuration" top-level directory to contain all of the classes related to managing the configuration for a given node. 6832a4b Fixing some failing unit tests. 2625eb1 Making a couple of small bugfixes in the configuration subsystem 1de5ae0 Adding support for providing a diff when files are being changed. Currently uses a local diff binary, but could easily be changed to use the ruby diff/lcs library. Modified puppet and puppetd to automatically show file diffs when in noop mode, but can otherwise be enabled using --show_diff. This only works when running interactively, because the diffs are printed on stdout. 11081ce Multiple environment support now works, and I have even tested it in real life. This commit is mostly a bug-fix commit, resulting from the difference between real-life testing and unit testing. 9ea8e6c The fileserver now uses an environment-specific module path. I also made various bug fixes around the network tree. 51ff72c Adding a bit of testing for node names. 4e9c631 Moving the node tests to rspec, and cleaning up the spec of the node, especially WRT the environment. a8f2a33 Moving the node tests to rspec, and cleaning up the spec of the node, especially WRT the environment. 9df4fd1 And we have multiple environment support in the parser. The only remaining piece to make this complete is to add multiple environment support to the fileserver. I also renamed Configuration.rb to Compile.rb (that is, I fixed all the classes that used to know it as a configuration). ba3a861 Removing this test for now; I do not have time to port it from test/unit to rspec 37f0eed Renaming the "configuration" object to "compile", because it is only a transitional object and I want the real "configuration" object to be the thing that I pass from the server to the client; it will be a subclass of GRATR::Digraph. deb0107 Oops, created a test directory in the main spec dir, rather than in the unit/ subdir 3030d3e Modules are now tested with spec, and they now can handle environment-specific module paths. ab54183 The config class now has support for add an environment to its search path. Now I just need to go through the whole system and use the search path in addition to the parameter name itself. c6e201c I have added basic support for a search path, altho not yet with any ability to manipulate it. All config tests pass in both the old tests and the new ones, so it is time to add the hooks for manipulating the search path. 520aaaf Adding some rspec tests for Config.rb, because I am planning on significantly changing its internals and the current tests, I think, will be harder to migrate than just writing rspec tests from scratch. 724fef1 Everything up to the parser (and the Modules) is ready to support multiple environments, including the parser having an environment setting. I have also created my first spec-based tests, for the interpreter (and deleted the old test/unit tests). 3d68ed6 Oops, left out the spec rake file from the main spec commit 58494cc Building a stand-alone spec directory for creating the new spec-based tests. d59315a Adding the second half of the rspec upgrade -- apparently the "git add" thing I used did not remove the old files, only add the new ones. 5601ecf Upgrading rspec to version 1.0.8. This only includes the contents of the lib directory, and even then only the spec-related stuff, not the autotest stuff. 7c4d39e Adding environment information to the client fact list. The environment is retrieved from the normal Puppet configuration, so it is set via puppet.conf or the cli, rather than being a normal fact. b599862 Fixing the integration test between interpreter and configuration -- the interpreter was not passing on that the config should use ast nodes a54fa7e Sync to latest specfile in Fedora 8b3361a The last commits before I actually start on the multi-environment support. There are still failing tests, but apparently only those that are also failing in trunk. f1727f1 Adding the topscope metadata to the configuration being returned to the client, just like it expects, and fixing how the resource handler calls the master type. efcd1e8 Fixed CA race condition (#693) 4eb87ed A round of bugfixing. Many more tests now pass -- I think we are largely down to tests that (yay!) fail in trunk. 2a4e101 All language tests now pass. I expect there are other failures elsewhere, but I want to commit this before delving into them. My method for fixing the tests was to do as little as possible, keeping the tests as bad or as good as they were before I started. Mostly this was about changing references to the interpreter into references to the parser (since that is where the new* methods are now for ast containers) and then dealing with the new config object and its relationship to scopes. 6467c21 The first pass where at least all of the snippet tests pass. I have unfortunately had to stop being so assiduous in my rewriting of tests, but I am in too much of a time crunch to do this "right". The basic structure is definitely in place, though, and from here it is a question of making the rest of the tests work and hopefully writing some sufficient new tests, rather than making the code itself work. a846ea9 The new parser configuration object works now, but the rest of the compiling process is hosed (although the parser itself should still be fine). 282ec89 Fixing the spec library so it correctly can see its version 1527f4a Adding node caching, so that node sources are not spammed during file serving and such a953954 Keeping the node names in the node object, so that they are available to the interpreter 297dabb Refactoring a small part of the interface between the configuration handler and the interpreter. 901ae68 Requiring mocha in all cases in the test tree 70dffdd The new configuration handler looks to be ready for usage. Now I just need to convert the interpreter to use SimpleNode objects, then continue with the Configuration object. aabad8e Adding the necessary name/ip fields to the node methods 65559af Adding a "none" node source, which will be the default node source and will just return an empty node. 2ff15c0 Added shortname support to config.rb and refactored addargs 90a9d09 Finalizing the node handler. It now correctly uses the different node sources and knows how to retrieve data from those sources. Now I just need to fix the language stuff to use this handler instead of the existing node stuff. ec50484 Fixing documentation string on the file "ensure" property to remove the confusing mention of "exists" 58e3855 Added optional per-module lib directory. aab419b An intermediate commit in the work towards adding multi-environment support. 40491eb Merge /opt/rl/git/puppet b59d396 Revert "Updating more milestone names" 3e9ac59 Updating more milestone names ab42534 Applying patch by Adam Jacob to make external node tools able to handle command-line arguments 24e7b4d Revert "Updating more milestone names" 61a747f Updating more milestone names b5aefd4 Adding milestone names to changelog 7e4f270 Actually honour :namevar => true on newparam calls 01b21ae Removing extraneous debugging from crontab 6ab30eb Fix for setting global exit code ($?) in SUIDManager tests 0195893 Broaden assert_absent so that it thinks that :purged is equivalent to :absent 6a78648 Change the service name so that it is less likely to trip on a common word and spuriously fail e143cae trac #763: Make redhat provider default for CentOS (patch by jtimberman) d2f2bc0 Trivial mock cleanups ada960b Constants in provider/interface/redhat.rb are getting redifined as they are dynamically assigned, changing them to instance variables 8f05951 Changes to lib/ corresponding to test refactoring from r2759, was unaware that subversion only commited in the CWD 13f358d Highlight what I think is a problem in the test suite that I just can't solve 3de4829 Refactor SUIDManager tests to run without root, change SUIDManager's behavior to not silently fail when it's not root and fix all other tests that broke as a result. 5a25701 Upgrade mocha to 0.5.1, which gives much better error messages 5e8d71d Fix the ral:providers:host:parsed tests so they run successfully 9530df1 Updated to version 0.23.2 0d312a1 Updated to version 0.23.2 b84015a The last set of bug-fixes before the next release. This commit just fixes a couple of problems that resulted when I changed the Provider#initialize method to not duplicate its argument, which was necessary for ParsedFile. aaf5959 Adding test support for the other mongrel configuration header db0ffc7 Copying the "commands" and "confine" statements to the actual dscl providers, since they need to be there to determine where the providers are suitable. Otherwise base classes could unnecessarily affect how subclasses work. 5e419cf Fixing #749 -- environment settings no longer accumulate. Significantly adding to the cron tests at the same time, such that hopefully we will no longer have these recurring bugs. I now do every combinatorial of multi-line cron jobs, including doing them all in one file. There are, unfortunately, still edge cases, but maybe we will have some peace in cron space for a while, anyway. d121b1f Removing the code from #745 until it can pass some basic tests 1e6c2ba Adding syslog support by devdas (#745). 22e7b39 Fixing #751 -- the interface providers now have basic tests, at least to verify that prefetching and listing works. I think these resource types need to be largely rewritten, though, and they currently have no relationship to ifconfig, which seems strange. 7bda32e Fixing #731 - we are now correctly only collecting exported resources 3d629bb Fixing #730 -- mounts now call flush() before trying to mount a8bf96a Adding a file that should have been in a commit from yesterda 40e4d6f Fixing #735 -- gen_config now uses a single heading, matching the name of the process 97cd057 Fixing #314 and #729; here's the changelog: 72f2ac3 Apply fix for typo provided by Toshio Kuratomi (bz250870) 2a37c73 Removed stray debugger method. 5a5d241 DirectoryService provider for users and groups. Alternative to netinfo, as apple has indicated NetInfo may go away at some point in the future. It might happen in October. 08d8945 Fixing #734. The problem was that when I switched how the configs were parsed, I managed to lose the ability to keep values set on the cli from being overridden by values set in config files. 5eacd19 Renaming the linux interface provider to redhat 6841397 Applying patch by stick to the linux interface provider 877282e Undo previous commit, which was an error 81d690a Do not set any options if they aren't set in /etc/sysconfig/puppetmaster - otherwise we clobber settings from puppet.conf 36a3e4a Changes for 0.23.1 52e9fa0 Adding interface implementations, as written by Paul Rose 7547baf Adding a test for rails 1e11a1a Removing test that ended up being redundant 4b25750 Applying patch my emerose to fix #652. 87da172 Adding the requirement that the cert dn have /CN= in it, thus hopefully catching clients without certs 530d290 Applying a modification of the patch from Marcin Owsiany, allowing Mongrel to be a CA 64fba48 Updated to version 0.23.1 d3988cc Updated to version 0.23.1 2229dc1 Fixing #726 -- mounts can now correctly handle mounted but absent filesystems. 47b7058 Adding some code in an attempt to fix #728, but it is all commented out since I could not get it fixed in time for beaker 2e14ea4 Attempting to clean up the mount documentation as in #727. 7401ada Caching whether a given file or module has been loaded, because the loading was greedy and was causing files to get loaded every time a class or module was asked for 3f1b957 Fixing the mail aliases generated by the mailman list provider; it was generating capitalized list names 3f1c865 Fixing #725. I was apparently not deleting the alias I was creating to the components. 55014a2 Hopefully fixing #720 -- I added tests and a lame back-off system to give the child process time to write eacb06c Converting mount test to use mount everywhere instead of df 501e8c8 Adding the ability to specify relationships to classes, using Class[name] resource references. b9dd7ee The first round of bug-fixes in preparation for beaker 5d7c5c9 Adding documentation to the "test" script 4f34fb0 Removing the chdir from util.rb, I forgot that the directory often matters f2a1a10 Hopefully fixing #640, and maybe some warnings at the same time. I added a call to Process.setsid after the fork, and I chdir'd to /. fdd2d49 Fix #696. (patch by Jason Kohles) 90c8b8c Fixing #716 -- the package type was considering anything that was not "absent" to be installed, but that included "purged", thus the problem 0316bed Applying patch by DavidS to fix #711. 48755b1 Fixing #702, hopefully. As suggested, I switched to "mount" instead of "df" to determine whether an fs is mounted. f59ce4e Fixing #695 -- resource references will correctly serialize and unserialize in the db 53a469c Fixing #703, mostly. You still cannot do multi-condition queries, but you can at least query against any parameter, and matching any value is sufficient for a match, so the tags work fine. d5569bc Fixing #719 -- the filebucket docs now only mention filebucket, not pbucket d9a30a6 Trying to get rid of the warning from #724 e618065 Applying a slightly modified patch by Dean Wilson -- puppetca now exits with a non-zero code when it cannot find certs to clean. 49d8ef2 Guard the rpm command suitability confine better so we hopefully stop seeing all of the 'Command not available' errors f104dc5 Updating the docs to mention that you can use the file server with no server name cf25b25 Fixing some logging in cron 60ef578 Fixing the rest of #705, except for the env stuff, which I was not able to reproduce. 53c2f0a Fixing #691 -- I added "refreshable" features to the types that can restart, although exec does not have providers (yet), so I just made the docs a bit clearer 4c1a70c Reordering some of the type docs e0237d1 Removing notice about "import loop", which was happening constantly when autoloading module files 1d261bf Fixing error message when a parameter is getting redefined 554c23c Adding rpm as a specific command to :rug 54a5f77 Fixing #589 2c13d53 Fixing #468 -- fully qualified resources can now be specified as dependencies e88d694 Applying patch from #714 -- aptrpm now loads on RHEL c3290a0 Fixing the mailman provider so it correctly matches case. Apparently mailman helpfully autocapitalizes list names. 2086e07 Removing extraneous debugging 6ddbec3 Fixing the interpreter autoloading so that it correctly loads classes even when being loaded from a namespace f217fbf Fixing the instances method. It now works when there are already managed resources. edb1be2 Fixing the :check metaparam so it does not try to check unsupported parameters f1462cb removing the test for a method I removed yesterday e98edac Applying docs patch by David Schmitt from #713 7580657 Applying a version of the diff to the defined() docs from David Schmitt a4b94cf Fixing the first half of #705 -- matching no longer fails. I think this also fixes #648. d104d4b Having FileType instances automatically back their contents up to a filebucket, so it is much harder to lose content. This does not yet back up crontab contents, though. 17a830d Fixing transactions so that they do not flush resources that are in noop f570a5f Adding a maillist type, with support for mailman. It is not as flexible as I would like, partially because of how mailman is implemented (the "mailman" command is never in the search path), but at least it is working for mailman on debian. 2d3c920 Adding support for a "mailalias" type, with /etc/aliases support initially. I have not yet figured out how to best rebuild the aliases file when necessary. fdfe0a3 Adding line/file info to parsing errors in ParsedFile 9f685e6 Adding support in Property for declarating whether a given property type will match all @should values or just the first. c8801b7 Always setting a to_s value in authstore, so we do not get dumped objects e79828f Cleaning up a log message in the transaction 20b9060 Adding benchmark info to fact retrieval in the config client 7a71db8 Adding patch by Valentin Vidic to add the "+>" syntax for adding values to parameters e662c86 Fixing #621 -- plugins are now downloaded directly into the $libdir, and autoload looks for them there. You can now easily download any reloadable file to your clients. 7befe1b Changing some of the internals of autoloading so that there is a class-level method to query whether a given file has been loaded. eabe0d1 Fixing #710 -- you can now specify the rails_loglevel d36d0cf Fixing a typo in a log message 8807ac2 Changing "element" to "resource" in the documentation, which just aligns with a terminology change we made almost a year ago. f5f8949 Changing the log message when a resource type cannot be found 773f187 Ignore the pkg directory if it exists, and fix up a couple of tests that were erroring out, which also will help the confinement of package types a bit more. 1bcca31 Fixing #687. 8a7fe9f Applying patch by David Schmitt from #701. 4080077 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. 07f0519 Making sure that #686 is fixed -- I specifically included the Daemon module in the Puppet mongrel server, and I call daemonize on the Puppet class, rather than the Mongrel http server e8217ab Hopefully fixing #685 -- I added a wrapper around the call to getconfig(), so any timeouts will just throw an error and skip the run, rather than failing and killing the daemon. This is not the best approach, since really, each method should be wrapped, but it is sufficient. 60e5e10 Applying further tests to double-quoted hostnames by Valentin Vidic 266d37d Applying patch by DavidS from #697 to allow host names to be double quoted aa74135 Fixing #596 -- classes in modules now autoload d0680c8 Fixing the dpkg querying so that it works for packages that are in "config-files" state rather than just missing entirely. Also fixing logging so that the package version is visible, instead of a dumped object 8b14ef8 Fixing logging of module mounts; it was using the module as the name, rather than the module name 32e5bff Removing extraneous debugging 3ae3a4e Fixing #689, although I have not added unit tests. The problem was that a tag name was being removed, rather than the tag object itself. 19e180f Fixed #680 puppet should handle internet enabled image files correctly. c22e667 Applying patch by daikinee from #690. I was not able to reproduce the problems, but it did not seem to hurt anything, either. 50b8f96 Fixing #704 -- Puppet was not failing correctly when schedules were missing, I think c762c19 Removing the long-obsolete Element base class. The Parameter and Type classes no longer have the same base class. 0ff7827 Fixing #620 - class names and node names now throw an error when they conflict a627f46 Adding a reference to the LDAPNodes wiki page in the ldapnodes config item 0f4de4f Fix trac #684 - set exit code for status properly (patch by abnormaliti) 6b7d3aa Create the right puppet.conf; make sure old config files get preserved and stay functional ac36ddd Fix name of main section ec2d469 Rename puppet.conf to puppetd.conf 8013e96 Get rid of using silly macros in %install dc2a0bf Use single config file 3aafa84 Updating reference docs 55a512c Updating trac location for laeg 044968f Updating build files to support laeg ada4355 Updated to version 0.23.0 d8f4c53 Updated to version 0.23.0 049faf8 Updated to version 0.23.0 8844fca Changing the paths to match laeg, instead of culain. d79a788 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. 944e1f4 More updates to puppet-test 4bb0228 Updating puppet-test with clearer options around describe and retrieve fd15de7 Removing extra debugging from the interpreter 5043ade Updating error message during test failure. e5a9e24 More test fixes. I seem to be getting very close. bd444d8 Refactoring puppet-test -- it now supports specifying a fork level (so you can get multiple tests running in parallel from one host), and tests are modeled explicitly, so it will be easier to add new tests. a57e39d Added documentation for pkgdmg provider in the provider desc accessor as per request in #641 It stil might not be crystal clear, but should be better than the one liner it replaces. 01420ac Adding tracing to prefetch failures, and Fixing the environment support in the cron type (#669). fa39488 The other half of fixing the versionable stuff -- removing "latest" as a requirement. 0b1dbbb Applying patch in #572 by trombik 611e783 Fixing my stupid fix of Matt's work. I conflated :versionable and :upgradeable. I have now added back all of the "has_feature :versionable" lines. 4cb30eb Adding fink package provider. 099bf6c Fixing some failing tests. f96ec6d Updating the has_version work that Matt did -- the only thing he missed was that the :versionable feature depends on the :latest method, and when that is present we can safely assume that a package is versionable. Also, created the :latest method on the dpkg provider, as requested in #647. 3f6c413 Applying patch by trombik to fix #628. eb2326d Applying patch by trombik from #624. 2d07334 Modifying the CA server so that it will not send back a cert whose public key does not match the csr. We have been getting a lot of instances of this, so this should cut down that problem. 6e16d9f Fixing #578 -- Invalid certs are no longer written to disk. bf5d5d5 Fix #657: allow puppet URL's with no server; 'puppet' looks those up on hte local modulepath, whereas 'puppetd' goes to the default server 52f3f83 fixing the appdmg provider to load the package provider base class, and trying to clean up the log-file opening in rails 12adea8 Adding puppetrun as an executable in the gem, along with ralsh (#313). 2ed10d8 updating changelog for #641 bfb3852 Adding appdmg package provider from #641. f05464e Adding the output_file.close, as wyvern recommended 30ebbc9 Applying the patch by wyvern from #662. This should hopefully kill the client hanging problems. ac05442 Updating rrdgraph documentation with a pointer to the new rrd package. 029a191 Reverting the change I just made to the config handler; it was only there for testing. 2b1d478 Adding puppet-test, which is useful for performance testing afc3563 Adding patch by Ghislain from #670, with slight modifications. f6838f5 Fixing #548, and making functions support other rvalues, too, including other functions. f842cef Fixing #643 -- replacing the get_posix_field method with a more degenerate version that works even on broken systems 46252b5 All rails and language tests now pass again. All of the rails tests should now be in the rails/ directory, and I have modified resource translation so that it always converts single-member arrays to singe values, which means the rails collection does not need to worry about it. 6084e1a Fixing #673, but I have not written a test case for it. I moved all rails-related unit tests into the rails/ dir, because they keep getting missed. e8c6cd9 Fixing the yum provider, and fixing the unit tests so the failures people were experiencing will result in failed tests. This fixes #672. 4f7c650 Moving puppetd and puppetmasterd back to bin. Damn. Reverting the fix to #323. 23f986c Move ralsh and filebucket into /usr/bin 659792f Adding ralsh and filebucket to the rpm and the rakefile, and changing the url in the rpm 6be8b21 Modifying the check metaparam so that it just silently ignores metaparams and parameters, since they're uncheckable, and only tries to check properties e039f7b Fixing the type/title index for mysql 9ba878a Removing erroneous debug message b5523ff Trying to load ruby gems, in case needed libraries are installed that way, and fixing a warning in the provider f84ac7d Significantly reworking both external_nodes and ldapnodes support (see changelog). fc9a798 Fixing error about non-puppet protos 45f76c5 Significantly optimizing the database queries -- I am getting about 40% better times now. See http://www.madstop.com/optimizing_the_activerecord_integration.html. e32a1bd adjusting the rrd color stack as requested by thijs 469d999 Updated the CHANGELOG. 51b9fc1 Fixing (hopefully) the last two providers that had "resource.is" calls 77934f4 Fixing sun package provider 270cea8 Fixing #644 -- the install.rb file works again 8003320 Applying metrics patch from #659 by thijs 4910301 Fixing a typo in the docs c67e016 A few small fixes here and there, and the rest of the zones commit 399c37b Fixing #655 -- Solaris zones are again fully functional, from what I can tell 9bc236b Adding indexes for the rails tables 7c53aab Removing the indexes migration, since the indexes are now in the main db schema ef2698c Updating ralsh with more functionality: You can now perform work on the command line, with commands like "sudo ralsh file /etc/passwd ensure=absent". This makes ralsh a bit more interactive. cb5bccc Added to_s to the values to ensure the check versus the database will be consistent and booleans and references will check correctly. d78a7a5 documentation fix 3a2f3d5 Fixing mongrel test so it does not try to load the mongrel server class on machines without mongrel 6aa5d76 Applying patch from #666 by Rainhead and monachus ea190c1 Changed the host to "eager fetch" all the resources and their associated tables. Also removed some unecessary lines from resource.rb that were causng it to re-read information it already loaded from the db. 3003aad Added the teardown of the database back to the tests. 4442a31 Revert unintentional change. 68e37a9 Major rework of the rails feature. Changed the relationship between host and facts (now many-to-many with fact_name through fact_values). Also changed the relationship between resource and params (similarly many-to-many with param_names through param_values). c26f678 Fixing #550 -- I had to list pass and dump as optional fields. f0b5090 Fixing #112 - mounts now default to 0 for dump and pass d396fd5 Fixing #637 -- defined resources can now correctly be virtual or exported ad9c9ce Removing old line from the fix for #660 -- I had strangely just commented it out, rather than removing it ffb7ae0 Fixing #660 by applying patch by freiheit. Looks like this is another problem with yaml on 1.8.1. 79b604d Oops; I forgot to add the base class for package providers. Also, cleaning up the package provider code a touch c826be9 Adding a simple unit test for mongrel, and adding the ability to select the header used to store the client SSL dn. b50c85d Fixing error when commands fail -- the error code is now printed, instead of the inspection of it 3479387 Adding (slightly modified) urpmi support from #592 by Devin 73502a7 Finishing off the type/provider interface work, including adding package prefetch for all packages. The only not-done one is yum -- prefetch is set up for rpm, but not yum. We need to modify prefetching so that it also prefetches latest information, to avoid having to run yum so many times. bf82d51 Fixing the "Server is not a class" problem with mongrel 992636a Applying patches from Valentin Vidic to fix open file discriptor and open port problems 1867d0e Fixing the few test failures that resulted from the changes to provider listing c35d07b 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. a7b057d Adding a "source" attribute to providers, so eventually types will be able to avoid duplication during listing by only listing one provider for each source (e.g., dpkg and aptitude should not both be listed). 0cfd28e this is a spurious commit to test the trac site e8aef1e Change pi to list properties instead of states f2c524d Add protect and priority properties; patch provided by Matt Hyclak 0a2b438 Fix trac #601: wrong location for client pidfile d467e18 Fixing #532 -- reparsing config files no longer throws an exception. The problem only occurred when reparsing a configuration file that was also being managed (which was common) and only whent the manifest was up to date (the combination was uncommon). Reparsing would find the existing file object and use it to check permissions and such, then it would remove all of the internal data in the object, for cleanup; the problem is, the client still had a reference to the object, so when it went to run its configuration, this broken reference was used. e8097a2 Changing --show-available to --showall, as requisted by Jeremy Dreese on the list e0fbd41 Switch the package type to use a :versionable feature, and convert all providers to use the feature. Hope it doesn't break anything. 37a221c Add a grammatically correct 'has_feature' alias, and switch to using it where appropriate in existing code 0a605e8 Clean up a really hairy code construct in the useradd provider 58be1fd Fixing up2date name matching, as mentioned by Jeremy Dreese on the list 464e688 Changing the resource title to be text instead of a string, because some title are > 255 chars 48ec137 Mark all package providers that don't currently report themselves as being versionable as not supporting versioning; this way we get a more sensible error message when people try to specify a package version. See #647 for some discussion. a9ea3c8 Correct a problem with the dpkg provider's handling of the :purged state, and expand the package type's understanding of what purged actually means. Fixes #644 d1458bd Adding a warning for when no properties are specified on services 1883b8f Adding a debug statement describing why restarts are skipped on services ca255b9 fixing the method to check for hostdir writability in the rrdgraph report ac686e8 Changing the location of the classes.txt to the state dir 25d5ebd Adding more detail to the per-host reports dirs, since it was not setting mode or ownership. 62a4d4c Adding better error reporting on unmatched brackets -- you will now get notification of what was expected in most cases 2b372df Updating the exec docs to specify that the timeout is in seconds 4aef0ba Fixing #323 -- puppetd and puppetmasterd are now in sbin; packages still need to be fixed 6f83d4d Fixing #501 -- there is now a splay option, disabled by default and when running under --test 7d1f760 Adding the execute bit to install.rb and fixing #473 -- there was a /win/ regex that matched darwin but was just supposed to match windows 611f88a fixing a documentation bug df6f41a Changing the notify type so that it always uses the loglevel for logging ef1a4af Fixing #568 e8d560e Fixing #566 -- definitions, tags, and classes can now be single characters eed85f4 Adding #629 -- an undef keyword now exists 8410c4d Fixing #507 (behaviour in cycles) by changing the topsort algorithm. 67ee251 Using the method for retrieving the dipper class, in case it has not been loaded e3b7a54 Making sure there is an editor set for ralsh e95734b Redoing autoload a bit in preparation for adding a plugindir dbedcd7 A round of fixes so unit tests pass; most of the failures were from the merging of the transaction-refactor branch 28f7d6c Fixing #569 - I have added a dynamic facts option to choose which facts will be ignored. d9f6f41 Fixing the "is" related problems in yum and rpm support, but there are still some package providers that use the "is" method (grep for "\.is[^_a-zA-Z]" in the package providers), and the util/posix.rb module has a call to obj.is. I will fix those soon. 1934f2b Removing obsolete parsedtype 61784ed Attempting to fix the fact that the yum package provider calls [] on the ensure property, and making the resulting error more readable 85fef63 fixing some problems with the config timeout -- I am not sure it ever actually worked 27cabf2 Fixing a weird bug that occurred because I was changing @parentclass in the AST stuff the first time it was called, from a string to a the actual instance of the parent. This worked fine as long as the parentclass was only called when parsing was complete, such as during evaluation, but if anything resulted in it being called earlier (e.g., attempting to add to the class during parsing), then things behaved, um, badly. This commit fixes the method so that the variable is not modified; there is now @parentclass and @parentobj. 613c413 Fixing a path problem that resulted from the changes I made to internal variable names -- the pathbuilder method in file referred to @resource instead of @parent aed12c3 Use @http in store, add filterhost 19af1cb First try at the REST config_store 12e5656 Initial configuration storage abstraction layer stuff. 426330c Updated the CHANGELOG with changes for retrieve and acts_as_taggable. 24b11b5 Removed acts_as_taggable from the rails stuff. I haven't removed the tables from the schema nor the indexes yet. ca2b9e6 Not parsing old versions of puppet.conf -- otherwise, puppet parses the whole configuration. eca5510 Fixing the to_trans method and ralsh a bit so ralsh now works with the new lack of "is" method 55666a5 correcting some of the function reference docs 1d23013 Fixing #605 -- providers now refer to @resource or @resource_type. de21226 Fixing #607 -- parameters and properties now refer to a @resource rather than a @parent. The @parent parameter is still set for now, for backward compatibility. 3e7d44e Fixing #606 -- now only components mention @children. 13c7f2f Allow Darwin to remount rather than unmount / mount, as per puppet-users discussion "mount type and ensure => present on OS X" (Message-Id: ) 7f8a903 Getting rid of the last vestiges of the logger tests bfc0c35 The TODO file has never really meant anything, and it hasn't been modified in 2.5 years cdd0dd3 Adding default provider info to the providers report 2fa529e Fixing the ability to fail correctly in the fileserver -- a constant was not defined correctly for it fbfaa0f Removed FIXARB's from the pfile stuff. These have been resolved. 93cbe77 Removed FIXARB's from a file that will be going away. a966606 Removed override of change_to_s since it is the same as the overridden method in EnsureProperty. 8bad074 Removed override of change_to_s since it is the same as the overridden method in EnsureProperty. b0374d8 Removed calls to is. 5b44159 Removed the testing method: checknewinsync. c164360 Merging of refactor-transacton to the trunk. This work removes the :is attribute from properties and relies on the provider to cache or return the current value of the property. 8f18746 Hopefully final version of the providers reference c99e99d Intermediate commit of more reference work, including making provider suitable more introspectable. I am about to significantly change the output format of the providers reference, so i want to get this committed before that change. 73df973 The result of .compact.join("\n") isn't assigned to anything. Fix. 568db0b Fixing configprint so it fails correctly when an invalid parameter is provided, rather than throwing a stack trace 40b3834 Sorting the network handlers in the network reference 7835d29 Adding a dynamic? option for references, so those are not stored in trac 1decfa3 Lots of work related to generating more reference. Moving all of the individual references out of puppetdoc and into an external "reference" class, which itself can autoload, so it is now easy to add new types of references. Also adding a network reference, along with an unfinished provider reference. 69cb721 Removing the obsolete logger network interface a040bd4 First run at moving references to lib/puppet instead of puppetdoc f42a755 Adding a module to abstract using Autoload to load and manage instances 53f1612 Fixing the time-cleaning in the rrdgraph report 494675b Fixing #206 and #422. Executables will still look for the deprecated config files and load them using the old parse method, but they now prefer a single configuration file, and files can set parameters (owner, mode, group) in brackets on the same line. 1f8de9d Consolidating all of the configuration parameter declarations into configuration, at least partially just because then the docs for each parameter have to be a bit better. Also, I have gotten rid of the "puppet" section, replacing it with "main", and changed, added, or removed a couple of other sections. In general, we should now prefer more sections, rather than fewer. f783859 Correcting function reference markup e864eab Applying patch to puppetrun docs from JosB e1438a5 adding --summarize option to the changelog 28254b5 Adding a --summarize option, to get a transaction summary 0c07125 Fixing #615 (subclasses with similar names) by getting rid of the class "type" and "fqname", and instead using "classname" everywhere. You should no longer see unqualified class/definition names anywhere. Also, rewriting how snippet tests work, to avoid creating all of the files, since the point was the parsing tests, not functional tests. 8d7ec14 Adding a fact handler, along with an abstract interface for fact stores and a simple yaml fact store, towards the Node Classification work. 79dcd33 Set LANG/LC_ALL/LC_MESSAGES/LANGUAGE to 'C' whenever we execute things, so that the output that comes back is more easily parsed, without needing to understand all sorts of foreign languages a1d4f35 Update to latest shipped for Fedora/RHEL 4022968 Committing all the work that josb did, plus a couple of small changes bf37676 Applying patch to puppetd from Jos Backus 8d11bb8 Fixing class name for Handler in puppetd 1ccdff5 Adding --serve back in as an option to puppetd, and failing when a handler is specified but missing fb4f04d updating changelog with version number 0f02a54 Updated to version 0.22.4 e049999 Updated to version 0.22.4 4f2b903 Updated to version 0.22.4 3e895b5 Changing the remount stuff back to not repeating the mount options. 6438270 Adding a "supports_parameter?" method to test whether a given provider supports the features required by a given parameter. This is used during attribute instance creation, but its creation was necessicated by test code. c9de332 Fixing the fileserver naming tests after the change to allow "-" in fileserver module names. 80ec494 Fixing #430 (I hope) -- execs now autorequire the specified user 483c25e Switching the simpler features to a single file, so it is easier to add new features c369c6a Fixing cron to correctly match blank lines, fixing #602 f69dcda Working a little bit on rails failures, with no real progress e05392e Fixing a bug in the tests introduced a while back when I switched to using "generate_report" 7fb7146 Updating the changelog for #594 3aafd81 Fixing #594 -- Files on the local machine but not remote machine now purge. Note that this introduces a difference in behaviour between recursing locally and recursing remotely. c2bc848 Adding purge => true to downloading of facts and plugins, and removing some extraneous logging from the provider base class 7e97143 Allowing "-" in fileserver module names, #617 4dbcc5d Changing the resource handler to return the whole object, rather than just type and title 8b60d20 Not stripping domain info from the ldap node, as requested 63e907c Switching the mount command to always add the mount options, so that the parsed provider can be used even in cases where /etc/fstab is ignored, like it is on OS X. dad9373 Fixing the tests for the aptrpm provider. 89ac6d7 Adding "rug" package provider from #609 4296e4e I managed to put those provider tests in the wrong file -- the file meant to test the resource type interactions with providers, rather than the provider file. Fixing that, and the failed test resulting from that silly mistake. 21eab22 Okay, one last try -- the Util#binary command was not returning a path in all true cases, and the provider tests were poorly written and missed it. 94bd3b2 Apparently I messed up providers a bit; binaries were not having their full paths returned, which made most providers suddenly unsuitable. This fixes that, and adds tests to verify behaviour. 96eed99 Closing #585 -- providers can now have optional commands, which only differ from normal commands in that they do not affect a provider's suitability 0a46bb2 Fixing #603 -- I had to add a special case for escaped carriage returns. I am not entirely sure this is the right solution, but so be it. 9a1a88c Fixing #574; puppetmasterd now exits with non-zero error code when there is a failure 3169bfa Adding extra info to the "Parameter already set" error, as requested in #573 86c206b Possibly adding the ability to manage passwords on os x. I expect it does not work, since there is probably no way to set up an encrypted password, but at least it now creates a user that can not log in by default. 0aeda97 Adding the ability to manage passwords with the useradd provider 7fbd3ff Adding the ability for parameters to declare that they require a given feature, and resources will not instantiate that parameter if required features are missing. This is mostly useful for properties. 4aaae62 Adding a note to the references indicating that they are autogenerated. 0681cfa Refactoring puppetdoc so it is a bit cleaner and is actually object-oriented. PDF output still fails miserably (there has to be some kind of markup problem, but I have no idea what), but other output now successfully varies on the pages. 0ff3772 Last modifications to rst conversion before bedtime 1d036bb All conversions to RST are done, but I did not quite succeed at making puppetdoc able to generate a single PDF with all of the references in them. 9526e53 Mostly done with the conversion to restructured text, but there are still some tweaks to perform on the typedocs output. 8d3673d Adding a :block_eval option to FileRecords in FileParsing, so ParsedFile providers can have records just define a bunch of methods at once, rather than using lots of hooks. This is cleaner when custom parse and generate methods are used. a478ed2 Translating all of the docs except the type docs to RST 70ec0cc Removing the naming restrictions on cron names 5afa587 Fixing #588 - the parser correctly ignores directories in globbing now 3c5ba06 Fixing #587 -- just defaulting to root when there is no USER set in the environment. e1b0444 Fixing #591 -- puppetd now correctly restarts itself when it receives a HUP 37ffb63 Removing the stubs for nodevar; I did not mean to commit them 7cc3a2f adding note about the class variables in the change log 5436f96 Enhancing the docs a bit for the apple package provider. 775c72b Adding support for aptrpm from #227 as added by Ian Burrell, the rest of the commit be68411 Adding support for aptrpm from #227 as added by Ian Burrell f1f4c42 Adding patch by apowers from #545. df0cd95 Adding init script by apowerrs from #546. 9828b25 Fixing fileserver doc links f8a0e99 Adding the functionality requested in http://mail.madstop.com/pipermail/puppet-users/2007-April/002398.html . You can now retrieve qualified variables by specifying the full class path. 9946249 Only caching the configuration when it has been successfully turned into Puppet objects b6d0d27 Adding a --version argument to puppetca da4d252 Changing the test package for debian b8b14d3 Forgot to change Puppet::Util::SUIDManager#run_and_capture arguments to execute efe9a83 Fix for #565: Final merge of changes from source:branches/execute-refactor into source:trunk Generated with svn merge -r 2378:HEAD https://reductivelabs.com/svn/puppet/branches/execute-refactor trunk 8ab2722 Hah! Finally fixing the problem where mount tests would fail when run as part of the whole suite. The real problem was that I was changing the filetype of the provider without setting it to change back after the test, but the key change that made it straightforward to fix this problem was that my test loader was not exiting with a non-zero code when there was a failure, which mean that the ./test script never thought anything failed. I fixed the former, then fixed the test script to work fine with -n method_name stuff, and quickly found the problem. *whew* f9d89b5 Changing the date that certs are valid to start one day before the cert is created, so clocks that are off by a little bit can still be used. 4615e3a Fixing Client.read_cert so that it automatically adds the certificate information to the driver when the certificate is correctly read. This makes sure the Net::Http instance has the cert all set up. ca5d068 Updating the docs for the sourceselect parameter 295b357 Renaming some methods so that we can generate a report on a transaction and then retrieve it later 1e8e7ee Fixing #567. I overrode the propertychanges method to only return changes if the file exists or if the file has a property that could create the file. 4863012 Enhancing the report docs a bit 0ecb775 Adding last bits to the change log for 0.22.3 a999752 Updated to version 0.22.3 24ad5ab Updated to version 0.22.3 9ce7c79 Updated to version 0.22.3 e154589 Fixing puppetdoc with the recent changes to the networking code 801d0f7 Fixing a bug I apparently introduced in the testing that would have made user management not work with netinfo. In the process, I am enabling validation on the nameservice subclasses. 2544f75 Fixing the documentation to match reality, as reported in #548. 4358e85 Trying to fix the problem that occurs when noop somehow manages to be nil when downloading files 2ad9469 Changing gems to automatically include dependencies 858cb81 Updating changelog and adding filebucket to the exluded file list in the Rakefile d54b645 Updating changelog and adding filebucket to the exluded file list in the Rakefile fa26552 Fixing #562; I had to fix how the client class was loaded 142d0fa Applying patch by Ian Burrell from #559 2c3abbe Refactoring some of the rails code. The speed is now pretty good, but the tagging stuff does not seem to be working and is certainly working very ineffficiently. Blake says he is going to take a look at that. 33f4a66 Renaming pbucket to filebucket 52df47e Finalizing the filebucket client, with test code. def15e3 Adding filebucket client app c5e1a44 Fixing the "readcert" method after getting the signed cert; the method got refactored, and essentially renamed in the process 60d36e2 Moving the authconfig setting to configuration.rb instead of network/authconfig.rb, as mentioned by Koen Vereeken 5bd0e8c Rails is now significantly faster. I refactored all of the queries; they are mostly reduced to three queries, each of which is relatively fast, although there are still a ton of file- and tag-related queries that I cannot find the source of. Note that this speedup requires indexes, which will only get added if you start puppetmasterd with --dbmigrate (although you cannot always start with that, as there is an error in the init code). I expect that the indexes will not help unless you forcibly reindex your database, but after that you should see significant speed improvements. 4c357d8 Adding a migration to create indexes 5ad9bf4 Fixing #553; -M is no longer added when home directories are managed 804c0f4 Fixing the same bug as the Metric stuff, but for logs this time. 46152c1 Fixing the Metric class old clients can still refer to the Puppet::Metric class. 36feb29 Fixing a small bug in testing whether instance methods are already defined. 45904ca Updated to version 0.22.2 0452878 Updated to version 0.22.2 474b86c Hopefully the last batch of commits before I release 0.22.2. Mostly just get tests to pass. 90d8b2d Remove no-lockdir patch. Clean changelog a68a7c2 Change puppet's homedir to /var/lib/puppet 145c39c Don't clobber an explicitly given waitforcert 41e1285 Reverting changeset [2243]; this apparently causes chkconfig not to work bcc937a Absolutely guaranteeing that the provider is always created before anything else. Previously, it could get created later if it were using a default. 60ea7d2 Fixing #432 - you can now manage home dirs with users. You cannot yet purge home directories, because there is still controversy over how that should be done. Also, allowdupe is now handled like a feature, which is, um, better. 3d17685 Adding a "has_feature" method, so a provider can just declare that it has a given feature 290ad14 Finally fixing #504, I think; I even have tests to prove it. It was a little thing, in the end. 32662cb cleaning up an error message a bit 9b5833a Clarifying the errors a bit when nodes come from external sources. 1f8b768 Apply patch from Ian Burrel (trac #479) 3e2510f Adding the "ralsh" executable (formerly known as x2puppet). 531136e Updating the config generation stuff a bit, mostly just cleanup, but also changing the servername fact to be the fqdn of the server. 0153a06 Changing the config cache location to the state dir f046067 Adding context to the warning message about unknown escapes 0040edf Changing execution to reopen stdin to /dev/null b804573 Changing notify to default to its message being its name e2c5dbb Another round of bug-fixes, prompted by test logs from David Schmitt 92bad78 Fixing the spelling of David Schmitt's name and giving credit to Chris McEniry in the changelog. 547fb64 Adding a provider feature table to the provider feature docs 5b2ffbc Adding provider features. Woot! 80dac92 Following Russ Allbery's advice and using the Candidate field in the apt-cache output. Apparently I'm blind. 3606482 Updating changelog for #487 4dc7233 Fixing #487. I know use "apt-cache policy", instead of apt-cache showpkg, because it clearly shows which version will be installed. This is basically impossible to test well, so I just added a test that verifies we always get a value back, although I cannot really test that it is the "right" value. Also, I modified the logging of packages so if there is a latest version, you will get the new version number, along with the old, in the log. 973f9d0 Taking another crack at #504 -- I was using Pidlock incorrectly. I should have been using "locked?" but was using "lock". cef41c2 A slight fix for #507. This should at least provide better information if this problem crops up, although I cannot reproduce it. 1778883 Changing the "found a bug" message to something a bit more informative. 184266d Fixing #447 - filebuckets now use deeply nested paths b436002 Oops. Fixing the other tests to now past the facts to "fresh?", as required by the fact checking. 61b3490 Fixing the fact caching so that facts are only downloaded and retrieved once, rather than once during fresh checking and once during config compile. 5f7ae35 Fixing #519. The facts are now cached in the state file and changes to them force a recompile. a2a9d93 Fixing #544 -- there is now an --ignoreimport option for commit hooks. a212ea7 Adding #539. Definitions can now have titles, and both $title and $name are guaranteed to be set within any definition. 90bdc33 Adding test to make sure ensure does not conflict with any of the creating types. e952029 Adding #541. There is now a "generate" function. 6654661 Fixing #538. There is now a simple file() function to read in file contents. 5ecfd39 Looks like I already accidentally committed the switch from using system() to exec(). I am hoping this will fix the many problems people are having with processes hanging around (e.g., #509). This change just removes the attempts at closing TCPServer instances, which should now be fixed from using exec instead of system. 6b85962 The first round of fixes for failing tests. 8eddd4b More work on #542 -- services in noop now produce noop events so that they can themselves trigger further changes 2fe9998 Removing bogus log message in file parsing 3b8dc6a Removing the cycle checks from the splice! method in pgraph, which *considerably* speeds up splicing of very large graphs. adedab1 Getting rid of a warning in the rpm provider 40eeadb Adding example cron tab from #492 and making the read/write tests ignore whitespace. This cron now parses successfully, as I thought it would with the move to providers. 2a3f56c Fixing #529 -- specified targets keep their values. The problem was that I was using model[:target] instead of model.should(:target) and model.is(:target). The real problem was that my tests were using a parameter for tests but all of the real code uses properties. fe2f0d9 Fixing #533 -- puppetd now exits in onetime mode. 5257837 Fixing #491 -- the client correctly realizes when the cache file is missing and only considers the config to be in sync if that is not the case. a76afb7 Trying to clean up the error message from #490. It looks like the problem is just a failure in one of the types, and it has nothing to do with the state file. 4a6d705 Fixing #542. Transactions now cause services to warn when they would have gotten restarted by a noop resource. Also fixing #549 -- transactions now only refuse to delete required resources if they are being purged. All other resources can be deleted just fine. 8387d48 Fixing #540. I modified Puppet::Network::Client::Master so that it disables noop during its run, so that facts and plugins will always be downloaded. 86c63ce Fixing cron support (I hope). It now uses providers, and seems to work, at least on my os x box. ba23a5a Adding spec libs, so we can use them some day 8ea6ada Clarifying that the ruby RRD support is provided by a binary library df4595e Significantly reworking the internals of the fileparsing code. It now passes around an instance of a FileRecord, rather than just a hash, which I think makes it much easier to understand. b05ae2a Don't blow up when PUPPETLIB isn't set 0fa3c43 Search manifests first within modules, and if no module is found, search in the directory the current manifest is in. 38975de Introduces a new implicit 'modules' fileserver module, whose allow/deny can be set from the fileserver.conf, but whose path is ignored and can therefore not be used directly in puppet:// URL's. ebcb6b6 The template function now tries to first find a template within a module (if the template path looks like it belongs to a module) and only when that fails looks for it in templatedir ba6257c The basic plumbing for modules: a modulepath config parameter, and a new class Puppet::Module that will contain the module-related functionality. The modulepath can be extended by setting the environment variable PUPPETLIB 10d6891 Adding support for a prefetch hook on individual providers, rather than only supporting it for the whole class. 4fa8008 Fixing a few of the log messages so file content changes always include the md5 sum 6ad8998 Adding a bit more testing to the config stuff 3489bd8 One last try at getting the config and mode stuff working. The tests were passing because "mode" is a valid config option in the tests, but not in the real configuration. So, now the Config class correctly only tries to set the meta params if they are valid options, otherwise they get skipped. b36f9c9 Fixing the config path to use Puppet[:name] rather than Puppet.name 6b92c04 Oops, forgot a file in the commit f59cade Fixing a bug related to link recursion that caused link directories to always be considered out of sync. b6df336 Looks like [2265] was not a complete solution -- it resulted in failures when the config set modes via integers. Everything is working now, and tested more completely. 0925fb0 Adding some more testing on the @should values for :groups on users, and fixing a bug that often made :groups think it was out of sync. 333842b Putting the final touches on #144, most of which I had provided in the mongrel work. fa253b5 Fixing #489. I was unnecessarily converting to octal in config.rb 69338da Adding some changelog info for the next release, which is still a ways away, probably. 205bbb9 Flushing out the ability to have a stand-alone CA server, specified using ca_server and ca_port. This is just a final unit test, since the code was done and lutter fixed the rest in [2261]. 185a003 Fixing #531 and #414. This includes pretty much a complete redesign of the AuthStore class, with (hopefully) all of the edge cases removed, the code is now much cleaner, the tests are (I think) complete, and everything is just generally better. fde8b28 Fix typo in default config and add simple test to check default config sanity 7e41d43 Turning a failure into an error when, for some reason, pfiles do not have paths set. 46d344b Merging the webserver_portability branch from version 2182 to version 2258. 6823370 Sync with latest Fedora specfile 4a73da3 Don't include bin/pi in distributed tarball (and hence fix trac #471) 1808c50 Apparently the include function was not failing when it could not find asked-for classes. Now it does. 521606b Allowing trailing commas in selectors 9080686 Committing patch by Dennis Jacobfeuerborn to only use the domain name if it is set. 17c59f8 Adding "ignorecache" option to always force a recompile of the configuration ebc4dd2 Fixing #464 and #515. ff9ec47 Applying patch in #528 by ask. cc26026 Fixing #467. It is a hackish solution, because I just reordered the definition of the params, but it works for now, anyway. d229d49 Fixing at least part of #514. This does not get rid of all errors, but at least it fixes the spurious warning be8dfd9 Fixing a problem with the splice! method on the graphing. The problem was another issue with hash ordering, where it would usually work but sometimes start failing. The solution was to splice the containers in topological-sort order. 4df0738 Applying a modified form of the patch by cstorey from #523. The modifications were mostly around the fact that Strscan does not set $1 and its ilk. 0f16bf3 Fixing #526. Implemented as a period of "never", rather than adding a new parameter. 07fce23 Fixing #477. setvar() can now accept the file and line info from callers. d5444e0 Fixing #199 and moving service tests (which are completely atrocious) around. 7d965ae Applying patch by cstorey from #521 36ae6a2 Making the package provider tests able to be executed separately, and using "clear" instead of resetting @objects in the types. b7a0fb4 Make up2date the default for RHEL <= 4, and confine it to RHEL; make yum the default for RHEL >= 5. Fixes trac #478 672e281 Fixing #142. As expected, trivial. 87f100a Applying patch by DavidS from #522, along with test code and a small bit of code cleanup. a3f3674 Redoing some aspects of the graphing in hopes of helping hte performance a bit. 1a7d8b6 Fixing file backup defaults to correctly use the puppet filebucket by default. d833c3e Changing the log messages for source and content properties to mention the md5 sum of the new content 789b786 More code related to #517. Oops. 4c885b7 Fixing #517 and more. Classes now support more than one namespace in their search path, parent classes automatically have their namespaces added to subclass namespaces, and (huzzah) there is a "search" function that can be used to add new namespaces into their search path. aad5123 Fixing #524. Functions, both statements and rvalues, now support no arguments as long as you use parens. 3e13e36 Actually commit the changes to lib/puppet that were supposed to be part of [2223] (Fuck svn and it's partial-repository-by-default behaviour) 1d711dc Partially complete #241. Add a 'purged' value for Package.ensure, and add a handler for all of the Debian providers. Also wrote sensible test cases, and so we've now got Mocha running around in our source tree. a752eb2 Fix #516, 'Cached manifests get unescaped twice' db8a23e Print stacktrace in debug mode when catchign a signal - useful for understanding client hangs fa02d67 Fixing #472. Apparently this has been broken since I did the parser redesign. I had to fix the scope trees so that subclass scopes are subscopes of the parent scopes, which used to be the case but was far more complicated. d145aae Fixing #505, #508, and #513. 774415b Allow 'key=' to be the only thing on a line (livna uses this) 4d02823 I believe this fixes the issues in ticket #469 My testing on mysql shows connections being reaped. 7a9e28a Applying patch from #495. 6fbd5fd Removing extraneous debugging 90b1058 Fixing a problem in collecting exported resources. Virtual resources worked fine, but exported resources resulted in an essentially infinite loop. deab3a0 Fixing the default dbadapter back to sqlite3 3d093ae Applying patch from #510 by curzonj. Note that the right solution to this problem is to use the ruby API, but it does not appear to be stable yet. 65599af Re-add the files beb7873 Undo the param_name param_value merge f4f555d Renamed Puppet.name to Puppet.execname so rails 1.2 doesn't freak out 9a672ec Undo the param_names param_values changes 8b18fdf Undo the params & facts stuff 9fe8905 Changing date to datetime in the database 964c805 Trying to fix problem of locks lying around 2418e4a Adding hook to update timestamp when a report is run 0aa3b66 Change Puppet.name to Puppet.execname so rails 1.2 won't freak out. 6555dad Update relationships Remove dynamic class generation for now. Include the fact class 328e576 Revamping collections to get what is hopefully more reasonable behaviour when they are used in combination with defined resource types. You should now be able to combine them in just about any way and get "correct" behaviour, which in this case means that you can have virtual definitions or definitions wrapping virtual resources and the resources will still all get realized. 69d4bfe This works for me. Probably not the most universal fix. 9de665c Apparently using "gem" requires an environment we don't have. 91991f1 Merge fact_names & fact_values, and param_names & param_values. 0b5600a Fixing features to use the new feature location 17a5f4c Applying patch from #502 by Jose 6216ae5 Applying patch from #497 by Jose Gonzalez 258651c Applying patch by Jeff McCune from #496 9cd2636 Applying patch from #474 by David Schmitt. 4effff4 Fixing #482. c3b6232 Fixing #493. 81ae397 Applying doc patch from #494. 1756bec Fixing #484. Moving unit tests at the same time. a216df2 Okay, last file moves for the night. The test code has been moved to match the lib directory, and I have moved a couple of things into network/ instead of network/server, since they did not belong as much. 7e07e3d Moving all of the client and server code into a single network/ directory. In other words, more code structure cleanup. 6d8068e Moving some of the stand-alone classes into the util/ subdirectory, to clean up the top-level namespace a bit. This is a lot of file modifications, but most of them just change class names and file paths. 1626023 Adding a libdir setting for puppet, so you can store your modifications to puppet in a separate directory. This probably will still be somewhat limited because it will always depend somewhat on load order. For instance, if you add a new provider, it might not be available when you expect, since providers are all loaded as soon as a type is loaded, which might happen before the libdir is set. It should always work fine if you do not override it, but if you do override it, things might behave a bit strange. 99c8a54 Adding a parameter to allow you to override how an exec refreshes itself. dd71a51 Changing exec so that the checks apply to whether an exec is refreshed. 31c9a6f Disabling the netinfo mount provider ad359f3 Reorganizing some of the tests. d403131 Merging the state-rename branch. This includes the diff from version 2156 to 2168. All states should now be properties, with backward compatibility for the types that restricted themselves to the methods. f6f72f2 fixing the cookbook link fix 3a024d7 Removing the default value for :ensure on mounts. 71346e9 changing the cookbook link f80bd5e Fixing exec so it actually works when path is specified as an array d117aa8 Updated to version 0.22.1 1e90209 Updated to version 0.22.1 463d3a8 Updated to version 0.22.1 1d059b0 Fixing #470, I think. I basically just threw away the validation and let suidmanager do it all when running commands. 69a07b1 The resolve functionality in "test" is almost working... 42d15fe Adding note about removing mounts netinfo provider 2c79bbf Oops, that last commit seems to have broken the rakefile. Works again. 3836201 Trying to get the functionality I had in previous tests. Mostly I want to be able to load many files but run just a couple of methods, which test/unit supports via -n, selectively enabling -d. I now can do this with the Rakefile, using 'TESTOPTS="-n -d" rake , although I find that a bit kludgy (certainly more so than 'rake -n -d '). fd2982f Fixing executable tests to take new rundir into account a62fd3e Filenames for test must _end_ with '.rb' 12bf816 Fix to make running tests work in ruby 1.8.5 18eebaf Fixing selector tests to get rid of a lame hash ordering bug in the tests. a3a85d8 fixing rails test to take into account the fact that resources now do not always return arrays f1deaa8 Fixing autogen so it passes on non-Darwin systems. 173f5cc Fixing a purging bug introduced by [2138]. I had to move the purge check to the recurse method, rather than the localrecurse method, because the purge check needs to happen after sourcerecurse. 62ab873 Deleting the file even if a source is specified, as mentioned by Robert Nickel. c8f38b7 Renaming "pelement" to "resource". The old name is a holdover from before we had settled on "resource" as a term. ea73cdb Fixing Files to work with the Resource server. Basically I just remove the "target" value if it is a nullop, so that it does not cause a conflict with "contents" on the far side. d7fde42 Adding explicit umasks to these tests. a4de59c Fixing rundir so that it is only set to be in /var if the process is named puppetd or puppetmasterd. Otherwise the unit tests set it to /var/run/puppet, which has the chance to cause hangs. 9dc6cf6 Removing all remnants of the old symlink type 7889b0b Revert 2125, and instead change the way the 'latest' version is selected from the sorted list of versions 97583b4 Updating changelog for 0.22.1, although I am not quite ready to commit yet. 8d90e56 Puppet can now read files on stdin. This means you can put "#!/usr/bin/env puppet" in the first line of a puppet manifest and then execute the manifest normally. Yay! 8821300 Providing a partial fix for #428. Resources can now be deleted as long as all of their dependencies are also being deleted. This, combined with the fix to #433, means that you can now explicitly specify the order of deletion, and everything will work as long as all required objects are being removed, too. 0a62369 Partially fixing #460, take 3 -- fully-qualified classes can now be included. 788a74e Partially fixing #460, take 2 -- fully-qualified definitions can now be used. a7bd786 Partially fixing #460 -- fully-qualified class names can be used as parent classes. c9e7699 Fixing #462. The package sort order was always resulting in the lowest-version package being first, rather than highest, so I inverted the sort order. add6e5d Applying patch in #465. 1f9ede2 fixing #427. Facts now timeout, both in loading and downloading 049d79c splitting the tagmail report into multiple methods and adding test code 727672f Not creating the listening server at all if --onetime is enabled. af3863e Fixing #440, albeit with a slightly hackish fix. 01ec5ba Moving code from external sources into an external/ directory 1374e4e Moving the switch that disables the certificate authority into the main library, so they can be disabled in the configuration file. dc580cf Fixing #433. I basically just added checks to all the places where I add edges, to make sure automatic relationships lose out to explicit ones. e418691 Fixing the warning message related to namespaceauth.conf d3fc49d Fixing a problem that occurs when puppetd starts with an up-to-date configuration -- the default schedules and filebucket were not being created. 6c61f0c Fixing #463. I redid all the autogen stuff so it can handle autogenerating string values for stupid os x. 8198e7e pointing documentation to the wiki now 54c458c Fixing #438. 04017b3 Fixing #444. I was losing the list of sources when creating new children. b7560d5 A couple small bug-fixes 3aff4a0 Doing more work on #113. Mostly, just making sure remounts do not happen spuriously very often. They will still have extra remounts when changing the value of "ensure", but that is not currently avoidable (similar to #199). 1cc8ecb Fixing info around newtype options dd502db Fixing #113. I added support in the transaction for self-refreshing, which just creates a special trigger for resources that have self-refreshing enabled. Logging is a bit different for them, so it is clear why they are refreshing. I still need to verify the remount methods work in the providers. bf46e7d Adding a "self_refresh" option, so resources can refresh themselves if they have changed in the current transaction. 1f41c35 Fixing #454. c07494f Fixing #441. 9924244 Fixing #431. Collection was always returning an array, even when only a single value was passed. 0a9c8da Changing how transactions check whether a resource is being deleted. This is a small step towards fixing #428. f6a3d94 Fixing #455. A simple fix, fortunately. 81025d1 hoo e29ef5c Updating reference docs af4f7d7 Fixing documentation references to refer to the wiki 37acfb9 Fixing #442. You can now do: defined(File[...]) to see if a resource is defined. 2db6878 Fixing #434. 6475487 Fixing #423. Configurations now default to timing out after 30 seconds (tunable with configtimeout). This only happens for remote configurations, not local configs compiled using puppet. a081d41 Fixing #418. The problem was that multiple objects might include Daemon, which means that Daemon#shutdown can be called multiple times. c33d5e4 Using Time instead of Time.to_i for compile time, because some versions of ruby have trouble converting Bignum to yaml 6d791f5 adding client name to processing line 7670df2 Fixing #445. Nodes can now inherit from default. afe77b6 changing selector error message aab3214 reworking the selector case-insensitivity test 9f8a3b1 Removing an extraneous debug message, and fixing the case where the server compile fails in --test mode -- it resulted in an extra warning message. ca1e36b Applying patch from #457, as submitted by Jeff McCune. 81ae09e making yum the default packager for centos 5735d48 Wrapping the resource generation methods in begin/rescue blocks so that failures cannot kill the transaction. e8f3806 Fixing error-catching in resources.rb a3041cd Updating changelog for 0.22, which fixes #429. bda74bc Fixing #415. Configuration parsing now removes trailing whitespace. f8115a7 Fixing #424. The configuration compile time is now cached in the yaml cache file. 53f3b8c Fixing rundir so that it does not throw an error when not running as root c285d7a Fixing #437. Transactions now check whether graphs are cyclic, with a somewhat-useful error message if they are. 9720a97 Fixing #436. Also finally renamed pfile/uid.rb to match the state name. db5494f Fixing #421 by changing the rundir to /var/puppet/run. bfb5506 Fixing #416. There is now an option (downcasefacts) that determines whether facts are downcased by the client. 53c3f5a Make rpm operations much faster by suppressing unneeded verification bcd81bc updating docs with new location for reference info f069418 Moving the reference docs to the top level c03a8c9 updates 42b78a0 Use a specific ActiveRecord subclass to check for the proper existence of AR in the Rails feature test, so that we don't kill everything if the machine has Rails installed, but it's an old version that doesn't support polymorphic associations e64e64d Make the version string optional in the dpkg-query output parsing regex (Fixes: #425) 32bbb3a Clear existing yumrepo instances befoer listing - assumes list should only return "is" instances 3dea961 Enclose values in single, not double quotes; otherwise if values have $ in them, the manifest will be incorrect 5836c23 Allow listing of yumrepos a676e08 Sync with latest in Fedora repo 965a82d Minor cleanup, leave cursor at beginning of indented line, not its end c35b441 Add indentation written by Mario Martelli f7d8350 Updating docs for 0.22.0 4ee6c97 Updated to version 0.22.0 98ed0ae Updated to version 0.22.0 38cfa67 Updated to version 0.22.0 3446dd6 Last round of fixes before the next release 954a285 Fixing puppet test task for older ruby versions 7afa69c Fixing rake test so it works with the new puppet loader 54c387f Adding #408. d0ecc0e Messing around a bit with how tests work 2728f50 Adding a bit more comments to the :template function 704bd76 Fixing a few testing bugs that have crept in, and fixing a self-reference problem when configuring, graphing, and setting graphdir manually. e756711 Fixing #411. 48bbd0b Further work on #407. I forgot to actually connect it to the interpreter internals. f6beef5 Fixing #407. You can use external_node to specify a command to retrieve your node information. f8f7c57 Don't rely on the type to store the actual NVR of the package; breaks in the provider tests since they call the provider slightly differntly 42c13e2 Adding a timeout to execs. This is not really a sufficient solution, since it needs to be added throughout the system, but this is a good start. The default timeout is 5 minutes. 6e10004 Using Puppet.settraps in puppet executable, instead of old ad-hoc code. 903b40b Applying patch by mccune from #409. 5e470b3 Regressing to always creating files/directories as root, rather than trying to do it as the right user. bb72a08 Throwing warnings instead of exceptions when dpkg-query produces info we cannot understand 08a56cb Fixing module_puppet to use the usage? feature. 239727c Re-enabling the dirchmod test and fixing its syntax 2195b76 Trying to fix #364. Somewhat used the patch provided by nslm. 8fd9765 fixing filebuckets so that only the client bucket is created on clients d5651f8 Fixing tests so they now include descriptions with all config options, which is now required. a454dfb Creating two filebuckets by default, one for the client and one for the server 9c1a446 Fixing #403. 2b271f8 #398 is already fixed, but this will fix things so it cannot happen again f4b2e13 Fixing #391. Keeping track of times of compile and freshness checks. 098081d Setting up specific allowed types for sshkey 5292e4e Handle continuation lines in inifiles properly; stick a little closer to how python's ConfigParser parses 2366c95 Explicitly require puppet/filetype; otherwise, tests for this module fail cbd20f0 Simple script to produce type info 3b6bf05 Fix yum update breakage - query should not change the name the user gave us; instead, the fully versioned pacakge name is now stored in the instance parameter 587deea Tone down the debug spewage from yum 9115fef Adding extra connection statements and enabling concurrency support in rails, hopefully fixing #399. 0ef8971 Fixing #394. LoadedFile was not checking to see if files went missing. f58bda2 The package name must match at the beginning of a line; otherwise we might get fooled by other yum spewage caabe9b Not saving tags right away. This seems to cause postgres to explode. 9271142 Adding a check to the rakefile to throw a warning if the test task is missing 651640c Fixing #401. Transactions were trying to trigger every resource, even those that did not respond to the specified callback. c140037 Not setting the graphdir to the puppet user, since it is only used by puppetd 127f0df Using text for parameter values, instead of string, so the fields support larger amounts of text 86e434e Adding postgres as a dbadapter option 2d25816 Fix trac #354, and some other oddities around installing multiple versions of the same package. 50965c7 Changing "sourcematch" to "sourceselect" 373f177 Adding sourcematch parameter to file. cc05e8d Fixing the error thrown when a dependency cannot be retrieved, WRT to #395. 57d0933 Fixing #396. Using the provider command instead of a direct exec, which automatically captures stderr. 0887fcd Modifying the "Resource#set" method to simplifying adding new parameters 56619d5 A couple of small fixes to pass existing tests. 4482691 Fixing some failing tests on fedora. 3e933cc Enabling debugging except when running under rake. 3b2521b Fixing graphing tests, and correctly only using storeconfigs in tests where rails is available 54a838e Fixing #369. I was not flushing changes to disk when ensure was out of sync. This is going to become a common problem, and should probably be addressed by the framework rather than by individual types, but for now, it works. b8f798f Fixing #390. You can now add --graph to produce dot files, and you can then produce pngs or whatever from those. 0a1dd1a Use Puppet::Util.sync instead of MonitorMixin to ensure that only one thread runs the executor at once 23b75e2 Fix a syntax error in lib/puppet/daemon.rb (That'll teach me to not run the test suite before committing) 38244fb Create rundir in a test that needs it 27c1b49 Switch puppet/daemon.rb to use Pidlock 16f7980 Switch the run-lock to use Pidlock instead of the ad-hoc code e4843f1 Make Pidlock#lock return true if we currently hold the lock e252505 Add a Puppet::Util::Pidlock class, for use by locks and PID files c1035cc Add system library directories directly in puppettest.rb, so you don't have to do it by hand in every single underlying directory; also change the debug check slightly so that we actually put debug stuff only when we really want it a333539 Applying patch by rainhead from #392. 7e62bb0 Add updated_at for all tables make sure it's removed from the resource hash that gets returned 6d8b3f3 Removing debugging da3e9d4 Hopefully fixing tagging problem 280f0b4 Still trying to track down the tagging problem 79e9b46 adding a bit better error reporting when tags are bad 419cdf0 exiting sleeper after no more than two minutes fa538bc Moving the tagging stuff to an "external" directory, instead of "lib". 0166004 Adding carriage returns to output of puppetrun f52e2d0 Trying to clean up how rails is loaded 3a313ad Supporting arrays for the backup parameter 17306c0 Features now load dynamically using method_missing, so that undefined features never throw errors. 96f91f6 Adding a bit more testing to mounts, and pulling a bit of the transaction into a separate method to shorten the apply() method. a2b0ee6 Finally writing unit tests for Transaction#trigger, and drastically simplifying the method in the process. 9ff80c0 *whew* Okay, simplified the splice method a bit, and I am actually somewhat confident that the stronger testing is correct. I have had a lot of problems with tests usually passing but sometimes failing, mostly because of ordering problems related to multiple edges. bb9c813 Did a short-cut on the graphing, since we currently only support one type of subscription. This solution still will not scale to all that many edges, but it works, although it will fail if we need to support different types of subcriptions. 7ae62a5 A couple of small bug fixes c4c3d77 Some tweaks to graph splicing, although I do not think it will be enough to handle some of the edge cases. d07570b Looks like providers work again on Solaris. 6529822 I have not yet finished testing, but most of the providers now successfully pass arrays to execute() instead of strings, which means that the vast majority of execution problems are now gone. I will finish testing tomorrow, hopefully, and will also hopefully be able to verify that the execution-related bugs are fixed. 038d6a6 Fixing #387, hopefully. a5cf056 Fixing #388. Paths now look a lot cleaner. 883c64a A couple of small bug-fixes 65e76e8 Fixing #353. It was as simple as exiting with a different error code depending on the results of the call. d3a7c28 Fixing #386. 1d05739 Switching files to use a filebucket named "puppet" by default. Also, set up MasterClient to create that default filebucket. 92ff712 Fixing #365. I am not sure what the problem was in previous versions, because the new graphing stuff changed the solution to this problem, but it all works now. 8ff7e0c Closing #362. Case-insensitivity is handled by downcasing all host names. f1dc103 Hopefully fixing #355. I could not actually reproduce the specific problem, but I found a couple of issues around the problem and they are all gone now. a3ce917 Fixing #348. Overrides now support an extra end-comma. 5e58273 Loading the rails lib early on, so that the rails configuration parameters are accepted on the CLI, as noted in #357. 2742995 Fixing #343. Collections and definition evaluation both now happen on every iterative evaluation, with collections being evaluated first. This way collections can find resources that either are inside defined types or are the types themselves. 85b19c4 Fixing #349. Doing some hackery so defined types can now (again) be used as dependencies. 311aba9 Fixing #66. The "defined" function previously checked for definitions and types, but since types and classes can't have the same name anyway, the function now works for classes. 9bb5c50 Not downcasing facts any longer, closing #210 (although not using the patch from mpalmer, since I had not noticed the patch was there). Also, making all nodes, classes, and definitions case insensitive, closing #344. Finally, I added case insensitivity to the language in general, which should preserve backwards compatibility and probably makes the most sense in the long run anyway. be711d3 Allow execution of bare strings as long as there's no attempt to change uid/gid c616572 Fixing test to work with new style of graphing. 2c2177c *whew* Fixing the vast majority of the graph-related performance problems. I found a simple solution to handling events produced by generated resources, and that basically fixed all of the performance problems. Transaction tests still fail, but I wanted to get the fix in now so I do not forget it. 299bdc1 Fixing #380. The problem was that a method was sometimes returning :absent when I expected it to return nil when the group was not found. All fixed, yay. e605c4a Adding some defaults to users, mostly for darwin because it is kinda stupid when it comes to this info 41562cc Adding test for the fix to #361 a9dd641 Fixing #361, I think. It appears to be a problem with missing a setting for realname. 1bdf379 Applying patch from #384, by jgonzalez 253376a Fixing #385. Puppetca correctly exits with non-zero exit code if there are no certs to sign. 36e8d65 Fixing #372 and #374. All is not perfect, since OS X still cannot set UID, but it is much better. There is still plenty of bug-fixing to do on other platforms, I expect. 115ec09 Re-add support for tags and file/lines f851ca6 Adding :replace aliases, as requested in #366. 9f48706 All rails *and* language tests now pass, with the exception of a language/resource test that passes by itself but fails when run as part of the whole suite. Also, I added deletion where appropriate, so that unspecified resources, parameters, and facts are now deleted, as one would expect. dc5f4dc Fixing most of the rails stuff. I think everything basically works now, and now I am just going through and making sure things get deleted when they are supposed (i.e., you remove a resource and it gets deleted from the host's config). 5a52855 Fix up a problem with initialising an sqlite3 data store, presumably only with older versions of Rails 3c93400 Adding daily snapshot tasks, altho they only work at my site 10dbb17 Some more graph optimizations; I think I am now close enough that I am basically just going to spend a bit more time making sure the modeling is right in the transactions, and then walk away for now. b01ffe6 Adding a simpler and *much* faster tree_from_vertex method, and using it instead of the default one a481f9b Requiring puppet/rails in the interpreter before Rails.init 9df9e4b Getting rid of the db init stdout and reindenting 02cfc44 Simplifying the splitpath method a bit, altho it is still strangely slow 9fa7794 Simplifying the the Puppet::Type[] method 0dbe96d Redoing the benchmarking a little bit f622e18 Go back to restype and remove STI classes, they were more trouble than they were worth. 9ad62d2 Modifying rails test 0dac4ec Changing some of the error output. This gets rid of the duplicated information that occurred when definitions or nodes were duplicately defined, and it tightens the error output a bit. 0f78282 Adding unit test for #364. It passes on OS X. dc96f98 Adding some selectability to host creation for testing. Using find_or_create_by results in lots of saves instead of one big one at the end, which causes initial saving to be much slower. To switch between them, just modify the value of "create" at the top of Host.store. 6b5d001 I like to checkin one-liners a lot. I'm cool. set the type when we create the object, might be a bit faster 7173c1f Don't use find_or_create_by_title since titles aren't unique. e28c604 Remove old files, don't require pp anymore 3d070f7 These are the same versions from changeset 1837 0cd5799 Some rails modifications 6d9ae0c Don't dump out debugging stuff. 342a4a6 Don't symbolize the param names 56098ca Rename some stuff I missed when it was reverted b5fd822 acts_as_taggable plugin 98ebb87 Moving the mount provider tests into a subdir, and fixing the basedir calculation in tests so it does not matter where the test is called from 8714e14 New rails stuff redux. 026ec4f Small changes to the test rakefile. This rakefile still is not completely satisfactory, as I cannot use it to load all libs but only run on test method, which is often important when trying to track down a bug that only occurs when multiple files are loaded. 8be0d33 Fix service.list, in particular for the redhat provider 29ded01 Fixing painfully difficult to find bug in defining exported resources 72f8b32 Reworking the package tests. Now providers are tested individually f5e7915 Rewriting the test rakefile so test directories can be more than one directory deep. This will be particularly useful for providers. 40c0905 Fix up the filelist for gems, so that all of lib/puppet gets put into the gem, not just the top-level .rb files c35988a Another round of bug fixing. Now everything passes except mounts, at least on OS X. ab60452 Fixing the next round of bugs, mostly little things but I had to modify transactions so they are willing to delete implicit resources even if they have dependencies, else we would often not be able to purge files at all. 3937aa3 Never default to rpm provider; use up2date on RedHat. This also works on RHEL5, which does not have up2date, the provider properly fails over to using yum c763346 You can now use the "resources" type to purge resources. I still need to modify transactions so they do not purge resources that have relationships. Also, apparently the noop metaparam was never actually working; now it is, and there is a test for it. 64d96e9 adding a note about facter to the faq e96049f Change the filelist slightly so that the externally-included lib/rake doesn't end up in the gem 8829aa7 Adding a metatype to manage resources of a specified type. For now, this metatype just supports purging unmanaged resources. Also, fixed a few tests here and there 4abbdc1 Working some on the export/collect problem. It actually works now, but there are not yet sufficient tests for it, so I will leave the bug open until we have got the new work in place. I also added a "rails" feature, so I do not have to keep testing whether ActiveRecord is defined. 8fee538 Adding a short note on variable interpolation 8accd80 Adding a bit of clarity about file locations dd1c4b9 Add a task to build Debian packages more-or-less 'directly' out of SVN. 01fecb1 Add external Rake task file as an svn:external, and modify the Rakefile to add lib to the load path b3eb9f1 All tests should now pass, with the possible exception of some tests that might fail when all tests are run in one process. e287d1e Almost all tests now pass. I have basically reached the point where I was before I integrated graphing, except that all of the relationship handling is now inside the transaction, and any kind of recursion (including file) is *tons* easier to model and manage. 37a059b Most tests now pass in the whole system, but there are still about 8 cases that do not work. I am working on those now. 374c830 Removing the explicit load for most types in type.rb 8aebdfc Removing the reference to the symlink type 2d43580 Most of the graph handling is now done, and all of the recursive types (basically just file, tidy, and component) now correctly use the generation mechanisms in the transactions, instead of sticking them in their @children array. Now I just need to go through the rest of the tests and make sure everything passes. d3b76d6 Removing the symlink type finally. cdd1e6e Another intermediate commit. Most of the graphing work itself is now done, but I am in the middle of converting files to use the graphs and at the same time am writing some actually decent tests for the file recursion stuff. 01e5b69 adding note about --no-client 8ff90ef Fix bug in example code: all resources now must have names f3a0c48 Most of the graphing work is now done. I have also added the generator work in transactions, but I need to migrate files to using it. Until that migration is done, files will not work correctly for many cases. a7354d0 Fixing link to fsconfig reference ccd7b58 Intermediate commit -- I am ready to start pushing the graph stuff into the types and transactions, which will break everything for a little while. c301b1b Make spec file work for Fedora < 5 and RHEL < 5 3e3f70e Adding GRATR and the beginnings of graph integration. 34c89b0 fixing typo b3c3de2 Fixing #342. Classes needed to have their namespaces set to their fully qualified names, so that contained code and definitions looked for definitions starting with that fq name. 4076101 Fixing mount tests after fixing the backward compatibility 185ba8c Fixing backwards compatibility in mounts -- they were not correctly copying the path over to the name 5f5417a Fixing #347 (I hope). Doing a provisional require of rubygems. c369e40 Fixing #346 -- on some scripts I accidentally used "feature" instead of "features" c3c5851 Fixing configuration storage -- there was a check being done that caused false values to get converted to nil values, which failed in the database 25d563b updating syntax matcher to highlight dollar signs in prototypes 60af8e2 Updated to version 0.20.1 e313a26 Updated to version 0.20.1 68d9e78 Updated to version 0.20.1 7d46167 Updating changelog for 0.20.1 7fa96cb Another small fix, just for solaris db5d9d4 Another testing fix 0efa969 Fixing more tests 5d2f954 Fixes to the test system, and a couple of small fixes to the main code. Also, disabled the "port" type, because I cannot seem to model it correctly. f8b9e86 Fixing a small syntax error in the port provider 35de0e3 Temporarily reverting all of the recent rails work so that I can release 0.20.1 26b32b9 adding svn keyword to notify type and reindenting e936fcc Removing the caveat so that the log message is always provided during a run. b2e98b1 Fixing #339 for real this time -- fixing the log message 1bf97cd Fixing #339, and the bigger problem it concealed. Metaparams are now only added to resources that do not explicitly set them. 50d28ef Applying patch from #335 0a35c34 Removing some debugging, and trying to track down a bug where symlinks get recreated for now reason ff06a8d Ported sshkey over, yay. 4e96031 Adding a NetInfo provider for hosts. Yay! 064ddbc Hosts now work again, and it should be straightforward to create a netinfo provider, too. bb80c1b Ports are still broken, but I need to work on something else while I am thinking about how to fix them. Stupid /etc/services. 25b575f adding a comment to namespaceauth.conf b2a49c9 adding up-to-date example configs 4d5f70f Trying to get a netinfo provider for mounts working, but i give up. I am leaving it in place but marked as highly experimental. bd169b4 Mounts work again, at least with the parsedfile provider. I still need to create a netinfo provider, but it should be short and easy. And, painfully, I still need to port the other six or so subclasses to this new provider. 138150d Doing some refactoring in how state values are set. The primary motivation was to provide the ability for the "newvalue" method to specify whether the provider should still be called, and if so, in what order (e.g., before or after). 5685013 Fixing the state class so that blocks are optional for values. This is useful for cases where you want to specify values for validation but you want a method called on the provider instead. 0643113 An intermediate commit. All of the classes that use parsedfile are assuredly broken, since I have basically completely rewritten it. These classes have been a thorn in my side almost since I created them, yet they have been significantly less functional that I wanted. So, I decided to do the rewrite I have been putting off, just to spend all of the maintenance time now so I do not spend 3 days on them every release. 7c8614b Adding module for parsing files. This module is only included into the parsedfile provider base class, but it is cleaner to have it broken out like this. f9d6213 Fix silly regexp mistake where lines with values containing '=' were parsed improperly. b44ebe2 Fixing some warnings 9f8849e Mostly small changes toward 0.20.1 10634d6 Fixing #324. Mkusers was not specifically ignoring the root user, and it is now. 349e2aa Updating docs with correct links for the doc restructuring, as mentioned in #322. 5a4f807 Fixing #326 -- parseonly now just creates a simple Master without opening a port cbb4578 fixing #327; debian packages now correctly register their "latest" status 87fd075 Adding a simple report that just duplicates client logs onto the server 10c860f Slightly more doc updates 114cd8a More doc updates -- I moved the doc headers into separate files, rather than having them in the code b14982a Small fixes here and there. The rails changes needs to be pushed through the collection code, so I am committing so Blake can take a look at that. aa2da58 Updating docs f438bab Refactoring the doc generator a big 1f548f9 Updating documentation 87aea8b Fixing rrdgraph report (as marked in #334); also, expanding the docs on all of the existing reports. 51882d9 The new rails files. cf166c2 Rails stuff part 1 28c283c Fixing some sticky problems with checksums when just using the "check" metaparam. 744ded3 Merging the code over from the oscar branch. I will now be doing all development in the trunk again, except for larger changes, which will still get their own branch. This is a merge of the changes from revision 1826 to revision 1834. dc4d980 Syncing up with FE repo specfile (only mandatory changelog entries) 033de88 Making some documentation changes e741b7b Fixing some Class.to_s handling 0930b5e Mostly rewrote intro doc 71924ad Updated to version 0.20.0 a488dd9 Updated to version 0.20.0 4688d93 Updated to version 0.20.0 f9f939e Updating changelog for 0.20 e3b4f23 Another round of bugfixing, including finding out that the tagmail report was leaving zombie processes lying around 07f616b A round of bug-fixing on OS X ed38ba4 Doing some work on the DSL. It behaves a little more like the real language now, although overrides use the same syntax as normal resources, and there is no facility for specifying defaults 7b34e25 Another round of bug-fixes in preparation for 0.20.0 ead49c6 Applying patch from #318. 7261cbb Applying patch from #319. 3a6683e Changing the realize() function to be just syntactic sugar for a collection -- it literally creates a collector object now. The benefit of this is that it is late-binding, so file order does not affect whether a resource is available. 05080ff adding docs for virtual resources a05b8f5 Adding a "realize" function that can be used to make one or more resource non-virtual. It is just syntactic sugar for a collection by title. 8a4bf1b Hacking cron so that it works even though I have changed ParsedType. The whole stupid thing needs to be rewritten from scratch, but this is the best i can do for 0.20. d77d6d4 Adding prefetch of providers to transactions. Nothing is using it yet. I wrote it for cron jobs, but it is too much work to fix this for cron jobs right now. f1ebef0 Fixing virtual object collection. I apparently broke it when I added rails collection back, and I never created any end-to-end tests. 52105c6 Fixing doc generation for objects w/out their own docs 1d35f28 Fixing a bug that only occurred if a defined resource was already defined in memory. ada7777 sshkey now uses a provider 95f2fe7 Ported mount over to using providers 86dae84 Fixing ports to now use a provider 7e488b2 Working on migrating the parsedtypes to using providers for the parsing aspects. This just converts the hosts; I will convert the others next. This is all work from the sync-retrieve-refactor branch, modified to work with trunk. ce4c494 Fixing puppetmodule to use env to find ruby 0472b24 First batch of fixes from running tests. 5ddbc36 Fixing sbindir path, thus fixing #302. 29ff4f3 Switching from calling "up" on the migration directly to using the "migrate" method. It is still not checking versions or allowing external forcing of migration, but it is a start. b4ebe76 Rewriting nearly all of the tests for the tidy type, and redoing the internals of the testing. 9e5ea8c Fixing the test scripts so that the library path is modified in ruby instead of in the env line 72688e3 Fixing gennode; it was not actually adding the class code to the generated node. 816c5ce Adding a ruby header to all of the tests so that they can now be executed as normal ruby scripts. Using multiple commits because I am having some svn problems. 1d56ca6 Adding a ruby header to all of the tests so that they can now be executed as normal ruby scripts. Using multiple commits because I am having some svn problems. 624eddf Adding a ruby header to all of the tests so that they can now be executed as normal ruby scripts. Using multiple commits because I am having some svn problems. 67704e7 Adding a ruby header to all of the tests so that they can now be executed as normal ruby scripts. Using multiple commits because I am having some svn problems. 0859da9 Adding a ruby header to all of the tests so that they can now be executed as normal ruby scripts. 1020c04 Making all test suites executable, adding some tests for handling changing files from one type to another, and fixing #304. The problem with #304 was only occurring when backing up to a filebucket (I can only think the example code was wrong) dad596e Fixing #291 -- the problem was that instead of throwing an error on a missing server, puppet was just exiting. 9a8636c Moving all of the configuration parameters out of puppet.rb into puppet/configuration.rb, and adding a PATH setting as requested in #307, although it does not include a default. 86b3386 Adding the ability to have hooks for configuration parameters. This will simplify things like setting the shell path. 32deb3f correcting warning about spaces before parens c48f68c Adding patch from Jeff McCune, #317 9bd5593 adding explicit load of ast/branch to its subclasses b9ed053 Format tweak for fact tutorial. 5403a91 Adding a summary of using facter and using imported facter facts with puppet. 90e4c7b Adding some documentation to the programmer's documentation introducing the concept of providers. f545350 Adding some documentation to the programmer's documentation introducing the concept of providers. 7b75590 New documentation hierarchy: fixing indexes. db24e17 New documentation hierarchy: fixing indexes. 628f6c9 New documentation hierarchy: fixing indexes. 9c01560 New documentation hierarchy: adding indexes. ba33249 New documentation hierarchy. da7cb9f New documentation hierarchy. 7b0e528 I was stupidly creating an error but not raising it. 29cce30 Moving methods around so they are alphabetical ed89572 Committing the metatype branch -- this is just splitting the type.rb code into multiple files for readability b4fd8d1 Catching missing ldap correctly in puppetrun b5344f2 Fixing the problem reported by Adnet Ghislain where facts do not load on later runs. 8f9264b Fixing code from [1754] -- I stupidly did not even do a parse check, and I had to refactor the patch because parameters do not have code associated with their values. 94484df Applying patch from #234 from David Schmitt. This is also untested, and the patch is slightly modified. 4a8c5dc Adding modified patch from #256 -- apt now uses "responsefile" for the preseed file. This is untested, though, since I do not know how to test it. a7c88e8 Adding patch from #308. 08900a4 Fixing #298 - refreshonly now correctly deals with specified false values 133c17f Fixing #305 -- logs now reopen when Puppet restarts, and there is also now an autoflush mechanism available so logs will flush to disk immediately. I also now trap USR2 and reopen logs when it is sent, so if you just want to reopen logs you do not have to restart the whole process. c3cc162 Fixing #309 -- files now correctly replace any number of slashes with a single slash. Also, trailing slashes are removed, since that is how Puppet expects to find files internally. c8ea361 Fixing #301 -- s/logfile/logdest/g 1c6cb60 Fixing #310 -- users no longer autorequire their homedirs, and files now autorequire their owner and group. 34f8337 Refactoring reporting. Reports are now modules instead of simple methods. 5b6ee8c Adding a "genmodule" equivalent to classgen, which we will use for reporting 1cdbe52 Added a section about testing. 96aabac adding id tag to tags.page d532ea8 Documentation edit addfe16 Adding a document to outline the use of tags 0716c83 Expanded documentation for rrdgraph report. 675495c Many, many, many performance improvements in the compiler (I hope). I did not change functionality anywhere, but I did some profiling and significantly reduced the runtime of many methods, and especially focused on some key methods that run many times. ab0141a More specific configuration and argument documentation. c7a7381 Documented signals the puppet daemons accept Documented host type to make it clear ipv6 is supported aea6eaf adding id tag to reports 8f058a0 Fixing the rrdgraph report so that it creates a separate rrd directory for each host b892093 Fixing weird case where the default node is in one node source and the real node is in a different one 06a7d34 Fixing ldap nodes -- they were always returning true because i was returning an empty array for missing nodes. 2489764 Changed document priority. c1afdec Another minor formatting fix. c9f6113 Missing tick. d72c970 Adding some documentation on reports. 8a8191f updating install docs with new suse pkgs 1213d60 Removing some left-over debugging 28cee40 Merging the changes from the override-refactor branch. This is a significant rewrite of the parser, but it has little affect on the rest of the code tree. e0e2913 Renaming logfacility to syslogfacility as recommended by lutter. 30fa686 Adding configurability to the syslog facility, using the "logfacility" parameter. da0c862 Do restart as stop + start, since sending HUP to puppetmaster doesn't work (see trac #289) ee76231 adding links to the real deb pkgs dc6d426 Comment out the setting of PUPPET_LOG, so that puppetd uses its default 6e6cb8f Messages will now be at current loglevel, regardless of whether the object path is displayed. 30f9fa3 Added parameter 'withpath' to toggle printing of the object path. 8cc3c8a adding a note about single-quoting node names 5da80db - New type Notify for sending client-side log messages f53b9b0 - New type Notify for sending client-side log messages 04c0c14 Changing warnonce to Puppet::Util::Warnings.warnonce. 8214c48 Fixing suidmanager so it uses warnonce instead of using a variable that only existed in Util de304e5 Rephrased a short section about finalizing the object dependence hierarchy. fee5116 Fixing reported problem of crons rewriting every time when the environment is set 244a11d Fixing what I hope are the last batch of problems caused by the addition of the suidmanager module. Also fixing a couple of other small issues that somehow cropped up. All tests should now pass again. 674841c Just fixed some RCS/CVS id tags. 98028ce Adding flush functionality as requested by Scott Seago fd9b2f6 File types were dying silently on OS X when the group specified in the manifest was not a valid group. db7d784 Fixing SUIDManager#asuser so that it only resets egid and euid if they were changed dfef7e1 Adding a note about another error d888d9e Added some documentation to the security page to offer some example invocations useful for generating/signing certificates for clients and servers. ab225aa regenning configref with the fixed spacing b4970a9 Expanded documentation of command-line arguments for the puppet executables. (Tweak) cc08e2f Expanded documentation of command-line arguments for the puppet executables. 008a138 Expanded documentation of command-line arguments for the puppet executables. f2ac4dc Updating changelog for 0.19.3, and merging the version changes over. 68016a9 ! rename file because rake_test_loader is dumb. 287b18c + New assertion: assert_uid_gid will check that the UID and GID have been changed to the proper values. This uses a fork and a FIFO to achieve it's checking. ! nonrootuser and nonrootgroup now only return users/groups that are less than 255, due to the "Darwin debacle" ! many, many, many fixes for suidmanager. This is rather embarassing. 55f2873 Merging the fix to server/master.rb 3aac2e1 Some small housekeeping things that I saw while doing other bug hunting 48082a1 adding note about the irc channel 515f3cc Harded-coded pathname to OSX's ssh_known_hosts as a work-around until the ssh pathnames are user-configurable. 7b5604b Adding some test reports 6f11dee + Puppet::SUIDManager - This replaces all calls to the built-in ruby 'Process' library for uid/gid/euid/egid operations, including (not surprisingly) Puppet::Util#asuser and a method to run commands and capture output. This is due to many inconsistencies (through bugfixes) between ruby versions in the 1.8.x branch. This is included in the core puppet library and can be used by all puppet types and providers. ! Modified Puppet::Util#uid to check (and warn) if passed a nil value. ! Changes to use Puppet::SUIDManager instead of Process and relevant Puppet::Util calls. ! Removed Puppet::Util#asuser. 320ac38 Updating CHANGELOG for 0.19.2 8f9dcb5 Updated to version 0.19.2 595d5ba Updated to version 0.19.2 6902f2d Updated to version 0.19.2 164c18f As requested by Christian Warden, triggering an object now results in an event, which can result in further triggers. 98004b2 Adding some error handling for when a non-existent report is asked for, and adding a bit more testing. a1e27bc Adding trace information to autoload.rb 0bd3055 Switching Autoload#loadall from using "load" to using "require", so it will not reload already-loaded files. Also updating the checksum docs a bit. eecc7cc Fixing error in tagmail when there are no messages to report a468c15 Disabling a test on solaris, since apparently sh on solaris is different than everywhere else 64a3392 Small test fixes in preparation for 0.19.2 d35d04e Adding class list method to group. Also added a test to verify every type responds to "list", but it does not pass right now so it is disabled. 9afdf1f Adding an Autoload instance to Type.rb so that I can load all known types for documentation purposes. And, of course, loading all types in puppetdoc. Also updating zone.rb to fix markdown's stupidity in trying to interpret the ERB template, and adding some timeouts to puppettest.rb 1ae4344 more fixes to the zone examples c8c3ee9 doc updates 2e17e4a Doc updates 9bd69c4 Typo: As stood had "remove" for "remote" fcf16f7 Fixing #245, opened by marthag. 1ad76d1 Fixing #278, opened by Digant, with patch. 5faa45d Fixing #274. I just set :ensure to be :link when :target is set. 62abbd5 Fixing #283, opened by luke. ea4b9c8 Fixing #285, opened by ericb. c99843a Fixing #288. 2b27289 Fixing #293, I think. The problem was that the groups state was not correctly passing strings in all cases, which caused some very strange problems internally. 0870c5c Fixed a minor typo dc8fb0a Fixing #292 (A bug in tagmail that causes any tag other than 'all' to fail) and #277 (tagmail report missing To: header). afed9a1 adding an extra make target for debugging, rather than defaulting to always creating the debug file e88bf77 Rake::TestTasks were running the test suite inadvertantly against the installed tree instead of the development tree due to a botched "libs" setting. bc15e04 Fixing provider commands and Util#execute so they always include the command output when possible, as mentioned on the list f0a9345 Regenerating docs, and correcting some markup mistakes 62917fc Small update to the fileserver tests; it was apparently not making some test dirs correctly 3891f48 Converting to using the Rakefile for testing. The old 'test' script is now deprecated, and I'll send an email to the dev list and update the docs to reflect that. dcab464 Reworking test/lib structure a bit, and renaming all of the files so that their file names match their module names abe1d3c Fix trac #282 (Change URL to configref, mention --genconfig) 7030aca Small modification so i can make more changes 6a1b43a updating changes from the trunk 6747b6a going through all of the other providers and making sure any reference to a state uses the :should value, not the :is value. 69d4083 Fixing case of silly states on os x, where files are owned by "nobody" and File.stat returns a huge number. I thought i had already fixed this, but apparently not. I added a test, and it is definitely fixed now. 8cbe19f Fixing the same bug in the netinfo provider -- it was retrieving the "is" value instead of the "should" value 6137812 Fixing #280; added a warning and exiting if no hosts are specified to clean 533a022 applying patch from #275. aptitude -q works fine on my testing release, but apparently stable does not support it 94f5865 Removing the no-longer-necessary type/nameservice info -- it is all in the provider tree now 94762e1 Trying to fix a bug where files other than site.pp do not get noticed for reparsing 176d483 Add config option 'node_name' to control what puppetmaster considers the proper name of a client (name in the SSL cert or name uploaded with facter) Default to name from the cert fd4ef3c Better documentation around certificate revocation and mgmt c8a6df0 Updated to version 0.19.1 ee8b8c7 Updated to version 0.19.1 6f85511 Updated to version 0.19.1 0e58f65 Updating changelog for 0.19.1 4a3c8d1 Adding testing for the default? method, and fixing it to support arrays and returning false when no defaults are specified 48992d7 Using the "trace" configuration parameter to determine whether a stack trace should be printed, rather than just using "debug". I added the param a little while ago and was using it internally in Puppet::DevError, but I just now went through the whole configuration and switched to using it. cda7253 Adding patch from #235 5219205 Fixing docs, as mentioned in #271. c04cb13 Reverting the work done in [1605] and [1606]. I have added it as a patch in #271. 08499b1 Adding the feature from #259. I had to rework the Scope#lookupvar a bit, but everything now works as expected when variables are either undefined or set to empty strings. 26bf373 Applying patch in #160. e46d007 Fixing #262. I somehow lost the line that only added a given user's jobs to each tab. 0af8ad7 Removing a test in the parser that is no longer necessary because of how imports work now, and fixing a snippet not to interfere with a local fact 5669d1b This commit adds two important features (but which probably were not worth the priority I suddenly placed on them). fbdd6c4 Specifically rescuing Exception, since apparently the default does not rescue LoadErrors and everything else 349bddd collecting output from blastlist, for later use 4cd37ad Merged test framework into trunk - still not ready until tests are converted to use it. 4897995 Fixing the "Adding aliases" message, so it is clear when an alias with spaces in it is used 6bb4814 Fixing #267. The problem was that the user provider was retrieving the @is value instead of the @should value, because it was using [] instead of the should method. I fixed the FakeModel to behave a bit more like real types, so that it keeps track of the is/should values, and also to keep track of which attributes are valid, since I immediately ran into another problem stemming from the use of the fakemodel. e5aa761 Updating changelog for 0.19.0 2c57173 Raising element creation errors up outside the "create" method, so that tests can more easily tell when an object is invalid. 16d9d6f Fixing spelling of retrieve, to fix ##268 3b8c9ff Fixing #269. I was aliasing every case where the title and name were different, where I should only have been aliasing isomorphic types, which does not include exec bf5d0bc Catching all errors encountered during loading, not just LoadError, to fix ongoing problems with rdoc/usage. 64eb1e8 Let puppetd listen (when given --listen) without a CRL 5e2091b Brute force fix for trac #266 37c9633 Fix test_host_specific to not depend on the path of the test directory and reenable it aa56185 Expanding the Fedora/RHEL instructions some 47dc290 adding note about david's yum repo 8ff418c Fixing the problem with fileserver expansions, and doing a bit of refactoring to make things clearer cf5291a Fixing the interpreter to nodesearch across all listed names, just like is done in the manifests. Also fixing a comment in type.rb ca6ac62 fixing typo 0527426 adding gentoo notes 3e1b6bc Adding a :trace config option that prints stack traces of DevErrors, and using that in DevError instead of :debug 95269bf Adding test code for providers that makes sure the default and confine mechanisms work internally. 09f264a Add config parameter ca_ttl and deprecate ca_days; ca_ttl makes it possible to generate certs that are valid for < 1 day 130b245 Use Pupet.warning instead of nonexistant 'warning' a5f4f53 Fixing #261. Applied patch, with small modifications. 0c0936b Adding a module for helping with warnings, starting only with the "warnonce" method af1de89 Sync with FE repo c651b19 Disable the sample fileserver module by default, otherwise users get spurious warnings about nonexisting directories 65bb635 Updated to version 0.19.0 61e42e7 Updated to version 0.19.0 12b219e Updated to version 0.19.0 e309b76 Modifying the provider base class so that it defines a method for every used command (e.g., you call "commands :rpm => 'rpm'", and it defines an "rpm" method. I then pushed this throughout the package providers, which are the heaviest users of commands. c5ce953 Adding aptitude support, including a new util::package module that provides a method for package version sorting, and a couple of smaller bug fixes. This fixes #237. 2113eed Adding hasrestart parameter to services fcc5bae Adding an "env" parameter to exec, for providing extra environment settings, as requested in #236. 8310c9d Adding a "withenv" execution util method, and using it in :exec for path handling. Next will be other env handling. f8254c6 Fixing #230. If the first line in the cron tab, before the header, starts with TZ= then the header will be inserted after the TZ line. fa16a92 Fixing small bug in cron where removed fields are not deleted from the file e28250d Adding further gentoo support -- finalized portage support, plus conf and init info for puppetd 41c9081 Adding the Daemon module back into the Client class, which fixes #247. 46fbf95 Adding an "ignoretags" attribute to transaction, and setting it for downloading plugins or facts, and for creating config directories b303e8d Adding the ability to download facts from the central server. This allows facts to be available before the configuration is compiled. b36df18 A small fix to the install/update aspects of packaging. 47c86e5 Fixing the package type so that :ensure is always used for version specification, rather than :version, which is now deprecated. This provides much more consistency. I have not tested on all platforms yet, but I want to enable testing on Gentoo, also. 19992f7 updating documentation for how to specify versions 7eed92e Applying a patch from Jose Gonzalez Gomez; apparently this makes package updating work 40b2ed6 Adding portage support again, since it was added in the branch i reverted 617fe58 Removing all of the changes I made towards refactoring in the last couple of days. They have all been moved into the sync-retrieve-refactor branch. This branch will soon become 0.19.0, and will not include that refactoring. 8f39318 Committing a small amount of work in cron. I have decided that this is too last-minute, and not important enough to hold up the release. I want to get this refactoring done, but it is clearly not the 4 hour job I hoped it was. It will have to be in another release, I think. b43b489 Committing functional mount support. All that's left in this chunk of work is cron. 639ed3d Adding first version of the portage provider, as contributed by Jose Gonzalez Gomez 655881c Ports now work with a provider daa79e2 Intermediate commit; ports are not working yet b657850 Fixing SSHKey support. 270f444 Beginning the process of moving parsedtypes to a provider. Each parsed type will have a parsedfile provider, which will be responsible for all of the file parsing and generation. This should allow us to create a useful DSL for file handling, but it also *drastically* simplifies these types. b9b3384 This is the initial commit of the changes to sync and retrieve. The structure itself is now in place, and a few of the types (the most complicated ones -- file, user, group, plus exec since it was my easy first test) have been converted. 3733034 Renaming parsedfile to loadedfile, which makes much more sense and reduces some naming conflicts a887993 Adding Gentoo support from #224. c626796 Adding eat-last-line support in ERB a115050 Adding pre- and post-hooks, as requested in #233. f797487 Fixing #239 -- missing checks now throw an ArgumentError. This will break if any command purposefully returns 127, but that would be a bug anyway, I suppose. ff18e55 Adding support for file purging, as requested in #250. Any unmanaged files in a purge-enabled directory will be removed. 18320ee Adding a "force" parameter in files to fix #242. Currently only used when replacing directories with links, but should probably be used in other places. 7fd5b6f Specifying true/false allowed values for file[:replace]. 8cbe1d3 Adding logcheck script from #244. 2cb2e03 Applying patch from #251, and switching "confine" to "commands", so we can document the command requirements. 712e157 Adding SuSE files from #252. 42812f8 Applying the patch from #253 plus tests. fd86428 Updating templating docs with more about usage, and adding installation notes about ruby segfaults on Debian f587d88 Adding note about configprint, indicating what versions include it 989716e Adding zone patches 96350b2 Fixing #257, where host aliases were not being retained. The problem was that the "should" method was returning an empty array if it was not set, and it should instead return nil. 114debd batch of small bug fixes 2802b70 Making sure that the svn bin directory is at the beginning of PATH, rather that at the end, so the svn-versions of the exes are being tested. b086280 Removing type/provider references on type removal 053f37c adding notes about the libruby deb package 77783e5 disabling reporting until i can find a way to make reporting only work with puppetd, not puppet -- clearly puppet should not try to report 199f5b0 Fixing the state so it tries to call provider methods and only checks for errors, rather than checking with respond_to? b6b9d0b Setting both "report" to true by default; I am going to enable pluginsync by default once I have plugins well-documented 02b8b13 Fixing array printing in to_manifest 29edb14 Fixing service refreshing -- there was a problem persisting from the provider work 14d64dd downgrading the template interpolation message fa0446b Adding back in the code to change the euid. I removed this yesterday because I thought it was redundant. It is absolutely clear that I need to add tests for running as separate users. 6f175ce Adding automatic stacktrace printing to deverror. I need to go through and remove the redundant puts in the rest of the code, but I need this now for some client debugging a46a620 disabling chuser on os x, since it is broken with < ruby 1.8.5 a53d840 fixing provider commands; I broke them when making them easier to document 0f231b8 Skipping blank lines and comments in autosign.conf 9381d5f Fixing report lookup so it looks up by name, not method 948c96a Changing autosign mode to 644 762599b Fixing tagmail config processing so it fails when appropriate 0674c9a Fixing reports error reporting a6c38b5 Fixing location of ca cert 04b2557 Fixing report autoloading; I was calling the wrong method, and they were never getting loaded 47dbf82 Upgrading Triggering line from info to notice d45d22b Adding a module specifically for making doc generation easier, and adding defaults info to provider docs. e1aff4c Last commit of puppet docs for the night b9ad604 Modifying providers so that docs generate better 9b526ba Adding provider docs for required binaries 6aaeb05 Updating generated docs 5395e23 Adding pointer to templating docs into the file[content] documentation 8dbca3d adding notice that "$" is now required in definition prototypes 333c229 adding first draft of templating doc 628896b Adding a "configprint" option for printing out the local config state 9f7621b Adding a little more validation to the schedule, and documenting the source search-path stuff in files ad32b71 Tracking down some weird bugs that managed to creep into the parser. I expect that the main ones were a result of the If support. db0be8e Committing some changes to the %h expansion in fileserving. This change makes the expansion entirely isolated in the Mount class, which makes it much easier to test. Also, switched the fileserver config to use ParsedFile, which makes it a bit easier to understand and handle reparsing. a44b1dd Committing the other half of the fix for #231; oops ed15471 Fixing #231. 55d3fb8 Support for %h and %H replacement in the path of fileserver modules. 2540cdf Demoting the xmlrpc access logs to debug from info 38a184e Changing permissions on the cert file and the ca cert file, since it is no problem for them to be readable and sometimes it is required b612a15 Try this; seems to work for machines with both ActiveSupport installed and not. MissingSourceFile is an AS thing, though it subclasses LoadError. 8fcec23 Adding up2date support, as submitted by Kostas Georgiou (with some modifications to support providers). 9576d1d Certificate revocation through puppetca. Keep a simple text inventory of all certificates ever issued. 4151fd5 Committing definition inheritance. I have not yet written tests yet, but my last commit pretty seriously broke some things without me realizing it, so I wanted to get this in. 1b2ee4b Adding "if/else" constructs. No operators, no elsif, but it is a good start, anyway. ea32a38 Function autoloading now works as requested in #214. bba972f Adding warnings and error throwing for #218 -- metaparams in prototypes are treated specially. a1d71d9 adding faq item about ipv6 support 6e4d4c9 Accepting patch from #220, thus fixing the bug. e322161 Fixing #225. Normal file copying worked with spaces, but recursive file copying did not. I modified one of the support methods so it works now. bf43c76 Fixing #228. The real problem was that "present" should match any type of file existence, whereas it was just matching files. If the file was a directory, as in this case, Puppet considered it to be out of sync. Now, "present" matches files, links, or directories, but still creates an empty file if the path is missing. aee1c6a adding cookbook into to the docs index page 7ade561 Support for certificate revocation and checking connections on the server against the CRL c6fc6c5 Adding a link to the cookbook b2031aa Making some of the metaprogramming a bit more explicit and a bit easier to manage. In the process, I have created multiple Util modules, only one of which is used for this current commit. beba3e4 Finishing changes to support titles instead of two types of names. This is basically a bug-fix commit. 607d7c6 A first pass of changing one of the types of names to titles. I still have to fix a lot of tests, but the core itself is now working. 12452ee Merging r1468 from the implementations branch with r1438 from when the branch was first created. 4d6120a Committing changes that require dollar signs in prototypes abaeb86 removing classing example, since it involves parameterized classes e684cd3 Adding some documentation for the cfengine module 1eaf1bc Fix problem when --fqdn is used e6aa4ab documentation updates, pointing to the suse yum repository and specifically mentioning package locations 0e862a3 Fix shebang lines in executables 450b495 Added comments to stay in sync with the spec file checked into Fedora Extras CVS d6fc1b7 more ordering info 89b4dfd adding ordering information 7957ce0 fixing puppetdoc to add ordering info f974ffc Fixing the master server so that it always uses the Facter hostname, not the cert or IP hostname. eb8c687 updating links after a link validator 257fb78 fixing faq links 3ef4663 adding DSL class. Sorry, not much in the way of docs. 31b1d0b fixing more doc links b2f1aa0 doc updates b8bf113 Updated to version 0.18.4 94cc68b Updated to version 0.18.4 ce95ee3 Updated to version 0.18.4 f13c451 updating changelog for 0.18.4 cdeccab Another batch of bug fixes, this time focused on OS X patches. Looks like I did not test on os x last time. b42eaee First round of bugfixes in preparation for 0.18.4 9e61510 Fixing #77. As I feared, this was a pretty complicated fix; I had to add a lot of infrastructure to both ParsedFile and Config. All config files now have a timer created for them, and by default they check for file changes every 15 seconds. If there is a change, they get rid of values set by the file (but not set on the cli) and set the new values, then the re-use all of the sections, so that any changed directories or whatever get recreated. 310b3a1 Adding timeout functionality to the ParsedFile class, in preparation to adding config reloading to the Config class. c8537a5 Apparently objects were legal rvalues, which does not make any sense. Fixed this, and added a test verify. 21ae8fb Fixing #185. Added a check for cdrom sources, and added an override parameter. 039abd6 Fixing #176. You can now do duplicate UIDs (or GIDs on most platforms) with :allowdupe. 2091edd Fixing #200. I basically just moved the daemonize statement before most other code. This makes things a little less nice when starting puppetd manually, since it might still fail and the user would not know without checking logs, but it is the only real option at this point. 76aec7c Fixing #202. Just bumped the log level to notice and changed the wording slightly 3cc3f66 Applied patch in #203 7228413 Fixing #201; users now autorequire extra groups ebd28e8 moving plugin evaluation into a begin/rescue block 8e25115 Fixing puppetdoc's output 041c07b adding all mailing lists to index 19e411b removing message about the statefile not existing 09e0792 more doc modifications c9640a7 Updating some docs, and renaming configuration reference page 40e2db3 All docs moved over now, and the real index page exists again 813d1c9 committing docs before i move all of them into a separate subdirectory f02f6f7 Adding Solaris SMF manifests and methods 0b90333 Fixing #191. I was only testing for parsed cron instances, not for created ones. aba3d65 Fixing bug in scope/interpreter where nodes found in ldap must have parent nodes. The problem was that the the scope was using the presence of a parent node to determine whether a node was found. Instead I added a flag in the arguments to "Scope#evaluate" to mark nodes as found. 01c8808 Adding a unit test for plain "nodesearch" 08650c1 fixing html markup e74b8af fixing html markup 8273f21 fixing index page in the docs b23b797 Updated to version 0.18.3 fe8ce26 Updated to version 0.18.3 a984a90 Updated to version 0.18.3 8063ab1 Fixing filebucket server so that paths are not added multiple times 1ab4594 Adding tests for previous config bugfixes, and updating changelog a6cc3e4 Fixing reports so that multiple host report directories can be created. There was a config conflict before. 73556a8 Fixing templating so it immediately fails when a variable is not found, as opposed to passing up the method_missing heirarchy, which was causing a nasty memory leak and some kind of weird, long-running search 1ec1b99 Fixing weird case involving interpolating config params in a URL 8f28c6f Fixing weird cases where configs might think non-files could be files b116ac7 adding sysidcfg param to zones a3849d7 Fixing templating bug that can result in what looks like an infinite loop, and changing default timeout to 2 minutes instead of 30 seconds 86a92de Reducing log level of missing file 7139901 Fixing error when template does not exist 3fbd06a Fixing misstated error name ExecutionError in blastwave packaging support 829c754 Fixing reports server so it refers to the main server 0acebb1 Default the passno to 2, defaulting to 0 is a bad idea since it disables fsck 44c54fc changing plugin owner to root 9be1e0b changing default plugin host to be $server a4a04fe adding rake targets 0c96fc6 Doc change: explain what the values for ensure do 67dab0e adding another state that is equivalent to "stopped" for smf services 8a10b08 Adding the newly generated docs e57f6e7 More documentation updates. I think this is sufficient for replacement of the plone site. 9b7f428 Fixing rakefile so it generates docs from markdown, and adding big-picture.page to the menu 70877fb removing faq.rst file fefe1c5 Updates; remove mention of patches from specfile completely f42666c updating changelog for 0.18.2 eff8d6e Accepting the patch from #190. 6b281ed removing cf2puppet from rpm bd9fd8d Updated to version 0.18.2 71036e7 Updated to version 0.18.2 aa87963 Updated to version 0.18.2 afe84ec small fixes towards 0.18.2 e17f4ed adding host information to reports and tagmail report 1503b42 renaming tagmail config file c3a8d45 Redoing reporting a bit, so that reports are now defined as methods. If they are not methods, then they cannot use return, which makes things a bit uglier. 3c22bc9 fixing some smallish bugs in preparation for 0.18.2 73569d0 fixing a small but important typo, and adding sunfreeware as a dupe of blastwave packages 87ce8ed Adding blastwave packaging, and doing some fixes on gem and sun packaging 2e78526 Some updates resulting from trying to track down a segfault introduced when I upgraded to 1.8.4-5 in Debian. I never found the segfault and had ot downgrade to 1.8.4-1. I expect it will not be encountered in real life, only in testing. e57c513 Adding Ruby Gem support to packaging 25cf31b Adding minimal update checking for templates. It will only check the templates that have been parsed in this process, but it is better than nothing. c899e23 documentation updates c1e0bc6 More report and metrics manipulations. This should be the last of it. 34e779f Significantly redoing metrics. There are now no class variables for metrics, nor no class methods for it. 24f07e0 committing tests for previous changes dea7e24 oops; adding transaction report class 70143d2 Trying to merge metrics and reports. There is now a separate transaction report class, and it works throughout the previously existing system. I will next go through trying to make a metric report that graphs the metrics in rrd. 795ec70 adding a "thinmark" method, which does a simple benchmark with no logging 19b5d30 Accepting patch #189, although I am just putting the environment statement in the main part of the class, since there are two apt commands ff6562f Fixing #133. Added a "notify" and a "before" metaparam; notify is the opposite of subscribe, and before is the opposite of require. 9bb9e10 Fix a small bug in mount where parsing fails if dump and pass are missing (they are optional on Linux) Revamp the tests slightly so that they parse fstabs provided in svn rather than relying on the fstab on the system the test is running on. f792a02 Moving the template handling into a simple wrapper object so templates don't have full access to the scope object without some real hacking. 8b60619 adding some tests for the template function a6dc7f2 Adding initial template support. It is just a function, and a method_missing method on Scope. e47a987 First commit of complete reporting support. The only existing report at this point is the tagmail report. I expect reporting to get significantly modified from here, but it is a good start. ea91896 changing the #!ruby lines to #!env ruby 1677594 Adding reporting client, server, and tests. At this point, the server just stores the report in a file as YAML. 56a2845 Adding report collection to both statechange and transaction. d275489 Updated to version 0.18.1 35ef37b Updated to version 0.18.1 427831c Updated to version 0.18.1 7adafc6 For each type, adding a "new" method to Puppet::Type, so instead of typing Puppet::Type.type(:file).create(...) you can now type Puppet::Type.newfile(...). e8c57ae Cleaning up plugin handling a bit -- they can now be colon-separated paths, and I added a separate "plugindest" setting for determining where plugins are synchronized to. The major feature I added, though, is that Puppet::Type now knows how to load plugins, and treats :pluginpath as a search path for plugins. d98ab11 Fixing zone tests 4985d8f adding message about retrieving plugins dec4053 updating CHANGELOG for 0.18.1 5471211 Moving the timer monitoring to after the services are created (because they actually create the timers), and adding a sleep statement to give the threads enough time to create the timers. ad1396d Fixing backgrounding in puppetrun; I had the bit flipped between the client and the server, such that setting --foreground caused the clients to go into the background. 5be3c10 Converting Parameter#proxymethods from using eval to using define_method b2304f1 Making sure fail function converts everything to strings c363af0 Adding "fail" function, which will raise a ParseError if it is encountered. 57a5a71 Catching errors thrown during object evaluation and marking the objects as failed. 506269f adding hooks for ignoring files in the plugins directory, and defaulting to ignoring cvs and svn files 7685957 removing that info message, duh; it produces a lot of spurious output during parsing c886194 Adding info messages about errors loading plugins 772ea91 Adding support for special freebsd @schedule crap. Also making sure that cron listing works as expected. 08f113c switching puts to print, so the carriage returns are always included in the messages bdd1761 Largely refactored how log destinations are handled, although it is not exposed externally. Most of this work is related to handling a large number of small problems related to threading. 73a4bcc Changes to make puppet package more LSB compliant. Update specfile for very latest Fedora ruby packaging guidelines. lsb-config.patch only checked in for documentation purposes, since changes are part of this checkin. 0411f74 Fixing some more small problems in puppetrun 31c17e4 Adding more docs to puppetrun, and fixing bug that can cause hosts to get skipped faab17b adding - to HUP in init scripts bb5366f Updating init scripts to use HUP for restarting 3c5b10d Adding a "latest" test for rpms, since I have been told this is not working. It seems to be working fine, but the test cannot hurt. 5cf2a4d Adding HUP and USR1 hooks 4a71706 Fixing #178. I just added URI escaping and unescaping to file names. f9a4d7a Fixing #175. The setpidfile setting was being ignored. d812840 Fixing #182. Added a retry section to try reconnecting to ldap. Only one reconnect is attempted in a given search, and LDAP produces bad enough error messages that we reconnect regardless of the error thrown. 46824cd Setting pluginsync default to false, and (hopefully) fixing autosign problem when the file exists and autosign is set to true (#180). The problem was that the puppetmasterd script was redundantly setting autosign in the CA, when the CA already knows how to deal with autosigning, which meant that autosign was being set before the config was parsed. Thus, there was no bug when autosign was set on the command line but there was when it was set in the config. 15905bd Fixing broken symlink behaviour mentioned on the list 0a1e847 Adding plugins and plugin management. The Master Client will now automatically download plugins if pluginsync is enabled, and they will be automatically sourced. edabf9e documentation updates 4df2583 More documentation changes. 8ad2008 adding id tags to all of the docs 58826ca further work on converting from rst to markdown b6a52b7 further work on converting from rst to markdown 3772aaf further work on converting from rst to markdown e891ffb updating some docs and puppetdoc in preparation for a move to webgen instead of plone 90e8ad8 removing the old rst index file 644fd4e updating docs to work with webgen f090760 Fixing my autorequire fix; oops 084a31d fixing autorequire message to include the object type bdb9110b Delete entries from the config file if their should is 'absent' 883921c Test that setting a state to 'absent' really deletes it from the config e841d8f Adding test and fix for empty execs being ignored 6ef3d88 fixing interpreter to initialize ldap in the nodesearch_ldap method, which really only matters for testing (since it is already being inited in the nodesearch method 662fcaf making links even if the target does not exist 0ab461b Updated to version 0.18.0 daac8cf Updated to version 0.18.0 8779dbe Updated to version 0.18.0 ae3dba9 updating changelog for 0.18.0 ead6b17 updating documentation for sshkey 20b0a6d fixing transaction tests to just warn when the user is not in more than one group, rather than failing. be92c44 Setting options and the facility for syslog 9a1b9ec Fixing some logging issues with puppetmasterd when daemonized with verbose mode on, and fixing ldap support when daemonizing c1dd0a1 Changing statechange noop message so it's a bit clearer 603a53c Just logging host failures, not exiting c1f0fb7 Adding fqdn, an --all flag, and --test mode to puppetrun f86357d adding namespaceauth and --listen docs to puppetrun 97c7342 Adding fixes for solaris zones 02d397c abstracting out ldap connections so that there is a single method responsible for all of them and a single connection can be shared in all classes if necessary 076e888 adding ssl usage to puppetrun 15da00c Fixing ldap usage when ldap libs are not available 4a5b886 removing extraneous debugging 7ed5560 Fixing installer; it somehow got broken with recent DESTDIR fixes fd8e080 Fixing #173. At this point, I am just calling both "--add" and "on", or "--del" and "off". This should probably be broken up into other states, but.... ef163ba fixing parallelization to match the docs 1dfd554 Fixing packaging to deal with the fact that yum exits with 0 exit code even when it is told to install a non-existent package. df340d6 Correcting puppetrun docs and fixing a test so it works with older versions of facter b4b3c27 Adding support for default nodes as requested in #136. ba4071c changing puppetclient schema to descend from top instead of iphost 07e0d59 Fixing #169. Tags are ignored during config. c380bfe Fixing the main bug reported on the list today relating to file sourcing truncating linked-to files. 90762c5 adding noop marker as requested ec0609d A round of bug-fixing in preparation for the next release. 9af5d69 adding nothing test to zone tests a6122e8 Fixing ldap node lookup. The test was set up badly, in that it did not actually provide a way to enable ldap node support, even though there was a config option that should have worked. All works now. 360a405 turning on output flushing edfaf6e Adding support for following referrals 555e1b8 Fixing #135. I was setting the object to the result of an include? test, instead of just "obj = ary[val]", so all but the first bucket-backed files were getting errors. 8ceb1f3 Found a bug where single-value selectors can fail on a second compile. Fixed it, and am now compiling all snippets twice. 4ca7ece modifying rakefile to specify the package hosts; they were previously hard-coded in the build library 09d2cd0 Fixing #168. Reworked the regex to allow matching TLDs. 1fc4ec3 Fixing #167. Started with the submitted patch and made a few more modifications, and added a regression test. c90d0b1 Fixing #157. Objects are no longer allowed to be their own parents, and there are checks in both directions to stop this. 7c358df Fixing #166. Function names are no longer reserved words. e73f2d4 Fixing #158. I did not add a force option, since I always back files up before I replace them. 4266f64 adding faq to docs d6d05d4 Fixing #154. Basically just accepted the patch that fixes master.rb and added a test case for it. 1cef8f5 Applied patch from #153. 2257d6f Fixing #155. It is now valid to have arrays with no values, although you will still likely get erratic behaviour elsewhere in the system, depending on what you do with this. 0e52409 Fixing #159 -- packages now have a default value for ensure (:installed). 3758bdb adding rakefile for the docs 1a93e6d copying all documentation from the plone site d84827e Committing largely complete Solaris zone support. I still need to add static filesystem support, but everything else should work. 73c5c58 removing one of the stack traces from error output 95f273e Fixing #163. Strings can now correctly escape dollar signs. a3ed629 Intermediate commit; most of the core zone functions now work, and some of the configuration functions work. 81ce66a Fixing node tests to handle comma separation 011e811 temporary commit so i can transfer my testing to a faster, sparc box add6b80 Fixing #160. Fixing the error in Puppet::Type#[]= and scope.newobject 76ff83d Fixing #161. Basically, AST::ObjectDef now catches when users specify a name as a parameter instead of the name before the colon and modify the results accordingly. This catches this kind of problem, and the normal name handling picks up everything else. b08816b Fixing #162. Node names must now be comma-separated. 2fcbc7f Adding an "execute" method to Puppet::Util, and including the module in element.rb 354b945 adding zone management stub; switching to my home vm for testing 98ad43a fixing destdir in installer, and adding solaris conf stuff 4cd3019 Did some work on making sure object removal actually works, thus stopping some potential memory leaks. Also explicitly removed objects in more places, again hopefully forestalling memory leaks. 45a9edb Reworking cron; adding many unit tests, and making it much more like a ParsedType (although still not quite the same). Too many of my tests were invalid; I think those are all fixed now, and it appears to work as desired. 3ab4a89 Small fix to include puppetrun in /usr/bin 62a0ff0 adding puppetrun to the red hat spec file fda013a Updated to version 0.17.2 3c15a28 updating changelog for 0.17.2 a08ca93 Fixing #138, all of it, I think. Environment settings are now allowed, although all bets are off in terms of parsing existing environment settings in crontabs. 69cf2fe Adding a small fix to cron tabs; they will at least parse tabs that have env settings in them, although you still cannot, at this point, set them. 0381cc1 slight ldap fixes in puppetrun d55adda First version of puppetrun. It seems to mostly work, but I need to test it with greater parallelization. 5671ce8 Added the last of the tests for the runner, along with the necessary work in puppetd to be able to start it. b3ea53c Adding a lot of structure to puppet.rb to make it easier to manage multiple objects in a single process, including making it easy to add threads. Added some testing for all of that. 93771b7 fixing user[:groups] management when the user is absent 738698c Updated to version 0.17.1 ed9adf5 updating changelog for 0.17.1 and 0.17.0 9b5de11 Allowing empty files 5382118 Fixing #146. I think I mostly just fixed the error message; I do not think there was another bug there. 89ce72f fixing stupid debian rails mistake dc3a6d5 Making sure file recursion works for all valid inputs 373afa3 updating version on spec file 4296b02 Updated to version 0.17.0 3be0f95 Wrapping the host storage into a transaction. It might have a slight performance improvement, but, ah, unlikely. 9d6166e adding a test to make sure that defaults get taken up by components a0bcf5a Adding code to try for the rails gem if the library cannot be found normally, and adding some protections in case there are problems 122e2bc only performing collection tests if activerecord is available 8f14c3f failing more intelligently in init if ActiveRecord is missing def5175 Making sure yum fails on unknown packages 2e9f1c4 removing extraneous logging d9fdd8e I believe I have finalized export/collection support. I still want to go through all of the code and s/collectable/exported/g (thanks to womble for that term). ba57dff I had to redo how the scopes handled collectable objects (which I will soon change to being called "exported objects"). All seems to work now, though. 22e70f0 Made a *huge* performance difference in storing hosts -- down from about 25 seconds per host to about 5 seconds on my machine. I will almost definitely still use forking or something to make this not affect the clients 637cc71 I appear to have object collection working, incredibly. This commit does the collection from the database up to adding the objects to the current scope, which is what sends it to the client. 9e9ef1a The "collectable" syntax now works end-to-end -- the parser correctly recognizes it, the AST objects retain the settings, the scopes do the right conversion, the interpreter stores them all in the database, and then it strips the collectable objects out before sending the object list to the client 8ed666a adding a few more fields to the host table 5863a03 Adding initial rails support. One can now store host configurations using ActiveRecord into a database (I have only tested sqlite3). Tomorrow will be the grammars used to retrieve those records for object collection. 0819e35 Adding some small changes towards fixing #140 and #83, but this work needs to take a back seat to object collection, so i will come back to it later. 678e142 Fixing #141. It was a problem related to the recent parser changes I made. 578cf7e removing some extraneous logging a2a4dd5 Updating doc system to add the list of valid values to the doc string, and tweaking a few docs. 710bf0d Slight modifications to package parsing on *bsd. It should be better about catching the version number, and unparseable lines are now just warnings, not errors. 9e77e7a It is just a snippet test, and thus a functional test but not a coverage test, but definition overrides officially work. This was important because it enables definitions to be collectable, which was not possible without the mechanism that enables this. 513b87a Preliminary commit of the first phase of the parser redesign. The biggest difference is that overrides should now work for definitions (although i do not yet have a test case -- i will add one on the next commit). The way this is implemented is by having scopes translate themselves at eval time, but in two phases -- the first phase does the overrides, and the second phase does the evaluation of definitions and classes. fe16f83 making a test to verify that the functionality womble is looking for now works bb60cab Making trigger logs much clearer -- you now get info logs indicating how many dependencies changed, and debug logs indicating what those dependencies are 88c3f7c Changing how events work. Events are now responded to inline, while an object is being applied. 7b7ac18 Changing default for pattern to include the binary if it is included f0aeaec require the very latest facter to avoid problems because facter changed iphostnumber to ipaddress e06c661 Small bug fixes 58cfd1e Fixing the problem that lutter ran into; the issue seems to be that Facter could not find the ipaddress on the server. 18de804 fixing log messages a7fadbe fixing log messages 43fdd89 Updated to version 0.16.5 64a58e4 updating changelog for 0.16.5 44f1579 Fixing a stupid bug i managed to introduce in 0.16.2 (probably) involving importing files with classes in them. This is a better solution than what I had before the bug, anyway. Also, some documentation fixes. a9df49d Fixing some naming problems with crons, and adding appropriate tests e8c912d Allowing dashes in class names, although grammar rules restrict it from working anywhere except node names or in tag(). They are valid in host names, and many companies have them in the host names; in fact, this fix is for a company with this exact problem -- they cannot use puppet with their nodes because all their hosts have dashes in the host names. 37d2850 Switching to just using "preserve" for file copying in file#handlebackups 8b0481c Updated to version 0.16.4 4b84ca9 updating changelog for 0.16.4 b67a19b Fixing #132, which involved creating a separate CA client and using it to retrieve the certificates. There was more work to do because of the weird client/daemon/server heirarchy. a435d07 Updated to version 0.16.3 3f08155 updating changelog 2faa447 Bug fixes from OS X for 0.16.3 5e246ab Hopefully final bug fixes in preparation for 0.16.3 cc5ce34 Fixing tests looking for pmap a1574a5 Fixing TransObject#to_type so that it does not modify the object being converted 7825f49 Changing test for service paths; only testing if it is a directory if it is present. 65f6656 Added some code that could be used later to make sure the user and mode are also copied on backups. 0ad65e9 Adding a check to make sure the mode is copied over. 84db91e Fixing the docs a bit for the executables, adding a --daemonize option to puppetd and puppetmasterd so they can still be daemonized with debugging or verbosity enabled, and causing puppetd to fail to start if a PID file exists (and not setting a pid file if running with --onetime enabled). 12c122c Puppetd now has an option for listening -- just run the --listen option, and it will start up with a pelement server. It will fail to start if the authconfig file (defaulting to /etc/puppet/namespaceauth.conf) is missing, since it defaults to access at this point. 047e63f Making file copying significantly faster -- i found an extra call to "describe" in file sources and an extra read/checksumming of the dest file 94caa8a Fixing #128. md5lite was being used instead of full md5. At this point, md5lite cannot be used for source copies. bcfc469 Adding in all of the patches necessary to make a prototype rails interface to puppet nodes work. The biggest change is that there is now a separate NetworkClient class for every Client subclass, because otherwise you get namespace collisions. Most everything other change is a relatively minor patch. 9539dbb Adding in all of the patches necessary to make a prototype rails interface to puppet nodes work. The biggest change is that there is now a separate NetworkClient class for every Client subclass, because otherwise you get namespace collisions. Most everything other change is a relatively minor patch. 9b627cd Trying to track down the bugs reported this morning, so I added some more test cases. I did find a bug in the filebuckets, fixed it, and added a test case. 003e897 updating changelog for 0.16.1 and 0.16.2 a78bf1e adding "clean" mode to puppetca bda8e52 This should have been in 0.16.1. Moving the "setclass" statements around so that classes are set before a given class's code is evaluated, so it can be tested within the code, within node defs, components, or classes. bff9463 Adding sum type to the retrieved sum if it is not already there. This provides backwards compatibility for existing cache files. feff317 removing unnecessary debugging baa412c Adding "defined" functino to puppet, so you can now test whether a given class or definition is defined. 46ce36b Creating a simplistic, generic function framework in the parser, so it is now very easy to add new functions. There is a pretty crappy, hardwired distinction between functions that return values and those that do not, but I do not see a good way around it right now. Functions are also currently responsible for handling their own arity, although I have plans for fixing that. ccc4d95 Modifying non-existent-package test to make sure syncing fails, and modified ports package type to check the error output instead of the return code, because the portinstall command returns 0 even on failure. e64bd22 Fix ownership on server files (trac #122) Change ownership on /var/puppet 9fe0b37 removing patch from red hat spec file a0b4553 Final commit before 0.16.0 63cdc6c making corrections to pass tests on freebsd d9fd002 Go some work started on developing authorization, but I have made little progress. I might wait on this for the next point release. 4a029d9 pelement listing now works d91b7df Added a list class method to just about all types, and it seems to actually work for everyone. Now just to add a list method to the pelement server. a9b67cc Adding a "list" class method to most types, and using it in the tests for the pelement server to verify that objects can be copied using it. I expect that most package types other than apt/dpkg are not yet working with these tests. e24a299 A simple first version of an object (called "pelement") server is now in place. There is not yet a client, and the tests are pretty simple so far -- only files have been tested yet. I had to make a significant number of modifications to the file object in order to get this all to work, and one of the big changes I made is to the internals of the checksum state. ac04981 Actually adding the ports file that provides freebsd port support 3f9e918 Adding freebsd ports support c83bc91 fixing test to know that i skipped alerts 538bc0c Fixing service stopping; I had the %x{} command quoted 6f66011 Fixin #102. The syslog name is now either the name if the process (if that name includes "puppet" in it) or "puppet-" and the name of the process. Also removing the "alert" test messages, since they result in a wall. d2634ba Fixing #118; the hash is now always 8 hex characters, 0-padded. Also changed the CA cert name to the FQDN of the host serving the CA, rather than "CAcert". 449f662 Fix handling of run files so services can't be started twice (reported with patch by soul916 at gmail.com) 21584a9 Don't create empty log files in %post (based on report by soul916 at gmail.com) a564e49 Changing the log level of the "defaulting to base service type" message d56870c Fixing a bunch of small bugs, mostly found by testing on solaris, and added a check to the test system that points out memory growth 0478f78 changing set to tag in the tests 26f18a2 Fixing puppetca so it does not call chuser; instead, it is configured to create all of the files with the correct permissions and ownership (using Config#write and Config#writesub). c3961ae Adding doc generation for exe arguments 133ad87 Oops, typo in client/master.rb 689dbf4 Adding --test option to puppetd (it enables --onetime, --no-usecacheonfailure, and --verbose), and modifying the docs a bit. cd9ea80 Adding locking to the master client, so that only one copy of puppetd will be running. This should make it safe to run puppetd manually while it is also running normally. 71793fb Changing "set" to "tag" f522a7e Adding the host name as a tag (stripped of the domain name) 373fb3b Modifying "setclass" on scope to check the validity of class names, now that "set" can be used to set them manually, and added a test for it. 8df349c Fixing the language side of #109. Added a "set" keyword. de0d1dd Adding a few informative facts on the server side: serverversion, servername, serverip. And only printing the parse time in the interpreter if it is not a local connection. bca4f5e Adding the puppet client version to the fact list as "clientversion" 9f92a3d Adding the puppet client version to the fact list as "clientversion" 4d75041 Adding a "tag" metaparam 201aa02 Adding simple benchmarking, and using it in a few of the more obvious places. Also, fixed a bug in Scope#gennode. 0507486 Fixing #117. If only one value was provided, then it was not placed in an array, yet AST::Selector expected an array. The grammar needs to have some abstraction added or something, because I seem to have encountered this bug for every ast type that supports arrays internally. ae4b12e Revamp the yumrepo type to deal with repositories defined anywhere in yum's config files. Adds a generic module Puppet::IniConfig for parsing ini-style files 8df6e84 another small mount fix; this time, for stupid os x 88dd992 committing version changes d10a638 Committing an important fix to mounts; since i am sure no one has downloaded 0.15.3, i am just going to rerelease 0.15.3 with this fix in it 83d5236 updating changelog for 0.15.3; I need these exec fixes for my client e5be7d3 Adding autoloading for types and service types, also. fcce820 Okay, last one, hopefully. Modifying checks to support arrays. 37a4a55 And, one more time. My test for the last bug did not actually retrieve, so it did not enounter the problem, and i had also forgotten to add the "check" boolean to the checks. Hopefully this will be the end of exec bugs for the day. 4ab74ce Fixing checks so that they can run even if the set cwd does not exist 50ffa7f adding a bit of debugging 1e4abae moving cwd existence check into "sync" instead of "validate" 7dae24f Fixing a small bug in type.rb that ignored false values (instead of nil values), another small bug in value setting that resulted in the file and line appearing twice in errors, and added validation to all of the checks in :exec (along with testing for all of it). b0edb35 removing patch from spec file 122cf58 updating changelog in preparation for 0.15.2 013cfd2 Adding darwinport type. 9697354 differentiating openbsd from freebsd, adding freebsd, and autoloading package types instead of manually loading them f540ec8 fixing a couple small bugs in doc generation 668342e fixing Config#mkdir test to not check gid on any BSD, since they appear to ignore egid when making directories or files 572648e adding deprecation notice 84693d6 adding some docs 2b27545 renaming; i hate bsd ee65279 Fixing #103. There are now no such things as node scopes; the entire tree is evaluated on every node connection, and node facts are set at the top-level scope. This includes,um, the code; the last commit was accidentally just test changes. c3c413e Fixing #103. There are now no such things as node scopes; the entire tree is evaluated on every node connection, and node facts are set at the top-level scope. a0728c0 removing the parser dir 8db837a getting rid of the parser tree, and moving everything into the language dir d4a5b48 loading yumrepo in the test, since it is not being loaded in the main code e8c0471 Fixing a couple of bugs in preparation for 0.15.2; mostly they were in the testing system and resulted from changing :File to :Manifest in server/master 9230289 Disable yumrepo type since it won't work with the FC5 repo files b5c759b Fixing #108 d8b4b0d adding -e ability to puppet executable c0a9e5f Change how names for nodes are specified: the 'node' keyword can be followed by a NAME or by single quoted text, i.e. fully qualified names for nodes must be enclosed in single quotes 5d42cd5 Fixing the class file to actually store class names, not object ids. Also added tests to make sure it all stays that way. c8be52b Finally! We now have mount support in OS X. Ouch. 3327dc8 Adding netinfo type and some tests 97d5ab6 eliminating some debugging, and removing a small redundancy bug in nameserver.rb 35e65de Fixing authstore to use an array for ordering, rather than a hash, duh. c3b7d62 Bugfixes for OS X. I had to do some shenanigans on type/file/ensure.rb -- it was testing whether the parent dir was writeable on object creation, and if not it was not setting ownership and such, so i added some post-creation checks that will fix ownership if it was not set correctly at creation time. caaa331 changing ssldir perms to 771, so non-root users can write to subdirs if they have permissions a791d98 Fixing a logging bug that apparently resulted from logging changes a while ago. 4daf2c1 Adding apple package support, but it is very limited -- packages can only be installed, not upgraded or removed. f37154e making a small change to the test, so failures are more informative 7c7c223 Added a test for Type#remove, and fixed the method so it actually works. I was missing every other object, because i was iterating over the array being modified. This caused the Config stuff to often fail, because objects were not correctly being removed. All fixed now, though. 72774bb adding mkdir equivalent of Config#write e6f9163 Adding a "write" method to config objects, so that files can be easily written with the correct owner, group, and modes 0f15e8c fixing a bug that appeared somehow in port.rb, and adding mount and sshkey to the types being autoloaded 0eae739 renaming filesystem to mount 48d7fd6 Adding filesystem support, and modifying parsedtypes a bit to fix a bug where non-instance lines were being duplicated c7ae839 Manifests can now specify node names with fully qualified domain names, too. 9b1e8d5 Accept a single file as a test to run in addition to a directory bdc819b Remove unused should method; add more yum parameters to the type a9fdf9d Disbale running puppetmaster as puppet until we've sorted out which files need what ownership (there's trouble with /etc/puppet/ssl right now when puppetmaster runs as non-root) 1365103 New yumrepo type for basic management of the yum configuration of one repo. 6d4e46c Adding os x group management support 791e4da Committing support for group membership management. Currently only works on Linuxes and other OSes that use "useradd" that support -G. 932fd03 commiting package test fix that i thought i committed ages ago 28602a6 Simplified as yum install can be used for both install and update 95b762b updating changelog for 0.15.1 fc98ab0 Fixing #100. I just added a bit of a hack to configuration parsing -- if a group is specified in a section that matches the name of the process, then it is assumed to be the group that the process should run as. The problem is that we are reusing the term "group" here for both the run-group and the file-group. Oh well. 5dcf303 Using differents commands with yum depending on whether the package is currently installed or not. 7e908a5 Removing ruby as a dependency, since too many packaging systems will have installed it differently 73d051f Fixing service enable/disable on solaris 10, and fixing some problems with the tests 5e86634 Converted everything over for Puppet. The Rakefile is, um, a *lot* shorter. :) 8416f21 This version appears to work well with epm stuff fc68910 removing dos EOL chars 086050d Minor changes from Fedora Extras review 72f1e8a Don't mark puppetmaster for start by default; makes rpmlint happier 6006a5a Add little snippet on passing an additional option during system boot, but leave it commented out 6af21e5 adding sbin directory c74fd81 Committing the EPM support. I am in the process of moving this to a common library that all of my projects can use. 271a8d2 Adding EPM package building. 805b32b Updated to version 0.15.0 92e3c1e Updating changelog for 0.15.0. ec7d46e fixing small bug in the test code when there are no packages to test f851be7 Adding upgrade ability to sun packages. Currently it removes the old package and installs the new one. 4d1c221 Changing the way the hosttest output is handled 29ec706 adding some extra info to the ldap test 76474ed Fixing fileserver tests; apparently they were still broken from when i changed the fileserving interface to handle links. d7a75c5 Fixing small bug in symlink recursion f2c8218 Fixing #94. When "ensure" is synced, it syncs the "enable" state at the same time. aed1f11 Ooops, did not save the docs before committing. 5a47afd Fixing #98. Filebuckets now work throughout the system, and the puppetmasterd creates one by default. I have also updated the :backup docs, adding an example. 1b11697 reducing the log level for checksum warning about symlinks, really this time 454247f reducing the log level for checksum warning about symlinks 08b36cc Adding enhancement #92. Unfortunately, I was not able to write test code to consistently verify that this works, because there is too much caching internally. I verified it personally using my own configurations, but that is as good as I could do. This indicates that caching should probably be rethought, so that there is some kind of global "do not cache anything" mechanism. 97913d4 Fixing bug related to recursion testing c6230dd Fixing rpms so they will automatically upgrade when you point Puppet to a new package file caa3d43 fixing broken test from my previous change fa9aab6 Fixing #82. You can now specify comma-separated tags to get run in puppet or puppetd: puppetd --onetime --tags "enhost, facter" -v. You cannot specify classes explicitly, but tags map well to classes and have the benefit of being more generic. 414d364 Supporting rpm installs when a package source is specified 8728920 Using undefined variables is no longer an exception, it just returns an empty string. 4ee395b Fixing small bug when autorequire returns an object instead of a string 2cd67ad There was a critical design flaw in the link recursion work I did previously, and fixing it required a decently large reorganization. Everything is much, much cleaner now. 02f91fc Merging symlinks back into files. Symlinks still exist but with a warning about deprecation. Fixes #93. Also the first time I have run any tests on OS X, so there are some bug fixes related to that. b336e7e Parameters and states can now register regexes as allowed values. Also, there are (finally) tests associated with params and states, although they should be much more comprehensive. b6d829b Fixing #95. I had to redesign how events were triggered; the transaction now individually triggers each subscription, so that it has control in how to respond to failures. Eventually, this will lead the way to error handling within puppet, but for now, it just allows us to trigger every appropriate subscription, whether some have failed or not. 782f85a Creating a single, constistent method for writing files, instead of having :ensure, :content, and :source each have a slightly different mechanism. This method also makes sure that the owner, group, and mode are always set on file creation, so extra runs are not necessary to make it work. 2dbd7e1 Fixing #96. Defaults are now set when the object is passed out by the scope, rather than when the object is created. This is nice because it also moves awareness of the scope internals out of the AST object and back into the scope. 7756f9a Fixing #97. I was wrong about the object type I had, so I was calling "type" with no arguments, which was causing the bug. 2faff5d lowering the log output for nonexistent files eb68633 Updated to version 0.14.1 cee0882 updating changelog for 0.14.1 2351cd7 making case statements not create a new scope 54fcdbd fixing some more logging issues 0549d03 Making some logging changes, and fixing a small bug in group management on missing files 72d747b Updated to version 0.14.0 b76004a Fixing yum listing bug, and caching the "latest" value so it is not asked for so many times; this fixes #90. 3c07deb Committing the last changes, for now, to handling links. You still cannot copy remote links, but you can either ignore or follow them. I do not think we will be able to copy remote links until I have merged symlinks and files to be the same object type again. e9e88b0 Adding "links" parameter to files, and adding support for following or ignoring links to all of the states it can matter to. I still need to modify "source" so that it behaves correctly when managing links. 1099c4a removing group ownership of the state file; I realized that the server does not ever actually write to it. 5cca870 Switching from using "evaluate" to using "retrieve" when getting checksum values, since retrieval is sufficient, and evaluate keeps printing messages about changes. 17d4b23 Fixing logging in the fileserver so it is always obvious where the logs are originating, and fixing a bit of debugging elsewhere. df74b62 fixing deprecation notice about services using "ensure" instead of "running" 8c0a07a removing extraneous debugging be4d3fd fixing the mode of the yaml file f2ea9b7 Supporting variables as the test value in both case statements and selectors. 1a3de8a renaming 549bc5f Only setting group or owner on config files when running as root 7ea739d logging config changes at debug, instead of the normal log level aae9b2a Definitions now always create their own context, which means that they cannot override elements in the containing scopes. 451ba6d upgrading to warning the message about using a cached copy faffd69 Updated to version 0.13.6 caa7f48 updating changelog for 0.13.6 343dd08 Fixing tests so they do not chmod /dev/null to 640 (stupid tests). 1a93c82 Fixing #68. After tons and tons and tons of work, everything successfully configures itself, and the --genmanifest argument should actually work. User and group creation will not necessarily work everywhere (in particular, Puppet uses dependencies to create the group first, but Fedora complains on user creation if the group already exists), but file and directory creation should. The only downside is that there is a decent amount of extra information printed on daemon startup, as the daemon checks its config; this could maybe be seen as a bonus, though, I guess. 95856ea Okay, Puppet is now almost entirely capable of configuring itself. I have not yet added the extra tests to puppetmasterd to make sure it can start as a normal user, and the executables still fail some simple tests because they are producing output when they start (I will get rid of the output), but overall things look pretty good. ff1df8e Remove hte fedora-usermgmt stuff. As it turns out, it's not a Fedora Extras requirement to use it; so we'll just have useradd/groupadd allocate id's dynamically 2db2317 Adding metadata to defaults 179779d Changing the setdefaults input format somewhat. It is always a hash of some kind now. 4574928 Intermediate commit; setdefaults now accepts both hashes and arrays 6d8a1dc Fixing user and group management in the config handling. 65ed766 adding a connect log to the master server 32cbc59 Fixing #70. We now have user and group management on FreeBSD. 8b5f709 Fixing bug #60. Converting nodes to use types everywhere instead of names, and adding a localobjectable to keep track of what parameters have been defined locally. eda9d95 Fixing #64; multiple class definitions in the same scope is now an error, although using the same class name in different scopes is not an error. 56116c2 Fixing bug #73; node names now appear only once in the path c894eb2 Fixing bug #75, providing support for unnecessary end commas. 020499c Removing all of the autoname code 8c821c0 Mostly, this is a refactoring commit. There is one significant new feature, though: overrides now only work within a class heirarchy, which is to say that a subclass can override an element in a base class, but a child scope cannot otherwise override an element in a base scope. 37c10d1 Switching setclass to use object_ids instead of class names, and adding some comments. c5d8680 Fixing scopes and AST so that definitions and classes are looked for in the scopes, instead of in a global list ee818a9 Adding some debugging to list the states being changed when in debug mode 63afa37 Fixing nodes so that their paths are printed correctly 5056054 Removing timestamp debugging b119a72 Fixing output when user/group are not found 772c7c8 Adding TERM to the signals being trapped 503ad38 Fixing bug #72, where trailing slashes break file sourcing 043fc33 adding commas to each line d06cd3f removing the initial syslog dest setting f6ca82b Updated to version 0.13.5 85e4d31 adding changelog for 0.13.5 2dffbee Adding redhat service type, to support enabling and disabling a service 7e5cc76 Fixing package types so you can specify the package type manually in a manifest 7806618 removing extra error statement 6e26a73 adding passwd converter 3aff15e Updated to version 0.13.4 1f05ad0 updating changelog for 0.13.4 cfb0e36 updates 89856ec Adding a bit more logging 82e02eb Fixing bug when creating containers with parents 31df227 Generate an error if the pattern for an import statement matches no file. beef01c Properly figure out when updates are available. Previously, packages would neverbe updated because 'yum list foo' first prints the currently installed package. Now we use 'yum list updates foo' 3ac5cd9 Incorporate initial feedback from FE review 68aa302 Fix failure of test_importglobbing in test/parser/parser.rb 70d2379 Enable passing --parseonly from the command line 1ebb416 Adding single-quote syntactical element 1fdb962 Changing transactions to be one-stage instead of two, and changing most of the type classes to use "obj[:name]" instead of "obj.name" where appropriate, because "obj.name" might be a symbolic name (e.g., File.unlink(file.name) will not do what you want if file.name == "sshdconfig" but file[:path] == "/etc/ssh/sshd_config") 5f8d615 Removed some of the autorequire stuff from :exec because it created untenable require loops, and created a test case for some complicated exec + file recursion. The test case fails, and I want to have a clean committed repository before i mess much more in trying to fix it, which might actually not be possible. 6cc8157 Duh, removing some debugging 5f4335f Adding logoutput parameter to :exec 89702d8 Fixing symbolic naming bug where symbolic names were being ignored in some cases 7d15fe1 Updated to version 0.13.2 037b7ac Changed the parsedtype definition of exists(), and fixed a few smaller bugs. Last code commit before 0.13.2 6fe01ce Tracked down a few other bugs; everything now passes on debian in preparation for 0.13.2 8602932 Changing "answerfile" to "adminfile", adding "responsefile", and autorequiring both. d1cd443 Fixing users so that they can use a group created by Puppet, and they also now autorequire that group. To do so, I modified Puppet::Util.gid, which required that I fix Puppet::Type#merge to support merging managed and umanaged objects, which required fixing a bug in Puppet::Type#managed?, and I also changed the ensure state to only default to a value, when the object is managed, which required that I change the defaults system to support default procs that do not return a value. In other words, lots of fixes for a smallish problem, but we are much better off now. 4df3468 Fixing the order of arguments when using admin files with sun packages 20b65e7 Some important bug fixes in the parsedtypes types; this all started from the submitted bug today, but I added :absent support to most params. 6cfee76 Committing the initial ldap support -- puppet can now look up node configurations in ldap. The test scripts currently only work on my home network. 1994263 Adding --enable/--disable locking for puppetd. You can now disable puppetd from running by creating a lock file, which is useful if you are testing a configuration and want puppetd not to run for a bit. 798b3be Adding a general "check" mechanism to :exec, so it is now terribly easy to define a new check to perform, converted :creates and :refreshonly to use that mechanism, and then added :onlyif and :unless as new checks. Also added any files they mention as autorequire files. 376725e Adding --loadclasses option to puppet f098485 Correcting some path problems with symlink, and changing "target" state to "ensure" 3f15cb8 Fixing :target reference in pfile.rb 9508bd0 Correcting some path problems with symlink, and changing "target" state to "ensure" 64eafa8 Updated to version 0.13.1 a456c4d updating alias docs to pass ReST checks 1a05ed2 updating changelog and docs for :alias 89d37f6 Fixing some problems with cron tab management, and creating Puppet::Util.{u,g}id methods. 96388cb Fixing locking. It apparently was not working on OS X, and I was not syncronizing access in threads -- i assumed locks themselves were a sufficient sync point. 2be25d5 Making the language name a real alias. Now all objects in Puppet support specifying both the name and the namevar, or just a name and having the namevar set. 7f7b5c6 Change in how logging is defaulted: by default logs go to :syslog, unless the user explicitly gives at least one --logdest argument, in which case logs only go to the destinations the user gave. b13b5ed Set the Release tag in the spec file to 1 when the version is changed 8c02ffd Adapt specfile to the fact that puppetmaster now automatically runs as user puppet. Add default config files that send logs to /var/log/puppet. d629a80 Fix version in last changelog entry (makes rpmlint happy) 44071d0 Updated to version 0.13.0 2cb5cb3 Updating changelog for 0.13.0 387db24 Adding answerfile support to sun pkgs. 2ce061a adding some documentation cd7a637 removing errant warning ccd0121 Fixing small problem where checksum retrieving did not look in the cache; this was only ever a problem in cases where checksums have no "should" value set, which is generally only the case on the fileserver, but it caused the fileserver to replace checksum values on every retrieval. 8eab733 Fixing the conflict between ensure and source. Ironically I had already made sure there was no conflict with "content", but I had forgotten "source". adda8f0 Checksums now get correctly updated for both the ensure and content state when those states are used 8e4cf22 first bug fixed, where sources were not updating the checksum 58db0ef adding keyword b03635b adding keyword 01072a8 replacing all occurences of "is_a?" in the parser with "instance_of?" 7ca3d3d Fixing bug that occurs with only one argument 195f2e9 wrapping all work in a single rescue clause 59992b5 adding initial ldap schema 0ba9d16 adding vim syntax stuff a5d2404 Simple emacs mode for editing manifests; only does pretty colors right now b98e65f There is now full support for configuration files, and the entire system has been modified to expect their new behaviour. I have not yet run the test across all test hosts, though. f1ffc34 Configuration parameters now require (and have) descriptions, and a set of configuration parameters can be converted to a configuration file, a manifest, or a component. All I have to do now is integrate them into the executables. 6affe22 Committing both the finalization of the config code, plus all of the code necessary to get basic isomorphism from code to transportables and back. Mostly keyword and autoname stuff. 59c7b02 Fix snippet_componentmetaparams test 39d33ca Temporary commit; configs now can be converted to manifests 4ecfa7b Config files now seem to work, so I am ready to start incorporating them. 9114cbe committing test code for bug lutter found d0436f1 Changes in lie with Fedora Extras requirements 56bf12b Fix processname tag 7d711c0 Allow passing of options with 'once'; fix processname tag, add config tag. 5590e3e a couple small changes; the most significant is the addition of a class-level "eachattr" method, to avoid all of the calls to attrclass and attrtype 30bf65f Fixing a significant performance bug in file recursion, and trying to help performance a bit in attribute handling on types d5af359 Rewrote client init script since puppetd is now a proper demon. 6637bb6 Install bin/puppet into /usr/bin/puppet, not /usr/sbin/puppet, since normal users are supposed to be able to run it 931c159 Fix rpm packaging; include conf/ in tar ball, use conf files from tarball in spec file 4ada6af Fixing class storage -- it was not working for nodescopes 8db35ec Caching Time objects instead of numbers, since Bignum does not seem to be YAMLable 96b761b Fixing waitforcert so that the client can actually add the certs once it receives them c7f9942 Adding release tag REL_0_12_0 cf82cfa Updated to version 0.12.0 282cfcf Updated to version 0.12.0 1186069 Small mods to the packaging stuff 87904d3 RPM release is almost entirely there, it just needs to be integrated into release management 9b2afcb Fixing some logging issues ae2575b Adding the event-loop stuff to the repository and switching to using it. Also, breaking many classes out into their own class files. 18e8e74 Committing most of the scheduling stuff. There is still a bit of work to do in terms of how puppetd interacts with scheduling, but the bulk of the work is done. 258114d Modifying docs, and adding scheduling hooks 0cb51f3 Fixing a small checksumming bug, reorganizing the client stuff a bit, and adding freshness checking for the configuration, so the config is recompiled every time nor is it downloaded unless it has been recompiled f49b103 Updated to version 0.11.2 c372a7d modding changelog for 0.11.2 6bab167 Made lots of small changes, mostly to help usability but also fixed a couple of key bugs ed39be9 Fixing most types to allow no statements 3d458ef Updated to version 0.11.1 c3df525 modifying changelog for 0.11.1 060b8bd Adding openbsd packaging support ada3aee Fixing problems where objects were passing @parameters[:param] objects, instead of specifically retrieving the value f36c7d1 Updated to version 0.11.0 3700b37 Adding an "ensure" state where appropriate, and significantly reworking the builtin docs. 92a780a Added "ensure" state to some classes, and added infrastructure for it to work elsewhere. 83906a2 Adding sshkey class plus tests, and adding "aggregatable" methods to type.rb c67fb7b Adding another host to the test lists, adding some test data, and modifying how hosts parse ad9d365 Adding a bit better logging and checking to file access 87b3bb1 Moving ast classes into separate files 1d4638a Added "finish" method, using it in Type.finalize, and moved autorequire and setdefaults to it. a6e367e Changing host and port aliases to also create Puppet aliases. This involved futzing around with the attr* methods in Type.rb, to make sure states are always checked first. c8b6401 Fixing up the parsedtypes, fixing Type.eachtype to ignore structure types bbf2c54 Adding /etc/services support 0c17149 finalizing cron and host management, hopefully df6ff9e Abstracting host support so it should easily support other types 5309479 Abstracting host support so it should easily support other types 3f15e38 Adding initial host support. I can promise that this will soon (hopefully almost immediately) be abstracted to make it easy to add new file types. f420135 Converting transport format to YAML instead of Marshal, and caching the file in a YAML format, also. This required a significant rework of both Transportable classes. Lastly, I am also now caching the list of classes in a class file in /etc/puppet. 8aa331d Adding further notes about openssl 4092a78 Fixed a couple of warnings, fixed a critical bug having to do with case statements (where there is only one listed option), and did a couple of other cleanups. c5782df Adding "content" state to files, and string interpolation handles escaped whitespace characters. b0ea70d Adding 0.10.2 stuff 6b6c49b Updated to version 0.10.2 1cf05ff Services now work at least somewhat on solaris 10, and service testing is pretty different. 4c4f530 Fixing dependencies to not depend on file order. Added Puppet::Type.{finalize,mkdepends,builddepends} 21410a2 Fixing documentation generation, and fixing aliasing so that objects can safely be aliased to themselves 29b00fb Adding "alias" metaparam; you can now create as many aliases as you want for any of your objects. 411ab22 Adding autorequire to files, and added the cwd to the list of files to be required for exec. Also, exec catches inline files and autorequires them. 97fb6c9 Adding generic autorequire mechanism, and thus removing it from exec 11b5463 Adding a requires? method to types, fixed the bug where exec fail when Puppet is downloading the script to execute, and modified "exec" to autorequire any managed scripts 932b783 Updated to version 0.10.1 854f16b modifying changelog for 0.10.1 89d0050 Adding Sun support and fixing the last remaining bugs related to the daemon changes i just made 45ac512 Supporting puppetmasterd running as a non-root user, and doing some basic message cleanup 45c91e3 Adding some extra feedback 7616289 Fixing init path default e5ac196 Adding some consistencies to the executable tests. All exe tests now pass on OpenBSD, although the only real problem was that ruby was in /usr/local/bin. dccafc7 Updating Puppet to work with the new Facter b7974b5 Updated to version 0.10.0 48031dd Describing 0.10.0 changes 48ba030 Modifying hosttest f00a7db All tests pass now, although the lack of service support on os x means that i have now disabled services on it 4275227 updates cbf10c5 Merging changes from the head of the rework1 branch, r 784 23f982e Undoing the merge that happened in 785 1d73973 Merging in refactoring from version 774 into version 784 3ba696d updates cb51688 converting waitforcert to an int cedefab adding ftools require statement to install.rb 7594411 Adding a host test task 86cc467 renaming the module, so it behaves better with people's svn clients 2994cbc fixing rakefile 3b9c9be Updated to version 0.9.4 6af79cc Removing tests from the list of tasks e611f2c adding some better readme stuff b532a30 adding things to the change log, and modifying the order of the steps 584652c Disabling most documentation generation except for the API docs, and wrapping the StatusServer in the xmlrpc check 0e0fdac Updated to version 0.9.3 e2e2fb3 some updates to the typegen stuff, even though i may still not use it 5302921 Fixing two reported bugs in cron jobs. Cron jobs correctly change when fields other than the command are updated, and they do not continually refresh when the command has trailing spaces 592c24d adding some comments f9f84bf removing "host" as a keyword; it was an alias for "node" 4eaf13a converting storage from Marshal to YAML 3c14db1 adding Util.lock, and switching storage to using it 5ce5b95 removing any direct references to /tmp in tests 84bf289 fixing files being put directly into tmp a03b03f removing ruby shebang at the top of all of the files 2a5bbd0 final updates for 0.9.2 58ef35d updates dd727ef removing marks 9f942b0 fixing tests to work from any CWD 58c0df1 fixing incredibly annoying bug where os x returns stupidly large uid when uid is below 0 42c077f updates from os x f815654 updates 5082132 adding cfengine module, which required passing the cfengine classes all the way through the stack to the scope c205bf6 fixing filesources so that the first found file is copied, and adding a test case 6960034 fixing puppet to default to using the console for output d5bd1bc Mostly fixing exec so it either captures stderr or runs as a user; Process.euid was not working correctly a4562bf Lots of refactoring, and added the capture_stderr method 42a9d9a Adding "isomorphic?" method to classes, and testing for isomorphism before throwing a conflict 300a163 Further progress towards the next release. Lots of small bugs fixed, the paths look much better now, and Transportable is much cleaner. 14d8186 Making paths work a little more intelligently 0335743 adding config file stuff, but not using it yet. I have decided to release the next version without them. ba76eb2 Cleaning up tests resulting from the changes to the parser e605a5c The language now verifies some resemblance to closurehood. I now only need to fix the library to expect this behaviour. 526deef Fixed merging of state values, but I have not yet solved merging of parameter or metaparam values, since they are quite different. 9e1c63a Protecting from bug in Syslog, and fixing some more log messages 76176a9 central logging is fully functional now, but it is painfully slow, so it is disabled by default b6c63f6 Central logging now works, although there appear to be a few kinks to work out. 0ae5e33 Adding logging methods to all Puppet::Element instances, and converting all instance log statements to use those methods. Additionally modified logging to take advantage of this by including the path of the logging object in the output. Logs will still need some cleanup to avoid duplicate information. 0d3db79 adding tags and path accessors a8645a4 Further small bug fixes towards running puppet on my network df8dbba fixing sources so that they always have a corresponding checksum state e8912d5 files and directories are now created as the correct user and group if they are set 1b74e8e The Puppet::Util.asuser function now works; had to slightly modify user.rb to make checking work. In addition, exec.rb now takes advantage of it. I also decided to make a small change to type.rb -- validstates now only returns state names, not names and states. 32d89ba fixing certmgr tests f732880 Getting rid of the tc_ prefix to test cases 8fe558c general cleanup, as i move towards running it locally 5ab12f9 fixing path to facter f036778 Apt-cache was showing more information that I thought, so I had to redo how I was collecting the most recent package version a232e5c Made tweaks here and there to get it running better on my local network. I am inches away from that happening. All tests pass. a6d0292 adding better docs to packages 3cd8ee9 "latest" is now a valid value for packages, and yum support is also included 66db3d8 making service changes; it is still basically non-functional except in the degenerate case of using "init" 194dab3 Adding some semantic tagging. It is not exactly full-featured yet, and it is not used at all, but it was sufficient for some proof-of-concept stuff in preparation for the conference 55d7bbd adding tag support to scopes and the transportable class e563189 switching test classes back to modules -- making them classes causes too many empty tests to run every time 9d3aaad removing the redundant pfile from the pfile state file names ed42371 Switched @should to an array, so all objects can now handle that. I have not yet gone through and done the other cool things that can result, but that will have to wait until tomorrow. Also, I moved all of the pfile states into separate files, since the file was getting unweildy. 6af0e0e added overrides ability 0d6241c switching all relationships to be centrally maintained and to use symbolic references, rather than literal ones; also going through and making all tests pass again after mucking with services a96bdac Okay, services are now managed, um, entirely differently. I have the beginnings of a system for supporting different service management frameworks, although I currently only support and init scripts 47ba186 adding a test for includes 9464224 moving specific packaging support into separate files f48f14a fixing behaviour when files are missing 5bb8c4a fixing output in noop abcac81 Rearranging the packaging support a bit -- it is now more clear and significantly easier to understand, maintain, and enhance. 8211df0 Many, many changes toward a completely functional system. The only current problems with my home config are that apache's stupid init script does not do status and that packages are not working as non-root users (which makes sense). d20ac8e Hoping this will get rid of the directory being printed 4c13c10 Nodes now support inheritance, for better or for worse. 781c69b The new "include" syntax works now. 400b103 fixing "ignore" documentation to parse RST correctly. 5590148 Okay, all tests pass again. The work done on nodes will take a little while to clarify and such, but it should work pretty well. f7d9b83 I am still somewhat in mid-change, but I have made the biggest changes to making nodes work correctly. The core code works, but I still need to fix my various test cases 03f5733 adding a ParsedFile class to handle figuring out whether a file has changed f757556 converting tc_relationships.rb to the test base class 5dc3cb0 Okay, significant change -- classes no longer accept arguments (which makes things simpler but encourages the user of global variables, which is bad), and classes are finally singletons, meaning they will only ever be evaluated for each node a single time. I still need to make nodes work correctly, but that is going to involve modifying the parsing system and a bit more 0747b4c adding stinkloads of comments to ast.rb (I am trying to go through and better comment my code over time), refactoring some of the AST classes, and working towards a more sensible class/definition/node distinction d6982d5 adding some new language tests 48155b8 added log, metaloglevel and @metaparams to support setting loglevels 041ca4b switching log.rb to raise Puppet::DevError events 16c9f83 My cfengine2puppet config now entirely parses. The biggest problem I ran into is that my glob-based parsing was only returning the results of the last parsed file, instead of collecting all of the results. 23d3c93 adding --noop to puppet executable, and removing some extraneous comments a345931 Successfully parsed my entire converted cfengine configuration; these are all fixes for bugs i found as a result. I have not tried to execute the configuration yet. a8bdada Users and groups now work on OS X. I had to make some key changes to how transactions and state changes work -- the most important is that it is no longer an error to try to sync a state that is already in sync. I could not find another way to handle all user states being out of sync but the first state actually syncs everything. 6654c4c fixing users on normal machines 106d397 Users and groups now nearly work on normal machines and on os x, and I think I have a decent platform for expansion to some of the other important elements like hosts (probably most important after users and groups). Some tests are still failing on os x, because netinfo sucks, but I will hopefully be able to figure out a solution soon. Stupid OS X and NetInfo. 42deabb all tests for users and groups pass again on fedora, debian, and solaris e0d1d31 temporary commit so i can move to my workstation; groups and users are way b0rked 29c291a Creating a "change_to_s" method on State, so individual states can override and and determine how they get printed 80a2808 Cron is now fully functional and tested on 3 platforms. In order to make it work, I had to do some modifications to TransObject#to_type and Type.create, but all tests pass now. Type.create is now handling errors on creating objects, so if you try to create an invalid object you will just get nil returned, rather than receiving an error. e25e8c6 adding keywords aebfef0 adding keywords 11d3d24 cron is working, but i want to write quite a few more test cases 036ba7a small fixing to merge(), and changing output of statechange 093963e finishing up merge method ac0454a making "Type.new" private, and switching to "Type.create", so that i can merge new objects with existing objects and such; converted all files, and tested them f7116e5 switching FileTesting to a class, and modifying test suites as appropriate 7861269 removed directory output bf701dc adding extra checks to make sure networking is secure, and refactoring a heckuva lot of test 0c97bb1 fixed problem using arrays 897de46 cleaning up tests a bit; mostly just making sure tests clean up after themselves, but also doing some rearrangement and fixing a list() call 6767dd2 debugged ignore in fileserver, added tests to ignore tc and fixed server.list in tc for filesources and fileserver 39aaa99 fixing some tests 6c6ff03 moving some testing and debugging around 192c07b fixing non-netinfo group 9ebb767 fixing non-netinfo group 669ae38 modifications to eliminate code duplication in state creation, mostly to support cron ae00500 preparations for supporting netinfo in users 610f95c switching groups back to having namevar be a parameter instead of a state 2a6710b fileserver still has a bug ec66034 added ignore to fileserver and pfile 3d5f5a1 adding cron stuff; it does not work at all yet 0d3024f adding globbing to import 5438eff removed comp.sync and replaced system mkdir -p 79961e1 added test case for ignore 157953a added ignore to pfile 51948bd adding example configs d43dc86 tracked down some sticky bugs related to having false values and empty strings; all fixed now, and all tests pass again, including the new tests that cover the bugs i found aca4cf8 fixing error handling so that failed objects are completely destroyed; they were receiving events even though they were supposed to be gone 0a4e392 fixing component flattening and sorting; it was not working for cases where objects inside of components had dependencies from other components 58ca9d1 adding snippet test to verify correct behaviour with missing exec path; also, adding "creates" parameter to exec f9df523 intermediate commit so cron is not loaded 4dd1dd8 adding more comments from talking to andy 3306cc7 incorporating comments from talking to andrew 29fa170 incorporating comments from talking to andrew d0eb566 some small fixes ee1eba2 small updates f69abc9 adding cfengine to puppet parser, but it is not even remotely close to functional yet 8f7b191 fixing groups again after getting them to work with netinfo da6590d group management now works on os x, although it is six shades of really nasty. Netinfo is a nightmare. 3726cee adding debug setting to the top-level test class 99db7bc reverting changes in the reverse order of what i executed them 8f0ef3f removing debugging 2213430 making states capable of being the namevar 923226e all tests now pass on solaris 10x86; i had to do some stupid hacking with base64 for it to work, and i am working getting a much better base class for all test classes edc392d adding a break if randomization takes too long 4cc12a8 finishing up user and group support for now 9e4ed0c rearranging some documentation 24b1c67 adding scopetest b77d295 adding user and group classes (although user class is not yet functional), and added "is(state)" and "should(state)" methods for retrieving the respective values on a specified state 4f2812a changing rollback() to do event handling also 72638e6 adding simple newcomp() method to base test class 795a5a0 adding all of the work necessary for tidying f7a5f87 removing DEFAULTPORT stuff 21244f4 fixing small bug in setting default port c0c958b changing some documentation methods around 3fb6f49 rearranging puppetdoc to not separate states and parameters, and to allow undocumented states 75ec054 committing initial tidy stuff, so i can move to the laptop 0d01770 cleaning up obviated methods 747c3f6 defining $name in each component scope, and adding test case f49ffa3 defining configstatted 750b880 removing if-related keywords 008cea5 fixing error calls e1bf0e1 fixing bug where remote server name is ignored e1de002 Fixed small bug that caused the config files to be parsed on every connection 5c0fb02 fixing documentation for file source state 6a96fcc adding file reread to master, although it is plenty hackish cff3a5b fileserver config file now reloads when it is more than 60 seconds out of date 998b415 correcting documentation on autosign in puppetmasterd, and switching the autosign.conf file to use the same authstore as fileserver.conf 7f274a4 catching a potential security problem -- requiring that "path" always be set for a fileserver mount 8a99636 adding a "--noop" option along with a test case for it f65563d changing default hostname to "puppet", and adding --parseonly option along with a test case for it 0629065 fixing puppet to use correct method and adding a real test case for it 82ac86e changing default manifest location to /etc/puppet/manifests/site.pp 3087f85 changing version number for beta e28e11b removing need for zip in rakefile fc20167 UPdated rakefile 914764b As Christian requested, I am no longer downcasing facts I receive from Facter. Also, removing some outdated files eab7314 bumping up the default log level for changes from info to notice cc48367 I did not have good puppetd tests, so I missed the fact that I was calling the wrong class in puppetd. Fixed. 65d1375 disabling print of library warnings, since webrick and openssl have far too many warnings for comfort cdfaa5e adding RDoc::usage documentation to all executables f279535 This should be the commit that brings us to Beta 1. All tests pass, although I get some (gracefully handled) failures in tc_metrics.rb, and there is now a config file for the fileserver module, including authorization specification for it. I have also reworked error handling in the xmlrpc client and server so errors should propagate more correctly. 28be88c Adding authorization store, initially just used for file serving but eventually for all authorization, most likely 814a9b0 adding config file parsing 9ec321e recompiling parser after removing extraneous debugging 773be96 Added GPL license 48a2e0f making array-as-name work in the language, and adding some more test snippets 66b3355 Certificates now verify! 386ebee replacing if statements with case statement, and adding defaults for both selectors and case statements 583a9c6 remote filecopying now works 5b20c92 Have done a significant reorganization of how clients work, also, along with some interesting trouble shooting on components ba51e70 fixing checksum generation -- i was causing some weird bugs by using the same indicator in different cases, and i added a test for invalid checksum types fb3cff7 small comment change dde841f Created a Handler base class for all of the server handlers, which allows a lot of the manual work to now be automatic 49e3e37 splitting normal file tests and source tests into different files a8fff85 ignoring remote file owner when not running as root f68fe00 moving all server handlers into a specific server subdirectory 129dad7 removing obsolete file 51294a0 moving rake file to the trunk dir 6029ef7 Moving all files into a consolidated trunk. All tests pass except the known-failing certificate test, but there appear to be some errors that are incorrectly not resulting in failurs. I will track those down ASAP. e87eb58 adding test to verify require does not kick off an event 4741eef Execution order is now based on dependency relationships, and those relationships correctly propagate up and descend into components. There is also an event test suite now, along with a (currently simple) component test suite. 63309c3 fixing test base class f026884 just fixed an incredibly ugly bug where @parent meant both parent array and parent class 78939cc passing file and line to all objects 51ffd6f trying to create a testing base class 54e5eb4 moving 4f0750b adding 9f84742 all tests pass except a certificate test i do not know how to fix 163db7c a basic first test of puppetmasterd now passes a3e03e2 all non-executable tests pass now d49a98e further progress towards passing tests 2be10b5 all tests pass except those related to puppetd; i am going to significantly reorganize how serving is done 6eac358 updated version number to allow gem install 8d71cd9 Added rake for Puppet, '--version' to puppet and test case for Puppet.version cfaee58 more progress towards a functional daemon 3cb04cb setting up http to log to a separate file adc10d8 setting up http to log to a separate file c7380c9 renaming cb1956d adding daemon helper module bb4b5a5 done a lot of work on certificates; all tests except one puppetca test pass e2e2474 removing obsolete files 0f7d185 fact.rb is obsolete af1b979 function.rb is obsolete fc0ba4b fixing directory mode creation 5bcae96 renaming again 2687652 renaming openssl to certauthority aacd780 removing comments and making @csr a class attribute 4b5c5bf fixing error output c0b0975 adding creation rollback f654f2d returning transaction from client.config() a6a1148 all tests now pass on os x f3c1487 adding better error handling for missing paths 77e1cf0 fixing verbose output to be better and easier to manage 9c7c71c passing state, so more info can be extracted afcb2ee adding a bit more output bc3a433 committing recent changes for testing 7b14e39 a bit of refactoring, and a bit more functionality 0fce775 okay, switch all cert management to the library, instead of using the openssl binary a1d206a moving 6ba0e10 certificate management; yay de4e5bf oops; removing errant notice msg 223cc9f cleaning up the defaults system even more 74dac8f adding some consistency and tests to the whole puppet defaults system 8bf85e2 adding tests for puppet defaults d14e1b4 uh 4af2559 renaming to avoid name conflicts 5fa0c20 all tests pass again edc2842 local filebuckets are now tested with files e041c8a basic filebucketing is now working cf37c06 Moved documentation to project website. 9f2aaac more changes to documentation 952a76c working on documentation 54895da Updated README, moved the old one to documentation/ 2d1e643 Updated doc/big-picture doc/structures, Added rst formating to puppetdoc, and added doc strings for types, states, params, and metaparams. 700b965 dpkg/apt package installation and removal now works e685ddb fixing statefile creation 01666f6 fixing lib loading test 075ffd7 making changes necessary to pass tests on all available platforms b5f6a54 fixing solaris packaging ffe318c fixing packaging to work with rpm, too 64b55c1 creating FileTesting module in puppettest.rb, and getting recursion working with symlinks 9d8f754 adding some attributes to errors dcbbef7 adding symlink back as a separate object e078329 adding symlink back as a separate object 60965c5 removing silly message f624cd5 making the console output slightly less redundant 848df27 okay, last try on sources hopefully -- no failures, and basic functionality all seems to be working cc67845 fixing package listing 326b4ad fixing exec tests to work with new event stuff fa6569d okay, sources seem to be much more understandable here -- only the recursion function is recursive 5351eb9 adding some error checking 60e02de not dump/loading for local client/server 8ec8c8d sources now pass all tests a33d5d4 everything seems to actually be working now, including getting the correct file list back 2d34f8e Ha! finally got recursion and sourcing working together bc38169 redoing how arguments are handled in type.rb -- it is much cleaner now 973385f first versions c577e57 reducing debugging 99eedb3 reducing debugging 8f718da recursive file copying now works even with some limited number of errors aa59473 okay, file sourcing actually seems to work now 9a5477b checkpoint commit 0e94644 moving setpath to parampath=, as a param method 3a67efa adding "=" to metaparam methods, and allowing types to use methods for params de91dbd temp commit before i change the whole way that i am doing source + recursion 9ba72f5 removing, since pfile now has link functionality 311218b removing, since pfile now has link functionality 6f4f821 downcasing the values of all facts b1f3fb8 switching facts to be auto-provided by the client 357afbb removed type.method() syntax, and replaced it with Type { default => value} syntax -- i still need to add test cases f38bdef changing path stuff to be service-specific 84f721d modifying failer stuff 3d725d9 adding more validation methods f2b6762 reducing to one statefile definition ad9913b converting debug() back to Puppet.debug() 13945c5 converting debug() back to Puppet.debug() 668b9de converting debug() back to Puppet.debug() c04b337 converting debug() back to Puppet.debug() 3c4181c adding methods callable from language 88071eb making rpm the default package manager for all linuxes, with exceptions allowed ca87010 defaulting to rpm for everyone i don't know anything about ebe779f adding rpm as the default type for Fedora fbafa84 updates b9c064e Updated install script to include all bin files. 96c37e3 using old name method, since it is required to make names unique given how classes currently work e97e2d9 fully qualifying warning method, because it is not being imported for some reason a984d66 disabling metrics tests on machines missing RRD 78bcfdc adding path stuff everywhere, although i had to keep component names kind of funky 99a9f4b adding link functionality d6e2102 toying with some doc stuff 3f9bb25 removing an obsolete VERBOSE setting 5ff4b02 removing some debugging bf09132 lots of refactoring cc78949 in the middle of a bunch of refactoring; committing so that others can try to replicate the segfault i am seeing 1f2c866 doing some refactoring e19ca97 converting to "include Puppet" and not qualifing output methods 8177700 hopefully, finally resolving recursive file creation problems 01a9905 moving namevar translation to a method fffc09c swapping console colors 2062aac changing comments 75e27cf cleaning up bugs in the tests, and adding more error checking around the bugs 0417fb5 adding refreshonly parameter 6e9975c found a bunch of bugs in Puppet::Storage, plus some bugs in how file recursion was being tested and thus some real bugs in the system 256b84e adding a to_s method d14dc32 fixing storage class; it was not actually correctly retrieving state from disk 96f3980 adding Puppet#recmkdir utility function, and making sure Log and Storage create their own files and directories as necessary 649d59a adding some more tests for recursion plus checksumming, because they are somewhat incompatible for directories cb95dc7 ok, verbose => info 459287c duh, somehow added Blink calls in there... 600f272 adding cwd parameter to exec 4157fa0 exec stuff all works now, end to end 2c1f637 fixing some nasty bugs related to parameter checking 05d59fd fixing a nasty bug related to recursion and nonexistent files 5b7fd63 fixing some nasty bugs related to parameter checking 9e0ab28 adding string vs. symbol testing 8f1da62 adding logdest config cf817e7 changing Log.destination to Log.destination=() d36b4c0 disabling debug by default 420062d changing Log.destination to Log.destination=() c718044 adding the exec stuff 3441b82 finishing rename of file class f250d92 renaming to remove naming conflicts 26bc12b further output cleanup, and fixed the problem with tc_server.rb leaving sleeper processes lying around bb7e283 fixing debugging to severely reduce output but allow it when debugging a specific service c16ca53 changing warnings to debug 00343e3 basic rpm stuff now works 2b2975f fixing metrics tests to work with everything else 0ac91ef correctly returning events 0c4254a metric testing and graphing now works in the library, although nothing has been done in the language 573d301 adding some more tests for loglevel stuff 0ba9d71 adding rpm as RedHat package manager b6b1f5a logging now exactly supports the list of levels that syslog supports, and multiple destinations (syslog, files, and console) are now supported e3c3283 renaming message.rb to log.rb 2b3b15e temporary changes to prepare for a rename 8395639 puppetest.rb should probably be puppettest.rb cd48af7 renaming blink to puppet 60783f0 renaming blink to puppet 8f95084 renaming blink to puppet 6f07413 you can now at least test for whether a package is installed, although you still cannot do anything if it is not -- the barrier is knowing how to get the package 65c2a69 adding validparam() to Blink::Type 500a135 fixing file.rb so it works on Darwin, also 90b9b2d updates 3f0687b updates 5324062 adding new classes and tests 0dad57a md5 summing now works, all the way through! d1f2187 checksums now work, but not all the way through yet 18d755a Rahh! component dependencies now work! 922994e everything passes all of the tests, but i do not think the component-level metaparams are working cdfdfb9 adding some more tests for Blink::Type d42efbe done some more work on making components act like normal objects, but now i need to figure out how to spread events up the tree, rather than just point to point 0ab9685 dependencies now work, although you cannot yet specify them for components 2b97b47 there are now explicit tests for transactions, including rollback, and more tests involving querying b46135f adding simple query syntax stuff 7fecad3 i now have basic events: you can only specify simple object types as requirements, but hey, it works -- refresh() gets called 02f1185 renaming events to event d70229b update before rename c0d86a4 intermediate; no extra functionality yet, but moving away from discussing namevars outside of a given class d86e479 All tests pass again, and i have added Type.allclear to remove all existing type instances, thus making it much easier to run all of the parser and lexer and then server tests; i have also added remove server tests ebc02a8 okay, everything works again, although it still might be a touch fragile 5477f36 i am giving up on fileparsing for now; it does not work yet 576a031 okay, bin/blinker works again, and i have modified both client and server to be a bit easier to use non-networked 03741b4 we now have networking it is very basic right now: the server gets passed a file name, and the client gets passed the server to which to connect. The client gets its config, evaluates it, and exits 4a4438d okay, implicit iteration now occurs at least at object definition time 4a9c1a2 enabling mucking with debug and noop f91451d yep, arrays are always passed d0d8df9 final fixes on passing methods through to types 3a07a4d typesettings now correctly pass all the way through and can only run specified methods, using "allowedmethods" 12418e1 the tree transformations work throughout the whole system now f9a223c transformations from the client through to the transactional execution now works! 00a620d unmoving 135134c updates 4da3b51 moving 259692c moving 3b33738 switched to just passing scopes around, instead of passing the interpreter object around 529b9a7 file recursion now works 4e03ed1 i can now execute simple scripts manually! ec88acf i can basically actually do work now 42dadad moving 817f571 adding lots of verbosity, and starting to actually make sure things execute -- tests are failing, tho 2cce619 basic components now work 49da910 we now have nested scopes, and we've got the beginnings of components, although many tests are currently failing 7689d19 simplifying argument parsing in Blink#message 08d4260 initializing @noop in element.rb 865dffe dealing with having moved the examples to the language area a1a7ae4 moving to the language tree 85b9f91 everything works again, including components 9ea0c30 making components work 1f95fe2 moving component.rb to type/ 7df572b removing most functionality, since type.rb already has it 565554f finishing reorganization of base classes and such; there is now a clear tree heirarchy, with parents and children -- all tests pass on the types, but there are still some issues with client/server stuff 9843da6 cleaning up object tree and adding a simple virtual base class for both types and states d0c6b0c adding transactions 89d2381 moving event info out of type.rb 3c6c0f7 removing elements.rb e452f00 preparing to remove elements.rb cf08be1 renaming statetree to elements b6d0f0c first version of finalized base classes f984192 noop seems to basically be working, but i need to reorganize the whole type/state structure -- that is starting after this commit c01bc81 using the example syslog.conf instead of the one in /etc 57194eb adding some example debian configs 6cd71f8 filetypes now support multiple record types, and newlines can be escaped 7378765 completed the first step to enabling multiple record types in filetypes 490854a the client now executes "evaluate", which might not be a good thing -- i am getting failures if files do not exist, for instance 6127e43 filerecord.rb: duh, fixing a small debug string printing bug 371235b reorganizing the FileType/FileRecord into a base class (TypeGenerator) and basing them off it 7deba97 adding typegenerator baseclass b18d071 fact.rb is no longer a subclass of Blink::Type 43a63db fixing filetype and component to take into account changes type interface.rb and type.rb a16493d type.rb: adding methods to simplify type initialization, and adding eachtype() to simplify testing 69a1276 adding some tests on the interface methods in Blink::Type de360c9 renaming objects to types, since they are just subclasses of Blink::Type b872b72 renaming types to type everywhere; all tests now pass a50d461 changing types to type internally c0bc63f moving types to type b85ac7b renaming types.rb to type.rb 9d163d0 changing Types to Type 68409ef adding cb5f54c deprecating interface.rb 974969d final changes before deprecating f8b08b2 adding an fqpath method to state.rb ae13f00 adding name information and a FileRecordState class a489639 moving some methods around 14c2308 adding markers a1bd01d changing type[param] to return the state instead of the value 41b7fbd renaming oparse to filetype 98374bc renaming oparse to filetype 8ab03d0 renaming oparse to filetype c2e83c5 renaming oparse to filetype 2360201 adding some comments 3904d2e fixing most of the function call stuff 13f16b6 the client is now successfully creating objects from the hashes passed by the server 6140bee making fact a real type d94d7a3 moving state.rb to types/ e86cf4c renaming "value" to "is" d8e8b7b renaming state, and adding modification class c0c1f1a renaming attributes to states 6dce8fc renaming objects to types a90af49 renaming objects to types 5e19d7d renaming objects to types afd4349 updates 67fae6d updates b4bb680 removing link when done c6755f5 adding class collection and naming stuff e5676ab testing keyword f3d1118 removing some excess libs from blink.rb 585c4fc cleaning up the start script 61f8434 cleaning up a bit 52fe914 removing evaluate test 69651dd removing bogus output 8fd8d62 removing output, and fixing path so it is not hardcoded 9da1485 temporarily removing inclusion of time in output 4d7bbaa creating extra message methods 0187bf7 removing execute bit 6a82d07 adding readme 3824817 copy test script 67b7b34 okay, all tests pass again 35f742c adding client stuff to the library tree 02ed7d3 reorganizing tests 8bc935e adding comments a0d0fa8 adding 11b0374 moving client.rb to the library tree cb785bd moving client.rb to the library tree 8788552 fixing [] method in function.rb c86a770 adding operatingsystemrelease 21ca565 renaming tc_functions.rb, and adding to ts_other.rb 1aab204 functions mostly work c45c5c4 renaming 06734a9 updates so i can rename 9bf82b6 adding package source, initially including only files as a source e40d5f2 returning value instead of fact 9fa899d reorganizing 4642515 reorganizing 5807dce reorganizing 6ee8b4e reorganizing 5416017 reorganizing 54e9b5e adding structure diff --git a/CHANGELOG.old b/CHANGELOG.old deleted file mode 100644 index bb0be94ba..000000000 --- a/CHANGELOG.old +++ /dev/null @@ -1,1705 +0,0 @@ -This is the legacy CHANGELOG. Please see the new ChangeLog file and the ReleaseNotes page at: - -http://reductivelabs.com/trac/puppet/wiki/ReleaseNotes - -0.25.0 - Fixed #2280 - Detailed exit codes fix - - Fixed #198 - Puppet man pages added - - Moved puppetd, puppetmasterd, puppetrun, puppetca from bin to sbin - - Fixed #2071 - Updated LDAP schema - - Fixed #1849 - Ruby 1.9 portability: `when' doesn't like colons, replace with semicolons - - Fixed #1910 - Updated logcheck regex - - Fixed #1879 - Added to tidy documentation - - Fixed #1881 - Added md5lite explanation - - Fixed #1877 - Tidy type reference update for use of 0 - - Fix autotest on win32 - - Doc strings update for REST - - Fixed #1840 - Bug fixes and improvements for Emacs puppet-mode.el - -0.24.8 - Fixed #2077 - ralsh user broken on OSX - - Fixed #2004 - ssh_authorized_key fails if no target is defined - - Fixed #1629 - incorrect permissions on ssh_authorized_keys created files - - Fixed #2000 - No default specified for checksum - - Fixed #2026 - Red Hat ignoring stop method - - Added ext/dbfix.sql script - fixes common errors in stored configuration databases - - Fixed #1963 - Failing to read /proc/mounts for selinux kills file downloads - - Fixed #2025 - gentoo service provider handle only default init level - - Fixed #1910 - updated logcheck - - Fixed #1871 - Sensitive information leaked in log reports - - Fixed #1956 - Cleaned up variable names to be more sane, clarified error messages - and fixed incorrect use of 'value' variable rather than 'member'. - - Fixed #1831 - Added sprintf function - - Fixed #1830 - Added regsubst function - - Updated up2date and service confines to add support for Oracle EL and VM - - Fixing #1948 and #1953 - augeas ins bug: wrong number of arguments (1 for 3) - - Fixing #944 - changing error message from warning to info - connection recycled - - Fixed #961 - puppetd creating too many/not closing TCP connections - - Fixed #1959 - Added column protection for environment schema migration - - Fixing #1869 - autoloaded files should never leak exceptions - - Fixing #1543 - Nagios parse errors no longer kill Puppet - - Fixed #1420 - nagios_serviceescalation not allowing host_name more than one type - - Fixed #1884 - Exported resources are marked as unexported when collected on the exporting host - - Fixed #1922 - Functions squash all arguments into a single hash - - Fixed #1538 - Yumrepo sets permissions wrongly on files in /etc/yum.repos.d - - Fixed #1936 - Added /* */ support to the vim file - - Fixed #1541 - nagios objects write files to clientbucket on every change - - Fixed #1542 - cannot purge nagios objects - - Fixing #1912 - gid still works with no 'should' value fixing ralsh issues - - Fixing the Rakefile to use 'git format-patch' - - Added README.rst file - - Enhancements to Stored Configuration performance - - Added Reductive Labs build library to tasks directory - - Fixed #1852 - Correct behaviour when no SELinux bindings - - Updated Red Hat spec file 0.24.7 - - Fixed #1920 - Shadow password corruption - -0.24.7 - Fixed #1804 - Added VDev and MultiVDev properties to the ZPool type - - Fixed #1496 - nagios_servicedependency needs a unique host_name? - - Fixed #1420 - nagios_serviceescalation not allowing host_name more than one type - - Bug #1803 Zfs should auto require the ancestor file systems - - Refactor #1802 Use 'zfs get -H -o value' instead of parsing output for value - - Fixing #1800 - tidy now correctly ignores missing files and directories - - Fixing #1794 - returning sync when it is already initialized - - Fixing #1750 again - All of the properties and now :ensure check replace? - - Deprecate the NetInfo nameservice provider. Use directoryservice instead - - Add macauthorization type - - Fixing #1703 - using a mutex around the sending of the tagmails - - Fix #1788 - allow rspec rake to run only some tests - - Fixing the AST constant warnings, using a variable instead of a constant - - Feature #1696 Add support for branded zones - - Feature #1783 - Add ZFS support - - type/mcx.rb Feature #1026 - MCX Type - - Fixing #1749 - Splay now hopefully behaves "better" for small values - - Fix #1741 - Add inline_template function - - Slight denormalisation to store a host's environment as a first class - - Added Rake :ci namespace and CI tasks - - Refactoring the thread-safety in Puppet::Util - - Removing the included testing gems; you must now install them yourself - - Refactoring of SELinux functions to use native Ruby SELinux interface - - Removing all mention of EPM, RPM, or Sun packages. - - Fixed #1496 - nagios_servicedependency needs a unique host_name? - - Fixed #1420 - nagios_serviceescalation not allowing host_name more than one type - - Fixed #1695 - Solaris 10 zone provider doesn't properly handle unknown zone attributes in newer releases - - Fixed #1776 - Trivial fix for gentoo service provider - - Fixed #1767 - Minor fix to emacs mode - - Fixed #1711 - fileserver test fails due to incorrect mocking - - Fixed #1751 - Mac OS X DirectoryService nameservice provider support for - plist output and password hash fil - - Fixed #1752 - Add an optional argument to Puppet::Util.execute to determine - whether stderr and stdout are combined in the output - - Added versionable feature to the RPM provider - - Fixed #1668 - puppetca can't clean unsigned certs - - Moved RRD feature from util/metric.rb to feature/base.rb - - Fixed #1735 and #1747 - Fixes to confine system - - Fixed #1681 - Add filesystem type check to test for per-file SELinux context support - - Fixed #1746 - Sync SELinux file attributes after file contents created/modified - - Replaced SELInux calls to binaries with Ruby SELinux bindings - - Fixed #1748 - Include spec directory in packages - - Fixes #1672 - unsafe crontab handling in Solaris - - Fixed #1718 - Added preseed to apt uninstall and purge - - Fixed #1739 - Added uninstall functionality to yum provider - - Fixed #1710 - Spurious output in test run - - Fixed #1667 - Documentation should specify natural language regexs, not Regexp objects - - Fixed #1692 - k5login fails to set mode when file is created - - Fixed #1660 - Added specific recurse values for tidy - - Fixed #1698 - All logs should now show up in the reports - - Fixed #1661 - Type reference: tidy should specify manditory parameters - - Fixed #1104 - Classes and nodes should set $name variables - - Updated Red Hat spec file for 0.24.6 - - Removed conf/debian directory - Debian packaging information - now maintained downstream - - Added augeas type - - Added support for @doc type and manifest documentation support - see: - http://reductivelabs.com/trac/puppet/wiki/PuppetManifestDocumentation - - Added multiline comment support - -0.24.6 - Adding support to the user type for: profiles, auths, project, - key/value pairs (extension to Solaris RBAC support added in - 0.24.6) - - Fixed #1662 - Configuration Reference still references 'section' - - Fixed #1460 - enhance redhat puppetmaster init.d script to easy start puppetmaster as a mongrel cluster - - Fixed #1663 - Regression relating to facter fact naming from 0.24.5 - - Fixed #1655 - Provider::Confine::Variable tests are broken - - Fixed #1646 - service puppet status does not work as non-root - on redhat system - - Fixed #1649 - Updated OSX package cleanup - - Fixed #1647 - puppetdoc -r providers now working again - - Fixed #1639 - uninitialized constant Puppet::Type::User::ProviderUseradd - - Fixed #1637 - With an inexistant (global) templatedir, modules - can't access their templates - - Fixed #1202 - Collection attribute matching doesn't parse arrays - - Fixed #1473 - Puppetd stops with error after puppetmasterd - is unavailable - - Fixed #1354 - yum provider problems with RHEL 3 - - Fixed #1633 - Added support for --detailed-exits to bin/puppet - - Fixed #381 - Allow Allow multiple overrides in one statement - - Fixing #947 - pluginsync no longer fails poorly when no plugins exist - - Fixed #981 - Removed 'Adding aliases' info message - - Fixing #1089 - Log messages are now tagged with the log level, - making it easier to match messages in the 'tagmail' report. - - Fixing #1098 - Multiline strings now correctly increment the line count - - Fixing #1614 - Environments no longer have to be listed out - - Fixed #1628 - Changed node search to use certname rather than Facter - hostname - - Fixed #1613 - The client environment will be substituted when looking - up settings. - - Updated puppet binary documentation - - Feature #1624 - Added RBAC roles to solaris user provider - - Fixed #1586 - Specifying "fully qualified" package names in Gentoo - - Fixed #1620 - Add 'sles' to Puppet confines when 'suse' is used - - Fixed #1585 - Allow complex 'if' and variable expressions - - Fixed #1564 - Saving File#checksum to state.yaml broken - - Fixed #1603 - Added support for running Puppet inside a Rack application - (mod_rails) with Passenger and Apache - - Fixed #1596 - Deploying file resources with ++ generates error - - Modified the group and zone resource types to no longer call - 'currentpropvalues' as a means of setting all values to absent. - There should be no behaviour change from this change. - - Modified the behaviour of resource-level 'retrieve' -- it only - calls 'retrieve' on each property if the resource exists. - - Fixed #1622 - Users and their groups should again add in one transaction - - Fixed #791 - You should now be able to create and find a user/group in one transaction. - - Fixed #1610 - Raise "Filebucketed" messages to Notice priority - - FIxed #1530 - ssh_authorized_keys provider does not crash anymore on SSH type 1 keys - - Added a number of confines to package providers - - Fixed #1609 - Added confines for the Gentoo, FreeBSD and - SMF (Solaris) service providers - - Fixed #1608 - Added ubuntu to defaultfor for apt provider - - Fixed #1607 - Added ubuntu to defaultfor for Debian service - provider - - Fixed #1045 - Multiple metaparams all get added to resources. - - Fixed #1472 -- defined, exported resources in the current compile - now get expanded correctly. - - Fixed #1595 - Internally, Property#retrieve is no longer called - when no 'should' value is available for a resource. - - Fixed #1588 - Fixed puppetca --clean --all - - Fixed #1584 - Added support for appended variables - - Fixed #1554 - Added support for multiple template directories - - Fixed #1500 - puppetrun not working - - Fixed #1579 and #1580 - errors in the Puppet RPM spec file - - Fixed #1572 -- file purging now fails if remote sources do not exist. - - Fixed #1521 -- ldap user and password are now used with the default connection. - - Fixed issues with file descriptors leaking into subprocesses - - Fixed #1568 - createpackage.sh - - Fixed #1571 - Puppet::Util::binary returns incorrect results - - Fixed #1553 - Puppet and Facter cannot both install the plist module into two different locations - - Adjusted hpuxuseradd user provider to confine to HP-UX and fixed HP-UX user provider path regression - - Fixed debug messages in package type - thanks to Todd Zullinger for this fix - - Fixed #1566 - changed password property of the user type - - Fixed debug messages in package type - - Updated Red Hat spec file - - Fixes #1455 - Adds HP-UX support for user type - - Fixes #1551 puppetmaster.freshness xmlrpc call returns incorrect type - - Fixes #1554 - Fix exception for undefined hostname - - Fixed #1533 - changed permissions for man directory - - Added daemontools and runit providers for service type - - Added simple rake task for running unit tests - - Added spec Rake task - - Fixed #1526 - Fixed leak in template - - Fixed #1506 - Removed storeconfig duplicate indexes - - Fixed #1457 - case insensitive match for error - - Fixed #1488 - Moved individual functions out of functions.rb into - lib/puppet/parser/functions directory. New functions should be create in this directory. - - Fixed #1508 - Added HP-UX package provider - - Fixed #1502 - Fixed poor stored configuration performance - - Fixed #1510 - Storeconfiguration fixed for Rails 2.1 - - Add the -P/--ping option to puppetrun, fixes #1501 - - Fixed #1394 - Added stored configuration clearing script to /ext - - Fixed #1442 - replaced use of Facter for report titling with certname - - Fixed $1456 - add proxy configuration capability to yum repo - - Fixed #1457 - removed confine warning - - A working script to create an OS X pkg out of the Puppet repository - - Fixed #1441 - Updated console colours - - Expose all puppet variables as instance member variables of the template wrapper. - This helps resolve redmine #1427, by providing a safe mechanism to access variables. - - * Implement Puppet::Parser::Scope#to_hash, which returns a hash containing all the - variable bindings in the current and, optionally, parent scope. - * Use that to set instance member variables into Puppet::Parser::Templatewrapper - * Report the time taken for variable binding at debug level, to help identify any - performance regression that is encountered in the real world. - * Rename the @scope and @file members of the template wrapper, to avoid clashing - with a scope variable exposed within puppet. - - Ensure that we consistently use either string #{} interpolation or String.% - interpolation, not both, to avoid issues where a #{} interpolated value - contains a % character. - - Feature #1476: Allow specification of --bindir --sbindir --sitelibdir --mandir --destdir - in installation - - Added feature #1241 : Improve performance of group lookups - - Fixed bug #1448: Puppet CA incorrectly writes out all certs to inventory .txt on each - certificate signing - - Fixing puppetlast to make it work with 0.24.5 / 0.25. Made puppetlast work on 0.24.5 - by using the YAML indirector - -0.24.5 - You can now select the encoding format when transferring the catalog, - with 'yaml' still being the default but 'marshal' being an option. - This is because testing has shown drastic performance differences - between the two, with up to 70% of compile time being spent - in YAML code. Use the 'catalog_format' setting to choose your format, - and the setting must be set on the client. - - Fixed #1431 - Provider confines must now specify similar tests in one call. - I.e., you can't do confine :operatingsystem => %w{a b} and then - confine :operatingsystem => %w{b c}; you'd need to do them in one command. - This now-obsolete behaviour does not seem to be used anywhere. - The fix for #1431 is actually just removing the tests that exposed - this change; the change happened when I refactored how confines work. - - Removed faulty interface type - - Updated /spec/unit/rails.rb test - - Fix #1426 - services on redhat are restarted again and status is - called from the Red Hat provider - - Fixed #1414 - Return code from waitpid now right shifted 8 bits - - Fixed #174 - a native type type for managing ssh authorized_keys - files is available. - - Further moves from the examples directory and ext directory - - Fixed #1397 One line fix, fail instead of log - - Moved debian to conf and updated examples directory - - Fixed #1368 - updated Red Hat init scripts - - Added message referencing ReductiveLabs build library - - Fixed #1396 - Added sha1 function from DavidS to core - - Fixed #1399 - the ldap user provider now knows it can manage - passwords. - - Fixed #1272 - if you provide a group name as the gid to an ldap - user, the name will be converted to a gid. Note that this only - looks up ldap groups, at this point; if you want to set an ldap - user's primary group to a local group, you have to specify the GID. - - Fixed #1226 - gems can now specify source repositories. - - Fixed #1232 - the rundir no longer specifies a user/group, - and there are now client- and server-specific yaml directories. - - Fixed 1240 - puppet will function more like puppetd if graphing - or reporting are enabled. - - Fixed #1231 - Exceptions during initialization should now be clearer. - - Fixed #1006 - puppetrun --class works again. I added the class - membership testing to the Ldap node terminus, and added tests, - so it shouldn't break again. - - Fixed #1114 - Facts in plugin directories should now be autoloaded, - as long as you're using Facter 1.5. - - Removed support for the 'node_name' setting in LDAP and external node lookups. - Fixed #1195 - Updated Gentoo init scripts - - Fixed #1367 - Updated Rakefile for new daily builds - - Fixed #1370 - removed test/util/loadedfile.rb tests - - Fixed #1221 - aliases to titles now work for resources. - - Fixed #1012 - templates in the templatedir are preferred to module templates. - - Fixed #707 - special '@reboot'-style cron jobs work again. - - Fixed #1360 - allowdupe works on groups again. - - Fixed #1369 - the init service provider now supports HP-UX. - - Removed support for the 'node_name' setting in LDAP and external node - lookups. - - Also removed support for 'default' nodes in external nodes. - LDAP nodes now use the certificate name, the short name, and 'default', - but external nodes just use the certificate name and any custom terminus - types will use just the certificate name. - - Fixing #1168 (for 0.24.x) -- automatically downcasing the fqdn. - Also requiring that passed in certnames be downcased; the setting - system isn't currently flexible enough to automatically downcase - it for the user. - - Adding a ResourceTemplate class for using templates directly - within resources (i.e., client-side templates). This would really - only be used for composite resources that pass the results of the - template on to generated resources. - - Exporting or collecting resources no longer raises an exception - when no storeconfigs is enabled, it just produces a warning. - - Always using the cert name to store yaml files, which fixes #1178. - The Master handler previously provided the support for the :node_name - setting, and that functionality has now been moved into the Node - class. At the same time, the names to search through have been - changed somewhat: Previously, the certificate name and the - hostname were both used for searching, but now, the cert name - is always searched first (unless node_name == facter), but only - the Facter hostname, domain, and fqdn are used otherwise. We no - longer split the cert name, only the hostname/domain/fqdn. - - Fixing transaction support for prefetching generated resources. - - Adding support for settings within the existing Facter provider confines. - - Moving all confine code out of the Provider class, and fixing #1197. - Created a Confiner module for the Provider class methods, enhanced - the interface between it and the Confine class to make sure binary - paths are searched for fresh each time. - - Modified the 'factpath' setting to automatically configure - Facter to load facts there if a new enough version of - Facter is used. - - Crontab provider: fix a parse error when a line begins with a space - character (fixes #1216) - - Instead of deleting the init scripts (with --del) we should simply - disable it with chkconfig service off, and respectfully do the same - for enable => true; - - Added ldap providers for users and groups. - - Added support for the --all option to puppetca --clean. If - puppetca --clean --all is issued then all client certificates - are removed. - - Resources now return the 'should' value for properties from - the [] accessor method (they previously threw an exception when - this method was used with properties). This shouldn't have any - affect functionally; it just makes the method equivalent to 'should' - for properties, but it works for all attribute types now. - - Modified the 'master' handler to use the Catalog class to - compile node configurations, rather than using the Configuration - handler, which was never used directly. I removed the Configuration - handler as a result. - - Modified the 'master' handler (responsible for sending configurations - to clients) to always return Time.now as its compile date, so - configurations will always get recompiled. - - Fixed #1184 -- definitions now autoload correctly all of the time. - - Removed the code from the client that tries to avoid recompiling - the catalog. The client will now always recompile, assuming it - can reach the server. It will still use the cached config if - there's a failure. - - Fixing #1173 -- classes and definitions can now have the same - name as a directory with no failures. - - Saving new facts now expires any cached node information. - - Switching how caching is handled, so that objects now all - have an expiration date associated with them. This makes it - much easier to know whether a given cached object should be used - or if it should be regenerated. - - Changing the default environment to production. - -0.24.4 - Pass source to pkg_add via the PKG_PATH environment variable if - it ends in a '/' indicating it is a directory. Allows pkg_add - to resolve dependancies, and make it possible to specify packages - without version numbers. - - Fixing #571 -- provider suitability is now checked at resource - evaluation time, rather than resource instantiation time. This - means that you don't catch your "errors" as early, but it also - means you should be able to realistically configure a whole host - in one run. - - Moved the configuration of the Node cache to the puppetmasterd - executable, since it otherwise causes caches to be used in all - cases, which we don't want (e.g., bin/puppet was using them). - - Ported #198 man page creation functionality to 0.24.x branch and - added man pages and man page creation logic to install.rb. The - man pages are stored in man/man8 and will install to config::CONFIG - mandir/man8. - - Fixing #1138 -- the yamldir is automatically created by the - server now that it's in the :puppetmasterd section rather than - a separate :yaml section. - - Disabling http keep-alive as a means of preventing #1010. - There is now a constant in Puppet::Network::HttpPool that will - disable or enable this feature, but note that we determined - that it can cause corruption, especially in file serving (but - it's client-side corruption). - - Applying patch by Ryan McBride to fix OpenBSD package - matching. The actual problem was caused by the fix to #1001. - - Found all instances of methods where split() is used without - any local variables and added a local variable -- see - http://snurl.com/21zf8. My own testing showed that this - caused memory growth to level off at a reasonable level. - Note that the link above says the problem is only with class - methods, but my own testing showed that it's any method that - meets these criteria. This is not a functional change, but - should hopefully be the last nail in the coffin of #1131. - - Found an array that leaked pretty quickly between reparsing - files, thanks to work by Adam Jacob and Arjuna Christenson - (the finding, not the leak). I'm going to act like this - fixes #1131, at least for now, but I doubt it does, - since that shows general memory growth over time, whereas - the leak here should go away as soon as files are reparsed - (because the parser is holding the reference to the leaking - array). - - Fixed #1147: Cached nodes are correctly considered out of - date if the node facts have been updated (thus causing - node facts to again be available in manifests, for those - cases where they were not). - - Fixed #1137: The certificate name is correctly being added - to the facts hash. - - Fixed #1136: Verbose and Debug no longer clobber each other. - - Hopefully *finally* fixed the "already being managed" problem - (#1036). The problem only cropped up if there was a failure - when trying to manage the system -- this would cause the - setting-based resources not to get cleaned up. - -0.24.3 - Modified the ldap node terminus to also use the facts version - as the version for a node, which should similarly encourage the - use of the yaml cache. (Related to #1130) - - Caching node information in yaml (I figured caching in memory will - cause ever-larger memory growth), and changing the external node - terminus to use the version of the facts as their version. This - will usually result in the cached node information being used, - instead of always hitting the external node app during file - serving. Note that if the facts aren't changed by the client, - then this will result in the cached node being used, but at this - point, the client always updates its facts. (#1130) - - Fixing #1132 -- host names can now have dashes anywhere. - (Patch by freiheit.) - - Fixing #1118 -- downloading plugins and facts now ignores noop. - Note that this changes the behaviour a bit -- the resource's - noop setting always beats the global setting (previously, - whichever was true would win). - - The change in checksums from 'timestamp' to 'mtime' no longer - result in updates on every run (#1116). - - Aliases again work in relationships (#1094). - - The CA serial file will no longer ever be owned by - root (#1041). - - Fixing the rest of #1113: External node commands can specify - an environment and Puppet will now use it. - - Partially fixing #1113: LDAP nodes now support environments, - and the schema has been updated accordingly. - - Always duplicating resource defaults in the parser, so that - stacked metaparameter values do not result in all resources - that receive a given default also getting those stacked - values. - -0.24.2 - Fixing #1062 by moving the yamldir setting to its own yaml - section. This should keep the yamldir from being created - on clients. - - Fixed #1047 -- Puppet's parser no longer changes the order - in which statements are evaluated, which means that case - statements can now set variables that are used by other - variables. - - Fixed #1063 -- the master correctly logs syntax errors when - reparsing during a single run. - - Removed the loglevels from the valid values for `logoutput` - in the Exec resource type -- the log levels are specified - using the `loglevel` parameter, not `logoutput`. This never - worked, or at least hasn`t for ages, and now the docs are - just correct. - - Somewhat refactored fileserving so that it no longer caches - any objects, nor does it use Puppet's RAL resources. In the - process, I fixed #894 (you can now copy links) and refactored - other classes as necessary. Mostly it was fixing tests. - - Hopefully partially fixed #1010 -- clients should now fail - to install files whose checksums do not match the checksum - from the server. - - Fixed #1018 -- resources now have their namevars added as - aliases in the resource catalog, just like they were added - in the resource classes. - - Fixed #1037 -- remote unreadable files no longer have the - permission denied exceptions caught, thus forbidding them - from being replaced with 'nil'. - - The environment is now available as a variable in the manifests. - - Fixed #1043 -- autoloading now searches the plugins directory - in each module, in addition to the lib directory. The 'lib' - directory is also deprecated, but supported for now to give - people a chance to convert. - - Fixed #1003 -- Applying DavidS's patch to fix searching for - tags in sql. - - Fixed #992 -- Puppet is now compatible with gems 1.0.1. - - 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, although the commands can still - be duplicated. 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/COPYING b/COPYING deleted file mode 100644 index 3912109b5..000000000 --- a/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/LICENSE b/LICENSE index 6bfcc22a1..e95cc9fc1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,17 +1,17 @@ -Puppet - Automating Configuration Management. Copyright (C) 2005 Puppet Labs LLC + Puppet - Automating Configuration Management. -Puppet Labs can be contacted at: info@puppetlabs.com + Copyright (C) 2011 Puppet Labs Inc -This program and entire repository is free software; you can -redistribute it and/or modify it under the terms of the GNU -General Public License Version 2 as published by the Free Software -Foundation. + Puppet Labs can be contacted at: info@puppetlabs.com -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README b/README deleted file mode 100644 index d6b7d66bc..000000000 --- a/README +++ /dev/null @@ -1,31 +0,0 @@ -Documentation (and detailed install instructions) can be found -online at http://docs.puppetlabs.com/. - -Additional documentation can also be found at the Puppet Wiki: - -http://projects.puppetlabs.com/projects/puppet/wiki/ - -Generally, you need the following things installed: - -* Ruby >= 1.8.1 (earlier releases might work but probably not) - -* The Ruby OpenSSL library. For some reason, this often isn't included - in the main ruby distributions. You can test for it by running - 'ruby -ropenssl -e "puts :yep"'. If that errors out, you're missing the - library. - - If your distribution doesn't come with the necessary library (e.g., on Debian - and Ubuntu you need to install libopenssl-ruby), then you'll probably have to - compile Ruby yourself, since it's part of the standard library and not - available separately. You could probably just compile and install that one - library, though. - -* The Ruby XMLRPC client and server libraries. For some reason, this often - isn't included in the main ruby distributions. You can test for it by - running 'ruby -rxmlrpc/client -e "puts :yep"'. If that errors out, you're missing - the library. - -* Facter => 1.5.1 - You can get this from < http://puppetlabs.com/projects/facter > - -$Id$ diff --git a/README.rst b/README.md similarity index 57% rename from README.rst rename to README.md index 63452ef91..4d17a752a 100644 --- a/README.rst +++ b/README.md @@ -1,41 +1,40 @@ Puppet ====== Puppet, an automated administrative engine for your Linux and Unix systems, performs administrative tasks (such as adding users, installing packages, and updating server configurations) based on a centralized specification. -Documentation (and detailed install instructions) can be found online at the -`Puppet Documentation`_ site. - -Additional documentation can also be found at the `Puppet Wiki`_. +Documentation (and detailed installation instructions) can be found online at the +[Puppet Docs site](http://docs.puppetlabs.com). Installation ------------ Generally, you need the following things installed: * Ruby >= 1.8.1 (earlier releases might work but probably not) * The Ruby OpenSSL library. For some reason, this often isn't included in the main ruby distributions. You can test for it by running 'ruby -ropenssl -e "puts :yep"'. If that errors out, you're missing the library. If your distribution doesn't come with the necessary library (e.g., on Debian and Ubuntu you need to install libopenssl-ruby), then you'll probably have to compile Ruby yourself, since it's part of the standard library and not available separately. You could probably just compile and install that one library, though. -* The Ruby XMLRPC client and server libraries. For some reason, this often - isn't included in the main ruby distributions. You can test for it by - running 'ruby -rxmlrpc/client -e "puts :yep"'. If that errors out, you're missing - the library. +* Facter => 1.5.1 (available via your package manager or from the [Facter site](http://puppetlabs.com/projects/facter). + +License +------- + +See LICENSE file. + +Support +------- -* Facter => 1.5.1 - You can get this from your package management system or the `Facter site`_ +Please log tickets and issues at our [Projects site](http://projects.puppetlabs.com) -.. _Puppet Documentation: http://docs.puppetlabs.com -.. _Puppet Wiki: http://projects.puppetlabs.com/projects/puppet/wiki/ -.. _Facter site: http://puppetlabs.com/projects/facter diff --git a/README.queueing b/README.queueing deleted file mode 100644 index 83a8e19c0..000000000 --- a/README.queueing +++ /dev/null @@ -1,126 +0,0 @@ -*PUPPET QUEUEING - -Puppet Queueing is a feature which is designed to take some load -off of the PuppetMaster by transferring the task of updating the -database to a separate program which is named puppetqd (Puppet -Queue Daemon). - -Currently this is only supported for "Storeconfigs" which is -documented at: - -http://projects.puppetlabs.com/projects/1/wiki/Using_Stored_Configuration - -In the future this feature can be extended to any new puppet -data which involves storage in a database. - -*OPERATION - -In a nutshell: - - puppetmasterd -> stomp -> service -> stomp -> puppetqd -> database - -At the moment the only messaging protocol supported is "stomp". Although -others could be implemented, stomp is considered by many as the -default queueing mechanism for Ruby and Rails applications. It is -distributed as a Ruby gem and is easily installed. - -(The queueing code inside Puppet has been written so that when other -interfaces and protocols are implemented they will be easy to use by -changing settings in puppet.conf). - -The "service" in the diagram above is any queueing service that supports -the Stomp API. For details refer to: - - http://xircles.codehaus.org/projects/stomp - -Both puppetmasterd and puppetqd subscribe to the same queueing service -using the stomp interface. As puppetmasterd posts data to the queue, -puppetqd receives it and stores it. The details of how to connect to -the service and the name of the queue to use are set in puppet.conf: - - [main] - queue_type = stomp - queue_source = stomp://localhost:61613 - [puppetmasterd] - async_storeconfigs = true - -Note: since puppetmasterd needs to recover the data being stored at a -later time, both puppetmasterd and puppetqd need to work with the same -database as defined in the STORECONFIGS setup. - -*QUEUEING SERVICES - -As mentioned previously any queueing service that supports the Stomp -protocol can be used. Which one you use depends on your needs. We have -tested with two of the most popular services - StompServer and ActiveMQ. - -+ StompServer - - http://rubyforge.org/projects/stompserver/ - -StompServer is a lightweight queueing service written in Ruby which is -suitable for testing or low volume puppet usage. Works well when both -puppetmasterd and puppetd are running on the same machine that it's running -on but we encountered some problems when using it from multiple machines. - -Just install the stompserver gem and run 'stompserver'. - -+ Apache ActiveMQ - - http://activemq.apache.org - -Considered by many to be the most popular message service in use today, -ActiveMQ has hundreds of features for scaling, persistence and so on. - -Although installation is fairly simple, the configuration can seem quite -intimidating, but for our use a one line change to the standard configuration -is all that is required and is explained at: - - http://activemq.apache.org/stomp.html - -Other customization of the internal workings of ActiveMQ, if any, will depend -on your needs and deployment. A quick skimming of the ActiveMQ documentation -will give you enough info to decide. - -Others - -We have looked at but not tried some other queuing services which are -compatible with the Stomp API: - -+ POE Component Message Queue -+ JBoss Messaging (with 3rd party support for Stomp) - -*SCALING - -For StoreConfigs you basically need to have the catalog for a node stored -in the database before the next time the node connects and asks for a -new catalog. - -If the puppetd on your nodes is set to check every 30 minutes, -then it would seem that there is no problem. However if you have 3000 -nodes you have a LOT of catalogs to store and it is possible you will -not get a catalog saved in time. - -Running puppetmaster, your queueing service and puppetqd on the same -machine means that they are all competing for the same CPU cycles. Bumping -up the power of the server they are running on may be enough to handle -even fairly large deployments. - -However since most queueing services (even StompServer) are designed to -deliver messages from a "queue" to whoever asks for the next message you -can split things up between machines: - - puppetmaster1 --\ /-- puppetqd1 -\ - puppetmaster2 ----> ActiveMQ ---> puppetqd2 ---> database - puppetmaster3 --/ \-- puppetqd33 -/ - \- puppetqd4-/ - -This is, of course a totally contrived example, but it gets the point -across. As long as the data gets to the database, it doesn't matter -which machines or services it goes through. - -Although for StoreConfigs absolute reliability is not a requirement as -a new catalog will be sent the next time a node connects, some amount -of persistence should some process crash may be desirable. Both ActiveMQ -and MySQL (and other databases) have these kind of features built in -which can be activated as needed. diff --git a/README.strings b/README.strings new file mode 100644 index 000000000..28289ee10 --- /dev/null +++ b/README.strings @@ -0,0 +1,115 @@ +Puppet Strings +================= +A set of executables that provide complete CLI access to Puppet's +core data types. They also provide String classes for +each of the core data types, which are extensible via plugins. + +For instance, you can create a new action for catalogs at +lib/puppet/string/catalog/$action.rb. + +This is a Puppet module and should work fine if you install it +in Puppet's module path. + +**Note that this only works with Puppet 2.6.next (and thus will work +with 2.6.5), because there is otherwise a bug in finding Puppet applications. +You also have to either install the lib files into your Puppet libdir, or +you need to add this lib directory to your RUBYLIB.** + +This is meant to be tested and iterated upon, with the plan that it will be +merged into Puppet core once we're satisfied with it. + +Usage +----- +The general usage is: + + $ puppet + +So, e.g.: + + $ puppet facts find myhost.domain.com + $ puppet node destroy myhost + +You can use it to list all known data types and the available terminus classes: + + $ puppet string list + catalog : active_record, compiler, queue, rest, yaml + certificate : ca, file, rest + certificate_request : ca, file, rest + certificate_revocation_list : ca, file, rest + file_bucket_file : file, rest + inventory : yaml + key : ca, file + node : active_record, exec, ldap, memory, plain, rest, yaml + report : processor, rest, yaml + resource : ral, rest + resource_type : parser, rest + status : local, rest + +But most interestingly, you can use it for two main purposes: + +* As a client for any Puppet REST server, such as catalogs, facts, reports, etc. +* As a local CLI for any local Puppet data + +A simple case is looking at the local facts: + + $ puppet facts find localhost + +If you're on the server, you can look in that server's fact collection: + + $ puppet facts --mode master --vardir /tmp/foo --terminus yaml find localhost + +Note that we're setting both the vardir and the 'mode', which switches from the default 'agent' mode to server mode (requires a patch in my branch). + +If you'd prefer the data be outputted in json instead of yaml, well, you can do that, too: + + $ puppet find --mode master facts --vardir /tmp/foo --terminus yaml --format pson localhost + +To test using it as an endpoint for compiling and retrieving catalogs from a remote server, (from my commit), try this: + + # Terminal 1 + $ sbin/puppetmasterd --trace --confdir /tmp/foo --vardir /tmp/foo --debug --manifest ~/bin/test.pp --certname localhost --no-daemonize + + # Terminal 2 + $ sbin/puppetd --trace --debug --confdir /tmp/foo --vardir /tmp/foo --certname localhost --server localhost --test --report + + # Terminal 3, actual testing + $ puppet catalog find localhost --certname localhost --server localhost --mode master --confdir /tmp/foo --vardir /tmp/foo --trace --terminus rest + +This compiles a test catalog (assuming that ~/bin/test.pp exists) and returns it. With the right auth setup, you can also get facts: + + $ puppet facts find localhost --certname localhost --server localhost --mode master --confdir /tmp/foo --vardir /tmp/foo --trace --terminus rest + +Or use IRB to do the same thing: + + $ irb + >> require 'puppet/string' + => true + >> string = Puppet::String[:facts, '1.0.0'] + => # + >> facts = string.find("myhost") + +Like I said, a prototype, but I'd love it if people would play it with some and make some recommendations. + +Extending +--------- +Like most parts of Puppet, these are easy to extend. Just drop a new action into a given string's directory. E.g.: + + $ cat lib/puppet/string/catalog/select.rb + # Select and show a list of resources of a given type. + Puppet::String.define(:catalog, '1.0.0') do + action :select do + invoke do |host,type| + catalog = Puppet::Resource::Catalog.indirection.find(host) + + catalog.resources.reject { |res| res.type != type }.each { |res| puts res } + end + end + end + $ puppet catalog select localhost Class + Class[main] + Class[Settings] + $ + +Notice that this gets loaded automatically when you try to use it. So, if you have a simple command you've written, such as for cleaning up nodes or diffing catalogs, you an port it to this framework and it should fit cleanly. + +Also note that strings are versioned. These version numbers are interpreted according to Semantic Versioning (http://semver.org). diff --git a/conf/redhat/puppet.spec b/conf/redhat/puppet.spec index e51af0a6b..26b2c9ba0 100644 --- a/conf/redhat/puppet.spec +++ b/conf/redhat/puppet.spec @@ -1,454 +1,454 @@ # Augeas and SELinux requirements may be disabled at build time by passing # --without augeas and/or --without selinux to rpmbuild or mock %{!?ruby_sitelibdir: %global ruby_sitelibdir %(ruby -rrbconfig -e 'puts Config::CONFIG["sitelibdir"]')} %global confdir conf/redhat Name: puppet Version: 2.6.0 Release: 1%{?dist} Summary: A network tool for managing many disparate systems -License: GPLv2+ +License: Apache 2.0 URL: http://puppetlabs.com Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz Source1: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz.sign Group: System Environment/Base BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: facter >= 1.5 BuildRequires: ruby >= 1.8.1 %if 0%{?fedora} || 0%{?rhel} >= 5 BuildArch: noarch Requires: ruby(abi) = 1.8 Requires: ruby-shadow %endif # Pull in ruby selinux bindings where available %if 0%{?fedora} >= 12 || 0%{?rhel} >= 6 %{!?_without_selinux:Requires: ruby(selinux)} %else %if 0%{?fedora} || 0%{?rhel} >= 5 %{!?_without_selinux:Requires: libselinux-ruby} %endif %endif Requires: facter >= 1.5 Requires: ruby >= 1.8.1 %{!?_without_augeas:Requires: ruby-augeas} Requires(pre): shadow-utils Requires(post): chkconfig Requires(preun): chkconfig Requires(preun): initscripts Requires(postun): initscripts %description Puppet lets you centrally manage every important aspect of your system using a cross-platform specification language that manages all the separate elements normally aggregated in different files, like users, cron jobs, and hosts, along with obviously discrete elements like packages, services, and files. %package server Group: System Environment/Base Summary: Server for the puppet system management tool Requires: puppet = %{version}-%{release} Requires(post): chkconfig Requires(preun): chkconfig Requires(preun): initscripts Requires(postun): initscripts %description server Provides the central puppet server daemon which provides manifests to clients. The server can also function as a certificate authority and file server. %prep %setup -q patch -p1 < conf/redhat/rundir-perms.patch %build # Fix some rpmlint complaints for f in mac_dscl.pp mac_dscl_revert.pp \ mac_pkgdmg.pp ; do sed -i -e'1d' examples/$f chmod a-x examples/$f done for f in external/nagios.rb network/http_server/mongrel.rb relationship.rb; do sed -i -e '1d' lib/puppet/$f done chmod +x ext/puppetstoredconfigclean.rb find examples/ -type f -empty | xargs rm find examples/ -type f | xargs chmod a-x # puppet-queue.conf is more of an example, used for stompserver mv conf/puppet-queue.conf examples/etc/puppet/ %install rm -rf %{buildroot} ruby install.rb --destdir=%{buildroot} --quick --no-rdoc install -d -m0755 %{buildroot}%{_sysconfdir}/puppet/manifests install -d -m0755 %{buildroot}%{_datadir}/%{name}/modules install -d -m0755 %{buildroot}%{_localstatedir}/lib/puppet install -d -m0755 %{buildroot}%{_localstatedir}/run/puppet install -d -m0750 %{buildroot}%{_localstatedir}/log/puppet install -Dp -m0644 %{confdir}/client.sysconfig %{buildroot}%{_sysconfdir}/sysconfig/puppet install -Dp -m0755 %{confdir}/client.init %{buildroot}%{_initrddir}/puppet install -Dp -m0644 %{confdir}/server.sysconfig %{buildroot}%{_sysconfdir}/sysconfig/puppetmaster install -Dp -m0755 %{confdir}/server.init %{buildroot}%{_initrddir}/puppetmaster install -Dp -m0644 %{confdir}/fileserver.conf %{buildroot}%{_sysconfdir}/puppet/fileserver.conf install -Dp -m0644 %{confdir}/puppet.conf %{buildroot}%{_sysconfdir}/puppet/puppet.conf install -Dp -m0644 conf/auth.conf %{buildroot}%{_sysconfdir}/puppet/auth.conf install -Dp -m0644 %{confdir}/logrotate %{buildroot}%{_sysconfdir}/logrotate.d/puppet # We need something for these ghosted files, otherwise rpmbuild # will complain loudly. They won't be included in the binary packages touch %{buildroot}%{_sysconfdir}/puppet/puppetmasterd.conf touch %{buildroot}%{_sysconfdir}/puppet/puppetca.conf touch %{buildroot}%{_sysconfdir}/puppet/puppetd.conf # Install the ext/ directory to %{_datadir}/%{name} install -d %{buildroot}%{_datadir}/%{name} cp -a ext/ %{buildroot}%{_datadir}/%{name} # emacs and vim bits are installed elsewhere rm -rf %{buildroot}%{_datadir}/%{name}/ext/{emacs,vim} # Install emacs mode files emacsdir=%{buildroot}%{_datadir}/emacs/site-lisp install -Dp -m0644 ext/emacs/puppet-mode.el $emacsdir/puppet-mode.el install -Dp -m0644 ext/emacs/puppet-mode-init.el \ $emacsdir/site-start.d/puppet-mode-init.el # Install vim syntax files vimdir=%{buildroot}%{_datadir}/vim/vimfiles install -Dp -m0644 ext/vim/ftdetect/puppet.vim $vimdir/ftdetect/puppet.vim install -Dp -m0644 ext/vim/syntax/puppet.vim $vimdir/syntax/puppet.vim %files %defattr(-, root, root, 0755) %doc CHANGELOG COPYING LICENSE README README.queueing examples %{_bindir}/pi %{_bindir}/puppet %{_bindir}/ralsh %{_bindir}/filebucket %{_bindir}/puppetdoc %{_sbindir}/puppetca %{_sbindir}/puppetd %{ruby_sitelibdir}/* %{_initrddir}/puppet %dir %{_sysconfdir}/puppet %config(noreplace) %{_sysconfdir}/sysconfig/puppet %config(noreplace) %{_sysconfdir}/puppet/puppet.conf %config(noreplace) %{_sysconfdir}/puppet/auth.conf %ghost %config(noreplace,missingok) %{_sysconfdir}/puppet/puppetca.conf %ghost %config(noreplace,missingok) %{_sysconfdir}/puppet/puppetd.conf %config(noreplace) %{_sysconfdir}/logrotate.d/puppet # We don't want to require emacs or vim, so we need to own these dirs %{_datadir}/emacs %{_datadir}/vim %{_datadir}/%{name} # These need to be owned by puppet so the server can # write to them %attr(-, puppet, puppet) %{_localstatedir}/run/puppet %attr(-, puppet, puppet) %{_localstatedir}/log/puppet %attr(-, puppet, puppet) %{_localstatedir}/lib/puppet %{_mandir}/man5/puppet.conf.5.gz %{_mandir}/man8/pi.8.gz %{_mandir}/man8/puppet.8.gz %{_mandir}/man8/puppetca.8.gz %{_mandir}/man8/puppetd.8.gz %{_mandir}/man8/ralsh.8.gz %{_mandir}/man8/puppetdoc.8.gz %files server %defattr(-, root, root, 0755) %{_sbindir}/puppetmasterd %{_sbindir}/puppetrun %{_sbindir}/puppetqd %{_initrddir}/puppetmaster %config(noreplace) %{_sysconfdir}/puppet/fileserver.conf %dir %{_sysconfdir}/puppet/manifests %config(noreplace) %{_sysconfdir}/sysconfig/puppetmaster %ghost %config(noreplace,missingok) %{_sysconfdir}/puppet/puppetmasterd.conf %{_mandir}/man8/filebucket.8.gz %{_mandir}/man8/puppetmasterd.8.gz %{_mandir}/man8/puppetrun.8.gz %{_mandir}/man8/puppetqd.8.gz # Fixed uid/gid were assigned in bz 472073 (Fedora), 471918 (RHEL-5), # and 471919 (RHEL-4) %pre getent group puppet &>/dev/null || groupadd -r puppet -g 52 &>/dev/null getent passwd puppet &>/dev/null || \ useradd -r -u 52 -g puppet -d %{_localstatedir}/lib/puppet -s /sbin/nologin \ -c "Puppet" puppet &>/dev/null || : # ensure that old setups have the right puppet home dir if [ $1 -gt 1 ] ; then usermod -d %{_localstatedir}/lib/puppet puppet &>/dev/null || : fi %post /sbin/chkconfig --add puppet || : %post server /sbin/chkconfig --add puppetmaster || : %preun if [ "$1" = 0 ] ; then /sbin/service puppet stop > /dev/null 2>&1 /sbin/chkconfig --del puppet || : fi %preun server if [ "$1" = 0 ] ; then /sbin/service puppetmaster stop > /dev/null 2>&1 /sbin/chkconfig --del puppetmaster || : fi %postun if [ "$1" -ge 1 ]; then /sbin/service puppet condrestart >/dev/null 2>&1 || : fi %postun server if [ "$1" -ge 1 ]; then /sbin/service puppetmaster condrestart > /dev/null 2>&1 || : fi %clean rm -rf %{buildroot} %changelog * Tue Jul 20 2010 Todd Zullinger - 2.6.0-1 - Update to 2.6.0 - Create and own /usr/share/puppet/modules (#615432) * Mon May 03 2010 Todd Zullinger - 0.25.5-1 - Update to 0.25.5 - Adjust selinux conditional for EL-6 - Apply rundir-perms patch from tarball rather than including it separately * Fri Jan 01 2010 Todd Zullinger - 0.25.2-1 - Update to 0.25.2 - Install auth.conf, puppetqd manpage, and queuing examples/docs * Tue Oct 20 2009 Todd Zullinger - 0.25.1-1 - Update to 0.25.1 - Include the pi program and man page (R.I.Pienaar) * Sat Oct 17 2009 Todd Zullinger - 0.25.1-0.2.rc2 - Update to 0.25.1rc2 * Tue Sep 22 2009 Todd Zullinger - 0.25.1-0.1.rc1 - Update to 0.25.1rc1 - Move puppetca to puppet package, it has uses on client systems - Drop redundant %%doc from manpage %%file listings * Fri Sep 04 2009 Todd Zullinger - 0.25.0-1 - Update to 0.25.0 - Fix permissions on /var/log/puppet (#495096) - Install emacs mode and vim syntax files (#491437) - Install ext/ directory in %%{_datadir}/%{name} (/usr/share/puppet) * Mon May 04 2009 Todd Zullinger - 0.25.0-0.1.beta1 - Update to 0.25.0beta1 - Make Augeas and SELinux requirements build time options * Mon Mar 23 2009 Todd Zullinger - 0.24.8-1 - Update to 0.24.8 - Quiet output from %%pre - Use upstream install script - Increase required facter version to >= 1.5 * Tue Dec 16 2008 Todd Zullinger - 0.24.7-4 - Remove redundant useradd from %%pre * Tue Dec 16 2008 Jeroen van Meeuwen - 0.24.7-3 - New upstream version - Set a static uid and gid (#472073, #471918, #471919) - Add a conditional requirement on libselinux-ruby for Fedora >= 9 - Add a dependency on ruby-augeas * Wed Oct 22 2008 Todd Zullinger - 0.24.6-1 - Update to 0.24.6 - Require ruby-shadow on Fedora and RHEL >= 5 - Simplify Fedora/RHEL version checks for ruby(abi) and BuildArch - Require chkconfig and initstripts for preun, post, and postun scripts - Conditionally restart puppet in %%postun - Ensure %%preun, %%post, and %%postun scripts exit cleanly - Create puppet user/group according to Fedora packaging guidelines - Quiet a few rpmlint complaints - Remove useless %%pbuild macro - Make specfile more like the Fedora/EPEL template * Mon Jul 28 2008 David Lutterkort - 0.24.5-1 - Add /usr/bin/puppetdoc * Thu Jul 24 2008 Brenton Leanhardt - New version - man pages now ship with tarball - examples/code moved to root examples dir in upstream tarball * Tue Mar 25 2008 David Lutterkort - 0.24.4-1 - Add man pages (from separate tarball, upstream will fix to include in main tarball) * Mon Mar 24 2008 David Lutterkort - 0.24.3-1 - New version * Wed Mar 5 2008 David Lutterkort - 0.24.2-1 - New version * Sat Dec 22 2007 David Lutterkort - 0.24.1-1 - New version * Mon Dec 17 2007 David Lutterkort - 0.24.0-2 - Use updated upstream tarball that contains yumhelper.py * Fri Dec 14 2007 David Lutterkort - 0.24.0-1 - Fixed license - Munge examples/ to make rpmlint happier * Wed Aug 22 2007 David Lutterkort - 0.23.2-1 - New version * Thu Jul 26 2007 David Lutterkort - 0.23.1-1 - Remove old config files * Wed Jun 20 2007 David Lutterkort - 0.23.0-1 - Install one puppet.conf instead of old config files, keep old configs around to ease update - Use plain shell commands in install instead of macros * Wed May 2 2007 David Lutterkort - 0.22.4-1 - New version * Thu Mar 29 2007 David Lutterkort - 0.22.3-1 - Claim ownership of _sysconfdir/puppet (bz 233908) * Mon Mar 19 2007 David Lutterkort - 0.22.2-1 - Set puppet's homedir to /var/lib/puppet, not /var/puppet - Remove no-lockdir patch, not needed anymore * Mon Feb 12 2007 David Lutterkort - 0.22.1-2 - Fix bogus config parameter in puppetd.conf * Sat Feb 3 2007 David Lutterkort - 0.22.1-1 - New version * Fri Jan 5 2007 David Lutterkort - 0.22.0-1 - New version * Mon Nov 20 2006 David Lutterkort - 0.20.1-2 - Make require ruby(abi) and buildarch: noarch conditional for fedora 5 or later to allow building on older fedora releases * Mon Nov 13 2006 David Lutterkort - 0.20.1-1 - New version * Mon Oct 23 2006 David Lutterkort - 0.20.0-1 - New version * Tue Sep 26 2006 David Lutterkort - 0.19.3-1 - New version * Mon Sep 18 2006 David Lutterkort - 0.19.1-1 - New version * Thu Sep 7 2006 David Lutterkort - 0.19.0-1 - New version * Tue Aug 1 2006 David Lutterkort - 0.18.4-2 - Use /usr/bin/ruby directly instead of /usr/bin/env ruby in executables. Otherwise, initscripts break since pidof can't find the right process * Tue Aug 1 2006 David Lutterkort - 0.18.4-1 - New version * Fri Jul 14 2006 David Lutterkort - 0.18.3-1 - New version * Wed Jul 5 2006 David Lutterkort - 0.18.2-1 - New version * Wed Jun 28 2006 David Lutterkort - 0.18.1-1 - Removed lsb-config.patch and yumrepo.patch since they are upstream now * Mon Jun 19 2006 David Lutterkort - 0.18.0-1 - Patch config for LSB compliance (lsb-config.patch) - Changed config moves /var/puppet to /var/lib/puppet, /etc/puppet/ssl to /var/lib/puppet, /etc/puppet/clases.txt to /var/lib/puppet/classes.txt, /etc/puppet/localconfig.yaml to /var/lib/puppet/localconfig.yaml * Fri May 19 2006 David Lutterkort - 0.17.2-1 - Added /usr/bin/puppetrun to server subpackage - Backported patch for yumrepo type (yumrepo.patch) * Wed May 3 2006 David Lutterkort - 0.16.4-1 - Rebuilt * Fri Apr 21 2006 David Lutterkort - 0.16.0-1 - Fix default file permissions in server subpackage - Run puppetmaster as user puppet - rebuilt for 0.16.0 * Mon Apr 17 2006 David Lutterkort - 0.15.3-2 - Don't create empty log files in post-install scriptlet * Fri Apr 7 2006 David Lutterkort - 0.15.3-1 - Rebuilt for new version * Wed Mar 22 2006 David Lutterkort - 0.15.1-1 - Patch0: Run puppetmaster as root; running as puppet is not ready for primetime * Mon Mar 13 2006 David Lutterkort - 0.15.0-1 - Commented out noarch; requires fix for bz184199 * Mon Mar 6 2006 David Lutterkort - 0.14.0-1 - Added BuildRequires for ruby * Wed Mar 1 2006 David Lutterkort - 0.13.5-1 - Removed use of fedora-usermgmt. It is not required for Fedora Extras and makes it unnecessarily hard to use this rpm outside of Fedora. Just allocate the puppet uid/gid dynamically * Sun Feb 19 2006 David Lutterkort - 0.13.0-4 - Use fedora-usermgmt to create puppet user/group. Use uid/gid 24. Fixed problem with listing fileserver.conf and puppetmaster.conf twice * Wed Feb 8 2006 David Lutterkort - 0.13.0-3 - Fix puppetd.conf * Wed Feb 8 2006 David Lutterkort - 0.13.0-2 - Changes to run puppetmaster as user puppet * Mon Feb 6 2006 David Lutterkort - 0.13.0-1 - Don't mark initscripts as config files * Mon Feb 6 2006 David Lutterkort - 0.12.0-2 - Fix BuildRoot. Add dist to release * Tue Jan 17 2006 David Lutterkort - 0.11.0-1 - Rebuild * Thu Jan 12 2006 David Lutterkort - 0.10.2-1 - Updated for 0.10.2 Fixed minor kink in how Source is given * Wed Jan 11 2006 David Lutterkort - 0.10.1-3 - Added basic fileserver.conf * Wed Jan 11 2006 David Lutterkort - 0.10.1-1 - Updated. Moved installation of library files to sitelibdir. Pulled initscripts into separate files. Folded tools rpm into server * Thu Nov 24 2005 Duane Griffin - Added init scripts for the client * Wed Nov 23 2005 Duane Griffin - First packaging diff --git a/conf/solaris/pkginfo b/conf/solaris/pkginfo index 7f7bb9736..14a2f0a79 100644 --- a/conf/solaris/pkginfo +++ b/conf/solaris/pkginfo @@ -1,7 +1,6 @@ PKG=CSWpuppet NAME=puppet - System Automation Framework -VERSION=0.23.0 +VERSION=2.7.0 CATEGORY=application -VENDOR=http://reductivelabs.com/projects/puppet -HOTLINE=http://reductivelabs.com/cgi-bin/puppet.cgi -EMAIL=luke@madstop.com +VENDOR=http://projects.puppetlabs.com/projects/puppet +EMAIL=luke@puppetlabs.com diff --git a/conf/suse/puppet.spec b/conf/suse/puppet.spec index 777f6a4b6..2a5b9f4db 100644 --- a/conf/suse/puppet.spec +++ b/conf/suse/puppet.spec @@ -1,297 +1,297 @@ %{!?ruby_sitelibdir: %define ruby_sitelibdir %(ruby -rrbconfig -e 'puts Config::CONFIG["sitelibdir"]')} %define pbuild %{_builddir}/%{name}-%{version} %define confdir conf/suse Summary: A network tool for managing many disparate systems Name: puppet Version: 2.6.1 Release: 1%{?dist} -License: GPL +License: Apache 2.0 Group: Productivity/Networking/System URL: http://puppetlabs.com/projects/puppet/ Source0: http://puppetlabs.com/downloads/puppet/%{name}-%{version}.tar.gz PreReq: %{insserv_prereq} %{fillup_prereq} Requires: ruby >= 1.8.2 Requires: facter >= 1.5 Requires: cron BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: ruby >= 1.8.2 %description Puppet lets you centrally manage every important aspect of your system using a cross-platform specification language that manages all the separate elements normally aggregated in different files, like users, cron jobs, and hosts, along with obviously discrete elements like packages, services, and files. %package server Group: Productivity/Networking/System Summary: Server for the puppet system management tool Requires: puppet = %{version}-%{release} %description server Provides the central puppet server daemon which provides manifests to clients. The server can also function as a certificate authority and file server. %prep %setup -q %build for f in bin/* sbin/*; do sed -i -e '1s,^#!.*ruby$,#!/usr/bin/ruby,' $f done %install %{__install} -d -m0755 %{buildroot}%{_sbindir} %{__install} -d -m0755 %{buildroot}%{_bindir} %{__install} -d -m0755 %{buildroot}%{_confdir} %{__install} -d -m0755 %{buildroot}%{ruby_sitelibdir} %{__install} -d -m0755 %{buildroot}%{_sysconfdir}/puppet/manifests %{__install} -d -m0755 %{buildroot}%{_docdir}/%{name}-%{version} %{__install} -d -m0755 %{buildroot}%{_localstatedir}/lib/puppet %{__install} -d -m0755 %{buildroot}%{_localstatedir}/run/puppet %{__install} -d -m0755 %{buildroot}%{_localstatedir}/log/puppet %{__install} -Dp -m0755 %{pbuild}/bin/* %{buildroot}%{_bindir} %{__install} -Dp -m0755 %{pbuild}/sbin/* %{buildroot}%{_sbindir} %{__install} -Dp -m0644 %{pbuild}/lib/puppet.rb %{buildroot}%{ruby_sitelibdir}/puppet.rb %{__cp} -a %{pbuild}/lib/puppet %{buildroot}%{ruby_sitelibdir} find %{buildroot}%{ruby_sitelibdir} -type f -perm +ugo+x -exec chmod a-x '{}' \; %{__cp} -a %{pbuild}/conf/redhat/client.sysconfig %{buildroot}%{_confdir}/client.sysconfig %{__install} -Dp -m0644 %{buildroot}%{_confdir}/client.sysconfig %{buildroot}/var/adm/fillup-templates/sysconfig.puppet %{__cp} -a %{pbuild}/conf/redhat/server.sysconfig %{buildroot}%{_confdir}/server.sysconfig %{__install} -Dp -m0644 %{buildroot}%{_confdir}/server.sysconfig %{buildroot}/var/adm/fillup-templates/sysconfig.puppetmaster %{__cp} -a %{pbuild}/conf/redhat/fileserver.conf %{buildroot}%{_confdir}/fileserver.conf %{__install} -Dp -m0644 %{buildroot}%{_confdir}/fileserver.conf %{buildroot}%{_sysconfdir}/puppet/fileserver.conf %{__cp} -a %{pbuild}/conf/redhat/puppet.conf %{buildroot}%{_confdir}/puppet.conf %{__install} -Dp -m0644 %{buildroot}%{_confdir}/puppet.conf %{buildroot}%{_sysconfdir}/puppet/puppet.conf %{__cp} -a %{pbuild}/conf/redhat/logrotate %{buildroot}%{_confdir}/logrotate %{__install} -Dp -m0644 %{buildroot}%{_confdir}/logrotate %{buildroot}%{_sysconfdir}/logrotate.d/puppet %{__install} -Dp -m0755 %{confdir}/client.init %{buildroot}%{_initrddir}/puppet %{__install} -Dp -m0755 %{confdir}/server.init %{buildroot}%{_initrddir}/puppetmaster %{__ln_s} %{_initrddir}/puppet %{buildroot}%{_sbindir}/rcpuppet %{__ln_s} %{_initrddir}/puppetmaster %{buildroot}%{_sbindir}/rcpuppetmaster %files %defattr(-, root, root, 0755) %{_bindir}/puppet %{_bindir}/puppetdoc %{_bindir}/filebucket %{_bindir}/ralsh %{_bindir}/pi %{_sbindir}/puppetd %{_sbindir}/rcpuppet %{ruby_sitelibdir}/* %{_initrddir}/puppet /var/adm/fillup-templates/sysconfig.puppet %config(noreplace) %{_sysconfdir}/puppet/puppet.conf %doc CHANGELOG COPYING LICENSE README examples %config(noreplace) %{_sysconfdir}/logrotate.d/puppet %dir %{_sysconfdir}/puppet # These need to be owned by puppet so the server can # write to them %attr(-, puppet, puppet) %{_localstatedir}/run/puppet %attr(-, puppet, puppet) %{_localstatedir}/log/puppet %attr(-, puppet, puppet) %{_localstatedir}/lib/puppet %files server %defattr(-, root, root, 0755) %{_sbindir}/puppetmasterd %{_sbindir}/rcpuppetmaster %{_sbindir}/puppetqd %{_sbindir}/puppetrun %{_sbindir}/puppetca %{_initrddir}/puppetmaster %config(noreplace) %{_sysconfdir}/puppet/* %exclude %{_sysconfdir}/puppet/puppet.conf /var/adm/fillup-templates/sysconfig.puppetmaster %dir %{_sysconfdir}/puppet %pre /usr/sbin/groupadd -r puppet 2>/dev/null || : /usr/sbin/useradd -g puppet -c "Puppet" \ -s /sbin/nologin -r -d /var/puppet puppet 2> /dev/null || : %post %{fillup_and_insserv -y puppet} %post server %{fillup_and_insserv -n -y puppetmaster} %preun %stop_on_removal puppet %preun server %stop_on_removal puppetmaster %postun %restart_on_update puppet %{insserv_cleanup} %postun server %restart_on_update puppetmaster %{insserv_cleanup} %clean %{__rm} -rf %{buildroot} %changelog * Tue Sep 14 2010 Ben Kevan - 2.6.1 - New version to 2.6.1 - Add client.init and server.init from source since it's now included in the packages - Change BuildRequires Ruby version to match Requires Ruby version - Removed ruby-env patch, replaced with sed in prep - Update urls to puppetlabs.com * Wed Jul 21 2010 Ben Kevan - 2.6.0 - New version and ruby version bump - Add puppetdoc to %_bindir (unknown why original suse package, excluded or forgot to add) - Corrected patch for ruby environment - Move binaries back to the correct directories * Wed Jul 14 2010 Ben Kevan - 0.25.5 - New version. - Use original client, server.init names - Revert to puppetmaster - Fixed client.init and server.init and included $null and Should-Stop for both * Tue Mar 2 2010 Martin Vuk - 0.25.4 - New version. * Sun Aug 9 2009 Noah Fontes - Fix build on SLES 9. - Enable puppet and puppet-server services by default. * Sat Aug 8 2009 Noah Fontes - Fix a lot of relevant warnings from rpmlint. - Build on OpenSUSE 11.1 correctly. - Rename puppetmaster init scripts to puppet-server to correspond to the package name. * Wed Apr 22 2009 Leo Eraly - 0.24.8 - New version. * Tue Dec 9 2008 Leo Eraly - 0.24.6 - New version. * Fri Sep 5 2008 Leo Eraly - 0.24.5 - New version. * Fri Jun 20 2008 Martin Vuk - 0.24.4 - Removed symlinks to old configuration files * Fri Dec 14 2007 Martin Vuk - 0.24.0 - New version. * Fri Jun 29 2007 Martin Vuk - 0.23.0 - New version. * Wed May 2 2007 Martin Vuk - 0.22.4 - New version. Includes provider for rug package manager. * Wed Apr 25 2007 Martin Vuk - 0.22.3 - New version. Added links /sbin/rcpuppet and /sbin/rcpuppetmaster * Sun Jan 7 2007 Martin Vuk - 0.22.0 - version bump * Tue Oct 3 2006 Martin Vuk - 0.19.3-3 - Made package arch dependant. * Sat Sep 23 2006 Martin Vuk - 0.19.3-1 - New version * Sun Sep 17 2006 Martin Vuk - 0.19.1-1 - New version * Tue Aug 30 2006 Martin Vuk - 0.19.0-1 - New version - No need to patch anymore :-), since my changes went into official release. * Tue Aug 3 2006 Martin Vuk - 0.18.4-3 - Replaced puppet-bin.patch with %build section from David's spec * Tue Aug 1 2006 Martin Vuk - 0.18.4-2 - Added supprot for enabling services in SuSE * Tue Aug 1 2006 Martin Vuk - 0.18.4-1 - New version and support for SuSE * Wed Jul 5 2006 David Lutterkort - 0.18.2-1 - New version * Wed Jun 28 2006 David Lutterkort - 0.18.1-1 - Removed lsb-config.patch and yumrepo.patch since they are upstream now * Mon Jun 19 2006 David Lutterkort - 0.18.0-1 - Patch config for LSB compliance (lsb-config.patch) - Changed config moves /var/puppet to /var/lib/puppet, /etc/puppet/ssl to /var/lib/puppet, /etc/puppet/clases.txt to /var/lib/puppet/classes.txt, /etc/puppet/localconfig.yaml to /var/lib/puppet/localconfig.yaml * Fri May 19 2006 David Lutterkort - 0.17.2-1 - Added /usr/bin/puppetrun to server subpackage - Backported patch for yumrepo type (yumrepo.patch) * Wed May 3 2006 David Lutterkort - 0.16.4-1 - Rebuilt * Fri Apr 21 2006 David Lutterkort - 0.16.0-1 - Fix default file permissions in server subpackage - Run puppetmaster as user puppet - rebuilt for 0.16.0 * Mon Apr 17 2006 David Lutterkort - 0.15.3-2 - Don't create empty log files in post-install scriptlet * Fri Apr 7 2006 David Lutterkort - 0.15.3-1 - Rebuilt for new version * Wed Mar 22 2006 David Lutterkort - 0.15.1-1 - Patch0: Run puppetmaster as root; running as puppet is not ready for primetime * Mon Mar 13 2006 David Lutterkort - 0.15.0-1 - Commented out noarch; requires fix for bz184199 * Mon Mar 6 2006 David Lutterkort - 0.14.0-1 - Added BuildRequires for ruby * Wed Mar 1 2006 David Lutterkort - 0.13.5-1 - Removed use of fedora-usermgmt. It is not required for Fedora Extras and makes it unnecessarily hard to use this rpm outside of Fedora. Just allocate the puppet uid/gid dynamically * Sun Feb 19 2006 David Lutterkort - 0.13.0-4 - Use fedora-usermgmt to create puppet user/group. Use uid/gid 24. Fixed problem with listing fileserver.conf and puppetmaster.conf twice * Wed Feb 8 2006 David Lutterkort - 0.13.0-3 - Fix puppetd.conf * Wed Feb 8 2006 David Lutterkort - 0.13.0-2 - Changes to run puppetmaster as user puppet * Mon Feb 6 2006 David Lutterkort - 0.13.0-1 - Don't mark initscripts as config files * Mon Feb 6 2006 David Lutterkort - 0.12.0-2 - Fix BuildRoot. Add dist to release * Tue Jan 17 2006 David Lutterkort - 0.11.0-1 - Rebuild * Thu Jan 12 2006 David Lutterkort - 0.10.2-1 - Updated for 0.10.2 Fixed minor kink in how Source is given * Wed Jan 11 2006 David Lutterkort - 0.10.1-3 - Added basic fileserver.conf * Wed Jan 11 2006 David Lutterkort - 0.10.1-1 - Updated. Moved installation of library files to sitelibdir. Pulled initscripts into separate files. Folded tools rpm into server * Thu Nov 24 2005 Duane Griffin - Added init scripts for the client * Wed Nov 23 2005 Duane Griffin - First packaging diff --git a/ext/nagios/naggen b/ext/nagios/naggen index c6ca15a55..16dbe6ce4 100755 --- a/ext/nagios/naggen +++ b/ext/nagios/naggen @@ -1,302 +1,309 @@ #!/usr/bin/env ruby # # = Synopsis # # Generate Nagios configurations from Puppet Resources in an ActiveRecord database # # = Usage # # naggen [-h|--help] [-d|--debug] [-v|--verbose] [--compare] # # = Description # # This executable is a means of short-circuiting the process of generating nagios # configurations on your server using Puppet Exported Resources. It skips any possible # naming conflicts resulting from Puppet's resource uniqueness requirements, and it # also skips the inefficiencies involved in converting and transporting large numbers # of Puppet resources. # # At the least, the machine that runs this will need ActiveRecord (2.0.2) installed, # along with any database libraries you use. # # = 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. # # You can add naggen-specific settings to your puppet.conf in a '[naggen]' section, # just like any other executable. # # 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'. # # compare:: # Compare new and old files and only backup and write if the files are different. # Potentially expensive computationally, but idempotent. Will exit with 0 if # no changes were made and 1 if there were. # # debug:: # Enable full debugging. # # detailed-exitcodes:: # Provide transaction information via exit codes. If this is enabled, an exit # code of '2' means there were changes, and an exit code of '4' means that there # were failures during the transaction. # # help:: # Print this help message # # verbose:: # Print extra information. # # = Example # # naggen --storeconfigs --confdir /foo --compare # -# = Author # -# Luke Kanies +# = License +# Copyright 2011 Luke Kanies # -# = Copyright +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# Copyright (c) 2009 Puppet Labs, LLC -# Licensed under the GPL 2 +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. require 'puppet' require 'puppet/rails' require 'puppet/rails/resource' require 'puppet/rails/param_value' require 'puppet/network/client' require 'puppet/parser/collector' require 'puppet/provider/naginator' require 'getoptlong' # Monkey-patch the rails resources so we can # easily convert them to nagios instances. class Puppet::Rails::Resource def to_param_hash values = @params_hash || Puppet::Rails::ParamValue.find_all_params_from_resource(self) if values.size == 0 return {} end values.inject({}) do |hash, value| hash[value['name']] ||= [] hash[value['name']] << value["value"] hash end end def to_nagios unless nagios_type = Nagios::Base.type(restype.sub("Nagios_", '').to_sym) raise Puppet::DevError, "Could not find nagios type '%s'" % restype end result = nagios_type.new to_param_hash.each do |param, value| next unless nagios_type.parameter?(param) result[param] = value end result[:name] = self.title result end end class NagiosWriter class FakeScope def debug(string) Puppet.debug string end def host "this host doesn't exist" end end attr_accessor :nagios_type, :bucket def backup(target) return unless FileTest.exist?(target) and File.stat(target).size > 0 Puppet.info "Backing up %s" % target bucket.backup(target) end def collector collector = Puppet::Parser::Collector.new(FakeScope.new, "nagios_" + @nagios_type.to_s, nil, nil, :exported) # We don't have a scope, so we're stubbing everything out that would interact # with the scope. class << collector def collect_virtual(*args) [] end def exported_resource(res) res end end collector end def default_target "/etc/nagios/nagios_#{nagios_type.to_s}.cfg" end def evaluate return unless resources = rails_resources() resources_by_target = resources.inject({}) do |hash, resource| target = resource["target"] || default_target hash[target] ||= [] hash[target] << resource hash end changed = false resources_by_target.each do |target, resources| begin result = write(target, resources) rescue => detail $stderr.puts detail.backtrace Puppet.err "Could not write to %s: %s" % [target, detail] end changed = true if result end changed end def initialize(nagios_type) @nagios_type = nagios_type @bucket = Puppet::FileBucket::Dipper.new(:Path => Puppet[:clientbucketdir]) end def rails_resources collector.send(:collect_exported) end def write(target, resources) # Skip the nagios type when we have no resources and no existing # file. return if resources.empty? and ! FileTest.exist?(target) dir = File.dirname(target) unless FileTest.exist?(dir) FileUtils.mkdir_p(dir) end count = 0 tempfile = target + ".tmp" File.open(tempfile, "w") do |file| resources.each do |resource| count += 1 file.puts resource.to_nagios.to_s.gsub("_naginator_name", Puppet::Provider::Naginator::NAME_STRING) end end if $options[:compare] if FileTest.exist?(target) and File.read(tempfile) == File.read(target) return false end end backup(target) # Atomic rename File.rename(tempfile, target) Puppet.notice "Wrote %s resources to %s" % [count, target] return true ensure File.unlink(tempfile) if tempfile and FileTest.exist?(tempfile) end end arguments = [ [ "--compare", "-c", GetoptLong::NO_ARGUMENT ], [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ] ] Puppet.settings.addargs(arguments) result = GetoptLong.new(*arguments) $options = {} result.each { |opt,arg| case opt when "--help" begin require 'rdoc/usage' RDoc::usage && exit rescue LoadError docs = [] File.readlines(__FILE__).each do |line| next if line =~ /^#\!/ unless line =~ /^#/ next if docs.length == 0 # skip the first line or so break # else, we've passed the docs, so just break end docs << line.sub(/^# ?/, '') end print docs exit end when "--compare" $options[:compare] = true when "--verbose" $options[:verbose] = true when "--debug" $options[:debug] = true when "--debug" $options[:debug] = true else Puppet.settings.handlearg(opt, arg) end } # Read in Puppet settings, so we know how Puppet's configured. Puppet.parse_config Puppet::Util::Log.newdestination(:console) if $options[:debug] Puppet::Util::Log.level = :debug elsif $options[:verbose] Puppet::Util::Log.level = :info end # See if Naginator is installed directly, else load Puppet's version. begin require 'nagios' rescue LoadError require 'puppet/external/nagios' end changed = false Nagios::Base.eachtype do |name, type| writer = NagiosWriter.new(name) changed = true if writer.evaluate end if $options[:compare] and changed exit(1) else exit(0) end diff --git a/ext/puppet-test b/ext/puppet-test index f2648c9ee..affb01249 100755 --- a/ext/puppet-test +++ b/ext/puppet-test @@ -1,560 +1,566 @@ #!/usr/bin/env ruby # == Synopsis # # Test individual client performance. Can compile configurations, describe # files, or retrieve files. # # = Usage # # puppet-test [-c|--compile] [-D|--describe ] [-d|--debug] # [--fork ] [-h|--help] [-H|--hostname ] [-l|--list] [-r|--repeat ] # [-R|--retrieve ] [-t|--test ] [-V|--version] [-v|--verbose] # # = Description # # This is a simple script meant for doing performance tests with Puppet. By # default it pulls down a compiled configuration, but it supports multiple # other tests. # # = Options # # Note that any configuration parameter that's valid in the configuration file # is also a valid long argument. For example, 'server' is a valid configuration # parameter, so you can specify '--server ' 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 puppetd with # '--genconfig'. # # compile:: # Compile the client's configuration. The default. # # debug:: # Enable full debugging. # # describe:: # Describe the file being tested. This is a query to get information about # the file from the server, to determine if it should be copied, and is the # first half of every file transfer. # # fork:: # Fork the specified number of times, thus acting as multiple clients. # # fqdn:: # Set the fully-qualified domain name of the client. This is only used for # certificate purposes, but can be used to override the discovered hostname. # If you need to use this flag, it is generally an indication of a setup problem. # # help:: # Print this help message # # list:: # List all available tests. # # node:: # Specify the node to use. This is useful for looking up cached yaml data # in your :clientyaml directory, and forcing a specific host's configuration to # get compiled. # # pause:: # Pause before starting test (useful for testing with dtrace). # # repeat:: # How many times to perform the test. # # retrieve:: # Test file retrieval performance. Retrieves the specified file from the # remote system. Note that the server should be specified via --server, # so the argument to this option is just the remote module name and path, # e.g., "/dist/apps/myapp/myfile", where "dist" is the module and # "apps/myapp/myfile" is the path to the file relative to the module. # # test:: # Specify the test to run. You can see the list of tests by running this command with --list. # # verbose:: # Turn on verbose reporting. # # version:: # Print the puppet version number and exit. # # = Example # # puppet-test --retrieve /module/path/to/file # -# = Author +# = License +# Copyright 2011 Luke Kanies # -# Luke Kanies +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# = Copyright +# http://www.apache.org/licenses/LICENSE-2.0 # -# Copyright (c) 2005, 2006 Puppet Labs, LLC -# Licensed under the GNU Public License +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # Do an initial trap, so that cancels don't get a stack trace. trap(:INT) do $stderr.puts "Cancelling startup" exit(1) end require 'puppet' require 'puppet/network/client' require 'getoptlong' class Suite attr_reader :name, :doc @@suites = {} @@tests = {} def self.[](name) @@suites[name] end # Run a test by first finding the suite then running the appropriate test. def self.run(test) unless suite_name = @@tests[test] raise "Unknown test %s" % test end unless suite = @@suites[suite_name] raise "Unknown test suite %s from test %s" % [suite_name, test] end suite.run(test) end # What suites are available? def self.suites @@suites.keys end def forked? defined? @forking end # Create a new test suite. def initialize(name, doc, &block) @name = name @doc = doc @tests = {} @@suites[name] = self raise "You must pass a block to the Test" unless block_given? instance_eval(&block) end # Define a new type of test on this suite. def newtest(name, doc, &block) @tests[name] = doc if @@tests[name] raise "Test names must be unique; cannot redefine %s" % name end @@tests[name] = @name meta_def(name, &block) end # Run the actual test. def run(test) unless doc = @tests[test] raise "Suite %s only supports tests %s; not %s" % [@name, @tests.keys.collect { |k| k.to_s }.join(","), test] end puts "Running %s %s test" % [@name, test] prepare() if respond_to?(:prepare) if $options[:pause] puts "Hit any key to continue" $stdin.readline puts "Continuing with test" end if $options[:fork] > 0 @forking = true $options[:fork].times { if pid = fork $pids << pid else break end } end $options[:repeat].times do |i| @count = i if forked? msg = doc + " in PID %s" % Process.pid else msg = doc end Puppet::Util.benchmark(:notice, msg) do begin send(test) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "%s failed: %s" % [@name, detail.to_s] end end end end # What tests are available on this suite? def tests @tests.keys end end Suite.new :parser, "Manifest parsing" do newtest :parse, "Parsed files" do @parser = Puppet::Parser::Parser.new(:environment => Puppet[:environment]) @parser.file = Puppet[:manifest] @parser.parse end end Suite.new :local_catalog, "Local catalog handling" do def prepare @node = Puppet::Node.find($options[:nodes][0]) end newtest :compile, "Compiled catalog" do Puppet::Resource::Catalog.find(@node) end end Suite.new :resource_type, "Managing resource types" do newtest :find, "Find a type" do Puppet::Resource::Type.terminus_class = :parser ARGV.each do |name| json = Puppet::Resource::Type.find(name).to_pson data = PSON.parse(json) p Puppet::Resource::Type.from_pson(data) end end newtest :search_types, "Find all types" do Puppet::Resource::Type.terminus_class = :rest result = Puppet::Resource::Type.search("*") result.each { |r| p r } end newtest :restful_type, "Find a type and return it via REST" do Puppet::Resource::Type.terminus_class = :rest ARGV.each do |name| p Puppet::Resource::Type.find(name) end end end Suite.new :remote_catalog, "Remote catalog handling" do def prepare $args[:cache] = false # Create a config client and pull the config down @client = Puppet::Network::Client.master.new($args) unless @client.read_cert fail "Could not read client certificate" end if tmp = Puppet::Node::Facts.find($options[:nodes][0]) @facts = tmp.values else raise "Could not find facts for %s" % $optins[:nodes][0] end if host = $options[:fqdn] @facts["fqdn"] = host @facts["hostname"] = host.sub(/\..+/, '') @facts["domain"] = host.sub(/^[^.]+\./, '') end @facts = YAML.dump(@facts) end newtest :getconfig, "Compiled catalog" do @client.driver.getconfig(@facts, "yaml") end # This test will always force a false answer. newtest :fresh, "Checked freshness" do @client.driver.freshness end end Suite.new :file, "File interactions" do def prepare unless $options[:file] fail "You must specify a file (using --file ) to interact with on the server" end @client = Puppet::Network::Client.file.new($args) unless @client.read_cert fail "Could not read client certificate" end end newtest :describe, "Described file" do @client.describe($options[:file], :ignore) end newtest :retrieve, "Retrieved file" do @client.retrieve($options[:file], :ignore) end end Suite.new :filebucket, "Filebucket interactions" do def prepare require 'tempfile' @client = Puppet::FileBucket::Dipper.new($args) end newtest :backup, "Backed up file" do Tempfile.open("bucket_testing") do |f| f.print rand(1024) f.close @client.backup(f.path) end end end # Note that this uses an env variable to determine how many resources per # host to create (with a default of 10). 'repeat' determines how # many hosts to create. You probably will get mad failures if you # use forking and sqlite. # Here's an example run of this test, using sqlite: # RESOURCE_COUNT=50 ext/puppet-test --suite rails --test storage --confdir /tmp/storagetesting --vardir /tmp/storagetesting --repeat 10 Suite.new :rails, "Rails Interactions" do def prepare Puppet::Rails.init @facts = Facter.to_hash if num = ENV["RESOURCECOUNT"] @resources = Integer(num) else @resources = 10 end end Resource = Puppet::Parser::Resource def execute(test, msg) begin send(test) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "%s failed: %s" % [@name, detail.to_s] end end def mkresource(type, title, parameters) source = "fakesource" res = Resource.new(:type => type, :title => title, :source => source, :scope => "fakescope") parameters.each do |param, value| res.set(param, value, source) end res end def ref(type, title) Resource::Reference.new(:type => type, :title => title) end newtest :storage, "Stored resources" do hostname = "host%s" % @count @facts["hostname"] = hostname args = {:facts => @facts, :name => hostname} # Make all of the resources we want. Make them at least slightly complex, # so we model real resources as close as we can. resources = [] args[:resources] = resources @resources.times do |resnum| exec = mkresource("exec", "exec%s" % resnum, :command => "/bin/echo do something %s" % resnum) exec.tags = %w{exec one} << "exec%s" % resnum user = mkresource("user", "user%s" % resnum, :uid => resnum.to_s, :require => ref("exec", "exec%s" % resnum)) user.tags = %w{user one two} << "user%s" % resnum file = mkresource("file", "/tmp/file%s" % resnum, :owner => resnum.to_s, :require => [ref("exec", "exec%s" % resnum), ref("user", "user%s" % resnum)]) file.tags = %w{file one three} << "file%s" % resnum file.exported = true resources << exec << user << file end Puppet::Rails::Host.store(args) end end Suite.new :report, "Reports interactions" do def prepare Puppet::Transaction::Report.terminus_class = :rest end newtest :empty, "send empty report" do report = Puppet::Transaction::Report.new report.time = Time.now report.save end newtest :fake, "send fake report" do report = Puppet::Transaction::Report.new resourcemetrics = { :total => 12, :out_of_sync => 20, :applied => 45, :skipped => 1, :restarted => 23, :failed_restarts => 1, :scheduled => 10 } report.newmetric(:resources, resourcemetrics) timemetrics = { :resource1 => 10, :resource2 => 50, :resource3 => 40, :resource4 => 20, } report.newmetric(:times, timemetrics) report.newmetric(:changes, :total => 20 ) report.time = Time.now report.save end end $cmdargs = [ [ "--compile", "-c", GetoptLong::NO_ARGUMENT ], [ "--describe", GetoptLong::REQUIRED_ARGUMENT ], [ "--retrieve", "-R", GetoptLong::REQUIRED_ARGUMENT ], [ "--fork", GetoptLong::REQUIRED_ARGUMENT ], [ "--fqdn", "-F", GetoptLong::REQUIRED_ARGUMENT ], [ "--suite", "-s", GetoptLong::REQUIRED_ARGUMENT ], [ "--test", "-t", GetoptLong::REQUIRED_ARGUMENT ], [ "--pause", "-p", GetoptLong::NO_ARGUMENT ], [ "--repeat", "-r", GetoptLong::REQUIRED_ARGUMENT ], [ "--node", "-n", GetoptLong::REQUIRED_ARGUMENT ], [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--list", "-l", GetoptLong::NO_ARGUMENT ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--version", "-V", GetoptLong::NO_ARGUMENT ], ] # Add all of the config parameters as valid $options. Puppet.settings.addargs($cmdargs) Puppet::Util::Log.newdestination(:console) Puppet::Node.terminus_class = :plain Puppet::Node.cache_class = :yaml Puppet::Node::Facts.terminus_class = :facter Puppet::Node::Facts.cache_class = :yaml result = GetoptLong.new(*$cmdargs) $args = {} $options = {:repeat => 1, :fork => 0, :pause => false, :nodes => []} begin explicit_waitforcert = false result.each { |opt,arg| case opt # First check to see if the argument is a valid configuration parameter; # if so, set it. when "--compile" $options[:suite] = :configuration $options[:test] = :compile when "--retrieve" $options[:suite] = :file $options[:test] = :retrieve $options[:file] = arg when "--fork" begin $options[:fork] = Integer(arg) rescue => detail $stderr.puts "The argument to 'fork' must be an integer" exit(14) end when "--describe" $options[:suite] = :file $options[:test] = :describe $options[:file] = arg when "--fqdn" $options[:fqdn] = arg when "--repeat" $options[:repeat] = Integer(arg) when "--help" if Puppet.features.usage? RDoc::usage && exit else puts "No help available unless you have RDoc::usage installed" exit end when "--version" puts "%s" % Puppet.version exit when "--verbose" Puppet::Util::Log.level = :info Puppet::Util::Log.newdestination(:console) when "--debug" Puppet::Util::Log.level = :debug Puppet::Util::Log.newdestination(:console) when "--suite" $options[:suite] = arg.intern when "--test" $options[:test] = arg.intern when "--file" $options[:file] = arg when "--pause" $options[:pause] = true when "--node" $options[:nodes] << arg when "--list" Suite.suites.sort { |a,b| a.to_s <=> b.to_s }.each do |suite_name| suite = Suite[suite_name] tests = suite.tests.sort { |a,b| a.to_s <=> b.to_s }.join(", ") puts "%20s: %s" % [suite_name, tests] end exit(0) else Puppet.settings.handlearg(opt, arg) end } rescue GetoptLong::InvalidOption => detail $stderr.puts detail $stderr.puts "Try '#{$0} --help'" exit(1) end # Now parse the config Puppet.parse_config $options[:nodes] << Puppet.settings[:certname] if $options[:nodes].empty? $args[:Server] = Puppet[:server] unless $options[:test] $options[:suite] = :remote_catalog $options[:test] = :getconfig end unless $options[:test] raise "A suite was specified without a test" end $pids = [] Suite.run($options[:test]) if $options[:fork] > 0 Process.waitall end diff --git a/ext/yaml_nodes.rb b/ext/yaml_nodes.rb index 2174da09d..3c7077c13 100755 --- a/ext/yaml_nodes.rb +++ b/ext/yaml_nodes.rb @@ -1,99 +1,105 @@ #!/usr/bin/ruby # # = Synopsis # # Use YAML files to provide external node support. # # = Usage # # yaml-nodes # # = Description # # This is a simple example external node script. It allows you to maintain your # node information in yaml files, and it will find a given node's file and produce # it on stdout. It has simple inheritance, in that a node can specify a parent # node, and the node will inherit that parent's classes and parameters. # # = Options # # help:: # Print this help message # # yamldir:: # Specify where the yaml is found. Defaults to 'yaml' in the current directory. # -# = Author +# = License +# Copyright 2011 Luke Kanies # -# Luke Kanies +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# = Copyright +# http://www.apache.org/licenses/LICENSE-2.0 # -# Copyright (c) 2009 Puppet Labs, Inc. -# Licensed under the GPL2 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. require 'yaml' require 'optparse' BASEDIR = Dir.chdir(File.dirname(__FILE__) + "/..") { Dir.getwd } options = {:yamldir => File.join(BASEDIR, "yaml")} OptionParser.new do |opts| opts.banner = "Usage: yaml-nodes [options] " opts.on("-y dir", "--yamldir dir", "Specify the directory with the YAML files") do |arg| raise "YAML directory #{arg} does not exist or is not a directory" unless FileTest.directory?(arg) options[:yamldir] = arg end opts.on("-h", "--help", "Print this help") do puts opts.help exit(0) end end.parse! # Read in a pure yaml representation of our node. def read_node(node) nodefile = File.join(YAMLDIR, "#{node}.yaml") if FileTest.exist?(nodefile) return YAML.load_file(nodefile) else raise "Could not find information for #{node}" end end node = ARGV[0] info = read_node(node) # Iterate over any provided parents, merging in there information. parents_seen = [] while parent = info["parent"] raise "Found inheritance loop with parent #{parent}" if parents_seen.include?(parent) parents_seen << parent info.delete("parent") parent_info = read_node(parent) # Include any parent classes in our list. if pclasses = parent_info["classes"] info["classes"] += pclasses info["classes"].uniq! end # And inherit parameters from our parent, while preferring our own values. if pparams = parent_info["parameters"] # When using Hash#merge, the hash being merged in wins, and we # want the subnode parameters to be the parent node parameters. info["parameters"] = pparams.merge(info["parameters"]) end # Copy over any parent node name. if pparent = parent_info["parent"] info["parent"] = pparent end end puts YAML.dump(info) diff --git a/install.rb b/install.rb index 6bf4f5587..dcff82403 100755 --- a/install.rb +++ b/install.rb @@ -1,442 +1,442 @@ #! /usr/bin/env ruby #-- # Copyright 2004 Austin Ziegler # Install utility. Based on the original installation script for rdoc by the # Pragmatic Programmers. # # This program is free software. It may be redistributed and/or modified under # the terms of the GPL version 2 (or later) or the Ruby licence. # # Usage # ----- # In most cases, if you have a typical project layout, you will need to do # absolutely nothing to make this work for you. This layout is: # # bin/ # executable files -- "commands" # lib/ # the source of the library # tests/ # unit tests # # The default behaviour: # 1) Run all unit test files (ending in .rb) found in all directories under # tests/. # 2) Build Rdoc documentation from all files in bin/ (excluding .bat and .cmd), # all .rb files in lib/, ./README, ./ChangeLog, and ./Install. # 3) Build ri documentation from all files in bin/ (excluding .bat and .cmd), # and all .rb files in lib/. This is disabled by default on Microsoft Windows. # 4) Install commands from bin/ into the Ruby bin directory. On Windows, if a # if a corresponding batch file (.bat or .cmd) exists in the bin directory, # it will be copied over as well. Otherwise, a batch file (always .bat) will # be created to run the specified command. # 5) Install all library files ending in .rb from lib/ into Ruby's # site_lib/version directory. # #++ require 'rbconfig' require 'find' require 'fileutils' begin require 'ftools' # apparently on some system ftools doesn't get loaded $haveftools = true rescue LoadError puts "ftools not found. Using FileUtils instead.." $haveftools = false end require 'optparse' require 'ostruct' begin require 'rdoc/rdoc' $haverdoc = true rescue LoadError puts "Missing rdoc; skipping documentation" $haverdoc = false end PREREQS = %w{openssl facter xmlrpc/client xmlrpc/server cgi} MIN_FACTER_VERSION = 1.5 InstallOptions = OpenStruct.new def glob(list) g = list.map { |i| Dir.glob(i) } g.flatten! g.compact! g.reject! { |e| e =~ /\.svn/ } g end # Set these values to what you want installed. configs = glob(%w{conf/auth.conf}) sbins = glob(%w{sbin/*}) bins = glob(%w{bin/*}) rdoc = glob(%w{bin/* sbin/* lib/**/*.rb README README-library CHANGELOG TODO Install}).reject { |e| e=~ /\.(bat|cmd)$/ } ri = glob(%w{bin/*.rb sbin/* lib/**/*.rb}).reject { |e| e=~ /\.(bat|cmd)$/ } man = glob(%w{man/man[0-9]/*}) -libs = glob(%w{lib/**/*.rb lib/**/*.py lib/puppet/util/command_line/*}) +libs = glob(%w{lib/**/*.rb lib/**/*.erb lib/**/*.py lib/puppet/util/command_line/*}) tests = glob(%w{test/**/*.rb}) def do_configs(configs, target, strip = 'conf/') Dir.mkdir(target) unless File.directory? target configs.each do |cf| ocf = File.join(InstallOptions.config_dir, cf.gsub(/#{strip}/, '')) if $haveftools File.install(cf, ocf, 0644, true) else FileUtils.install(cf, ocf, {:mode => 0644, :verbose => true}) end end end def do_bins(bins, target, strip = 's?bin/') Dir.mkdir(target) unless File.directory? target bins.each do |bf| obf = bf.gsub(/#{strip}/, '') install_binfile(bf, obf, target) end end def do_libs(libs, strip = 'lib/') libs.each do |lf| olf = File.join(InstallOptions.site_dir, lf.gsub(/#{strip}/, '')) op = File.dirname(olf) if $haveftools File.makedirs(op, true) File.chmod(0755, op) File.install(lf, olf, 0644, true) else FileUtils.makedirs(op, {:mode => 0755, :verbose => true}) FileUtils.chmod(0755, op) FileUtils.install(lf, olf, {:mode => 0644, :verbose => true}) end end end def do_man(man, strip = 'man/') man.each do |mf| omf = File.join(InstallOptions.man_dir, mf.gsub(/#{strip}/, '')) om = File.dirname(omf) if $haveftools File.makedirs(om, true) File.chmod(0755, om) File.install(mf, omf, 0644, true) else FileUtils.makedirs(om, {:mode => 0755, :verbose => true}) FileUtils.chmod(0755, om) FileUtils.install(mf, omf, {:mode => 0644, :verbose => true}) end gzip = %x{which gzip} gzip.chomp! %x{#{gzip} -f #{omf}} end end # Verify that all of the prereqs are installed def check_prereqs PREREQS.each { |pre| begin require pre if pre == "facter" # to_f isn't quite exact for strings like "1.5.1" but is good # enough for this purpose. facter_version = Facter.version.to_f if facter_version < MIN_FACTER_VERSION puts "Facter version: #{facter_version}; minimum required: #{MIN_FACTER_VERSION}; cannot install" exit -1 end end rescue LoadError puts "Could not load #{pre}; cannot install" exit -1 end } end ## # Prepare the file installation. # def prepare_installation $operatingsystem = Facter["operatingsystem"].value InstallOptions.configs = true # Only try to do docs if we're sure they have rdoc if $haverdoc InstallOptions.rdoc = true InstallOptions.ri = $operatingsystem != "windows" else InstallOptions.rdoc = false InstallOptions.ri = false end InstallOptions.tests = true ARGV.options do |opts| opts.banner = "Usage: #{File.basename($0)} [options]" opts.separator "" opts.on('--[no-]rdoc', 'Prevents the creation of RDoc output.', 'Default on.') do |onrdoc| InstallOptions.rdoc = onrdoc end opts.on('--[no-]ri', 'Prevents the creation of RI output.', 'Default off on mswin32.') do |onri| InstallOptions.ri = onri end opts.on('--[no-]tests', 'Prevents the execution of unit tests.', 'Default on.') do |ontest| InstallOptions.tests = ontest end opts.on('--[no-]configs', 'Prevents the installation of config files', 'Default off.') do |ontest| InstallOptions.configs = ontest end opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir| InstallOptions.destdir = destdir end opts.on('--configdir[=OPTIONAL]', 'Installation directory for config files', 'Default /etc/puppet') do |configdir| InstallOptions.configdir = configdir end opts.on('--bindir[=OPTIONAL]', 'Installation directory for binaries', 'overrides Config::CONFIG["bindir"]') do |bindir| InstallOptions.bindir = bindir end opts.on('--sbindir[=OPTIONAL]', 'Installation directory for system binaries', 'overrides Config::CONFIG["sbindir"]') do |sbindir| InstallOptions.sbindir = sbindir end opts.on('--sitelibdir[=OPTIONAL]', 'Installation directory for libraries', 'overrides Config::CONFIG["sitelibdir"]') do |sitelibdir| InstallOptions.sitelibdir = sitelibdir end opts.on('--mandir[=OPTIONAL]', 'Installation directory for man pages', 'overrides Config::CONFIG["mandir"]') do |mandir| InstallOptions.mandir = mandir end opts.on('--quick', 'Performs a quick installation. Only the', 'installation is done.') do |quick| InstallOptions.rdoc = false InstallOptions.ri = false InstallOptions.tests = false InstallOptions.configs = true end opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full| InstallOptions.rdoc = true InstallOptions.ri = true InstallOptions.tests = true InstallOptions.configs = true end opts.separator("") opts.on_tail('--help', "Shows this help text.") do $stderr.puts opts exit end opts.parse! end tmpdirs = [ENV['TMP'], ENV['TEMP'], "/tmp", "/var/tmp", "."] version = [Config::CONFIG["MAJOR"], Config::CONFIG["MINOR"]].join(".") libdir = File.join(Config::CONFIG["libdir"], "ruby", version) # Mac OS X 10.5 and higher declare bindir and sbindir as # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/sbin # which is not generally where people expect executables to be installed # These settings are appropriate defaults for all OS X versions. if RUBY_PLATFORM =~ /^universal-darwin[\d\.]+$/ Config::CONFIG['bindir'] = "/usr/bin" Config::CONFIG['sbindir'] = "/usr/sbin" end if not InstallOptions.configdir.nil? configdir = InstallOptions.configdir else configdir = "/etc/puppet" end if not InstallOptions.bindir.nil? bindir = InstallOptions.bindir else bindir = Config::CONFIG['bindir'] end if not InstallOptions.sbindir.nil? sbindir = InstallOptions.sbindir else sbindir = Config::CONFIG['sbindir'] end if not InstallOptions.sitelibdir.nil? sitelibdir = InstallOptions.sitelibdir else sitelibdir = Config::CONFIG["sitelibdir"] if sitelibdir.nil? sitelibdir = $LOAD_PATH.find { |x| x =~ /site_ruby/ } if sitelibdir.nil? sitelibdir = File.join(libdir, "site_ruby") elsif sitelibdir !~ Regexp.quote(version) sitelibdir = File.join(sitelibdir, version) end end end if not InstallOptions.mandir.nil? mandir = InstallOptions.mandir else mandir = Config::CONFIG['mandir'] end # This is the new way forward if not InstallOptions.destdir.nil? destdir = InstallOptions.destdir # To be deprecated once people move over to using --destdir option elsif ENV['DESTDIR'] != nil? destdir = ENV['DESTDIR'] warn "DESTDIR is deprecated. Use --destdir instead." else destdir = '' end configdir = "#{destdir}#{configdir}" bindir = "#{destdir}#{bindir}" sbindir = "#{destdir}#{sbindir}" mandir = "#{destdir}#{mandir}" sitelibdir = "#{destdir}#{sitelibdir}" FileUtils.makedirs(configdir) if InstallOptions.configs FileUtils.makedirs(bindir) FileUtils.makedirs(sbindir) FileUtils.makedirs(mandir) FileUtils.makedirs(sitelibdir) tmpdirs << bindir InstallOptions.tmp_dirs = tmpdirs.compact InstallOptions.site_dir = sitelibdir InstallOptions.config_dir = configdir InstallOptions.bin_dir = bindir InstallOptions.sbin_dir = sbindir InstallOptions.lib_dir = libdir InstallOptions.man_dir = mandir end ## # Build the rdoc documentation. Also, try to build the RI documentation. # def build_rdoc(files) return unless $haverdoc begin r = RDoc::RDoc.new r.document(["--main", "README", "--title", "Puppet -- Site Configuration Management", "--line-numbers"] + files) rescue RDoc::RDocError => e $stderr.puts e.message rescue Exception => e $stderr.puts "Couldn't build RDoc documentation\n#{e.message}" end end def build_ri(files) return unless $haverdoc begin ri = RDoc::RDoc.new #ri.document(["--ri-site", "--merge"] + files) ri.document(["--ri-site"] + files) rescue RDoc::RDocError => e $stderr.puts e.message rescue Exception => e $stderr.puts "Couldn't build Ri documentation\n#{e.message}" $stderr.puts "Continuing with install..." end end def run_tests(test_list) require 'test/unit/ui/console/testrunner' $LOAD_PATH.unshift "lib" test_list.each do |test| next if File.directory?(test) require test end tests = [] ObjectSpace.each_object { |o| tests << o if o.kind_of?(Class) } tests.delete_if { |o| !o.ancestors.include?(Test::Unit::TestCase) } tests.delete_if { |o| o == Test::Unit::TestCase } tests.each { |test| Test::Unit::UI::Console::TestRunner.run(test) } $LOAD_PATH.shift rescue LoadError puts "Missing testrunner library; skipping tests" end ## # Install file(s) from ./bin to Config::CONFIG['bindir']. Patch it on the way # to insert a #! line; on a Unix install, the command is named as expected # (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under # windows, we add an '.rb' extension and let file associations do their stuff. def install_binfile(from, op_file, target) tmp_dir = nil InstallOptions.tmp_dirs.each do |t| if File.directory?(t) and File.writable?(t) tmp_dir = t break end end fail "Cannot find a temporary directory" unless tmp_dir tmp_file = File.join(tmp_dir, '_tmp') ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) File.open(from) do |ip| File.open(tmp_file, "w") do |op| ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) op.puts "#!#{ruby}" contents = ip.readlines contents.shift if contents[0] =~ /^#!/ op.write contents.join end end if $operatingsystem == "windows" installed_wrapper = false if File.exists?("#{from}.bat") FileUtils.install("#{from}.bat", File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true) installed_wrapper = true end if File.exists?("#{from}.cmd") FileUtils.install("#{from}.cmd", File.join(target, "#{op_file}.cmd"), :mode => 0755, :verbose => true) installed_wrapper = true end if not installed_wrapper tmp_file2 = File.join(tmp_dir, '_tmp_wrapper') cwn = File.join(Config::CONFIG['bindir'], op_file) cwv = CMD_WRAPPER.gsub('', ruby.gsub(%r{/}) { "\\" }).gsub!('', cwn.gsub(%r{/}) { "\\" } ) File.open(tmp_file2, "wb") { |cw| cw.puts cwv } FileUtils.install(tmp_file2, File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true) File.unlink(tmp_file2) installed_wrapper = true end end FileUtils.install(tmp_file, File.join(target, op_file), :mode => 0755, :verbose => true) File.unlink(tmp_file) end CMD_WRAPPER = <<-EOS @echo off if "%OS%"=="Windows_NT" goto WinNT -x "" %1 %2 %3 %4 %5 %6 %7 %8 %9 goto done :WinNT -x "" %* goto done :done EOS check_prereqs prepare_installation #run_tests(tests) if InstallOptions.tests #build_rdoc(rdoc) if InstallOptions.rdoc #build_ri(ri) if InstallOptions.ri do_configs(configs, InstallOptions.config_dir) if InstallOptions.configs do_bins(sbins, InstallOptions.sbin_dir) do_bins(bins, InstallOptions.bin_dir) do_libs(libs) do_man(man) unless $operatingsystem == "windows" diff --git a/lib/puppet.rb b/lib/puppet.rb index ef5fb7f06..64236576a 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -1,165 +1,160 @@ # Try to load rubygems. Hey rubygems, I hate you. begin require 'rubygems' rescue LoadError end # see the bottom of the file for further inclusions require 'singleton' require 'facter' require 'puppet/error' require 'puppet/util' require 'puppet/util/autoload' require 'puppet/util/settings' require 'puppet/util/feature' require 'puppet/util/suidmanager' require 'puppet/util/run_mode' #------------------------------------------------------------ # the top-level module # # all this really does is dictate how the whole system behaves, through # preferences for things like debugging # # it's also a place to find top-level commands like 'debug' module Puppet - PUPPETVERSION = '2.6.7' + PUPPETVERSION = '2.6.8' def Puppet.version PUPPETVERSION end class << self include Puppet::Util attr_reader :features attr_writer :name end # the hash that determines how our system behaves @@settings = Puppet::Util::Settings.new # The services running in this process. @services ||= [] require 'puppet/util/logging' extend Puppet::Util::Logging # The feature collection @features = Puppet::Util::Feature.new('puppet/feature') # Load the base features. require 'puppet/feature/base' # Store a new default value. def self.setdefaults(section, hash) @@settings.setdefaults(section, hash) end # configuration parameter access and stuff def self.[](param) case param when :debug return Puppet::Util::Log.level == :debug else return @@settings[param] end end # configuration parameter access and stuff def self.[]=(param,value) @@settings[param] = value end def self.clear @@settings.clear end def self.debug=(value) if value Puppet::Util::Log.level=(:debug) else Puppet::Util::Log.level=(:notice) end end def self.settings @@settings end def self.run_mode $puppet_application_mode || Puppet::Util::RunMode[:user] end def self.application_name $puppet_application_name ||= "apply" end # Load all of the configuration parameters. require 'puppet/defaults' def self.genmanifest if Puppet[:genmanifest] puts Puppet.settings.to_manifest exit(0) end end # Parse the config file for this process. def self.parse_config Puppet.settings.parse end # XXX this should all be done using puppet objects, not using # normal mkdir def self.recmkdir(dir,mode = 0755) if FileTest.exist?(dir) return false else tmp = dir.sub(/^\//,'') path = [File::SEPARATOR] tmp.split(File::SEPARATOR).each { |dir| path.push dir if ! FileTest.exist?(File.join(path)) begin Dir.mkdir(File.join(path), mode) rescue Errno::EACCES => detail Puppet.err detail.to_s return false rescue => detail Puppet.err "Could not create #{path}: #{detail}" return false end elsif FileTest.directory?(File.join(path)) next else FileTest.exist?(File.join(path)) raise Puppet::Error, "Cannot create #{dir}: basedir #{File.join(path)} is a file" end } return true end end - # Create a new type. Just proxy to the Type class. + # Create a new type. Just proxy to the Type class. The mirroring query + # code was deprecated in 2008, but this is still in heavy use. I suppose + # this can count as a soft deprecation for the next dev. --daniel 2011-04-12 def self.newtype(name, options = {}, &block) Puppet::Type.newtype(name, options, &block) end - - # Retrieve a type by name. Just proxy to the Type class. - def self.type(name) - # LAK:DEP Deprecation notice added 12/17/2008 - Puppet.warning "Puppet.type is deprecated; use Puppet::Type.type" - Puppet::Type.type(name) - end end require 'puppet/type' require 'puppet/parser' require 'puppet/resource' require 'puppet/network' require 'puppet/ssl' require 'puppet/module' require 'puppet/util/storage' require 'puppet/status' require 'puppet/file_bucket/file' diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index 57bd88877..374dc850b 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -1,410 +1,416 @@ require 'optparse' require 'puppet/util/plugins' # This class handles all the aspects of a Puppet application/executable # * setting up options # * setting up logs # * choosing what to run # * representing execution status # # === Usage # An application is a subclass of Puppet::Application. # # For legacy compatibility, # Puppet::Application[:example].run # is equivalent to # Puppet::Application::Example.new.run # # # class Puppet::Application::Example << Puppet::Application # # def preinit # # perform some pre initialization # @all = false # end # # # run_command is called to actually run the specified command # def run_command # send Puppet::Util::CommandLine.new.args.shift # end # # # option uses metaprogramming to create a method # # and also tells the option parser how to invoke that method # option("--arg ARGUMENT") do |v| # @args << v # end # # option("--debug", "-d") do |v| # @debug = v # end # # option("--all", "-a:) do |v| # @all = v # end # # def handle_unknown(opt,arg) # # last chance to manage an option # ... # # let's say to the framework we finally handle this option # true # end # # def read # # read action # end # # def write # # writeaction # end # # end # # === Preinit # The preinit block is the first code to be called in your application, before option parsing, # setup or command execution. # # === Options # Puppet::Application uses +OptionParser+ to manage the application options. # Options are defined with the +option+ method to which are passed various # arguments, including the long option, the short option, a description... # Refer to +OptionParser+ documentation for the exact format. # * If the option method is given a block, this one will be called whenever # the option is encountered in the command-line argument. # * If the option method has no block, a default functionnality will be used, that # stores the argument (or true/false if the option doesn't require an argument) in # the global (to the application) options array. # * If a given option was not defined by a the +option+ method, but it exists as a Puppet settings: # * if +unknown+ was used with a block, it will be called with the option name and argument # * if +unknown+ wasn't used, then the option/argument is handed to Puppet.settings.handlearg for # a default behavior # # --help is managed directly by the Puppet::Application class, but can be overriden. # # === Setup # Applications can use the setup block to perform any initialization. # The defaul +setup+ behaviour is to: read Puppet configuration and manage log level and destination # # === What and how to run # If the +dispatch+ block is defined it is called. This block should return the name of the registered command # to be run. # If it doesn't exist, it defaults to execute the +main+ command if defined. # # === Execution state # The class attributes/methods of Puppet::Application serve as a global place to set and query the execution # status of the application: stopping, restarting, etc. The setting of the application status does not directly # aftect its running status; it's assumed that the various components within the application will consult these # settings appropriately and affect their own processing accordingly. Control operations (signal handlers and # the like) should set the status appropriately to indicate to the overall system that it's the process of # stopping or restarting (or just running as usual). # # So, if something in your application needs to stop the process, for some reason, you might consider: # # def stop_me! # # indicate that we're stopping # Puppet::Application.stop! # # ...do stuff... # end # # And, if you have some component that involves a long-running process, you might want to consider: # # def my_long_process(giant_list_to_munge) # giant_list_to_munge.collect do |member| # # bail if we're stopping # return if Puppet::Application.stop_requested? # process_member(member) # end # end module Puppet class Application require 'puppet/util' include Puppet::Util DOCPATTERN = File.expand_path(File.dirname(__FILE__) + "/util/command_line/*" ) class << self include Puppet::Util attr_accessor :run_status def clear! self.run_status = nil end def stop! self.run_status = :stop_requested end def restart! self.run_status = :restart_requested end # Indicates that Puppet::Application.restart! has been invoked and components should # do what is necessary to facilitate a restart. def restart_requested? :restart_requested == run_status end # Indicates that Puppet::Application.stop! has been invoked and components should do what is necessary # for a clean stop. def stop_requested? :stop_requested == run_status end # Indicates that one of stop! or start! was invoked on Puppet::Application, and some kind of process # shutdown/short-circuit may be necessary. def interrupted? [:restart_requested, :stop_requested].include? run_status end # Indicates that Puppet::Application believes that it's in usual running run_mode (no stop/restart request # currently active). def clear? run_status.nil? end # Only executes the given block if the run status of Puppet::Application is clear (no restarts, stops, # etc. requested). # Upon block execution, checks the run status again; if a restart has been requested during the block's # execution, then controlled_run will send a new HUP signal to the current process. # Thus, long-running background processes can potentially finish their work before a restart. def controlled_run(&block) return unless clear? result = block.call Process.kill(:HUP, $PID) if restart_requested? result end def should_parse_config @parse_config = true end def should_not_parse_config @parse_config = false end def should_parse_config? @parse_config = true if ! defined?(@parse_config) @parse_config end # used to declare code that handle an option def option(*options, &block) long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ =]+).*$/, '\1' ).gsub('-','_') fname = symbolize("handle_#{long}") if (block_given?) define_method(fname, &block) else define_method(fname) do |value| self.options["#{long}".to_sym] = value end end self.option_parser_commands << [options, fname] end def banner(banner = nil) @banner ||= banner end def option_parser_commands @option_parser_commands ||= ( superclass.respond_to?(:option_parser_commands) ? superclass.option_parser_commands.dup : [] ) @option_parser_commands end def find(name) klass = name.to_s.capitalize # const_defined? is used before const_get since const_defined? will only # check within our namespace, whereas const_get will check ancestor # trees as well, resulting in unexpected behaviour. if !self.const_defined?(klass) puts "Unable to find application '#{name.to_s}'." Kernel::exit(1) end self.const_get(klass) end def [](name) find(name).new end # Sets or gets the run_mode name. Sets the run_mode name if a mode_name is # passed. Otherwise, gets the run_mode or a default run_mode # def run_mode( mode_name = nil) return @run_mode if @run_mode and not mode_name require 'puppet/util/run_mode' @run_mode = Puppet::Util::RunMode[ mode_name || :user ] end end attr_reader :options, :command_line # Every app responds to --version option("--version", "-V") do |arg| puts "#{Puppet.version}" exit end # Every app responds to --help option("--help", "-h") do |v| puts help exit end def should_parse_config? self.class.should_parse_config? end # override to execute code before running anything else def preinit end def initialize(command_line = nil) require 'puppet/util/command_line' @command_line = command_line || Puppet::Util::CommandLine.new set_run_mode self.class.run_mode @options = {} require 'puppet' end # WARNING: This is a totally scary, frightening, and nasty internal API. We # strongly advise that you do not use this, and if you insist, we will # politely allow you to keep both pieces of your broken code. # # We plan to provide a supported, long-term API to deliver this in a way # that you can use. Please make sure that you let us know if you do require # this, and this message is still present in the code. --daniel 2011-02-03 def set_run_mode(mode) @run_mode = mode $puppet_application_mode = @run_mode $puppet_application_name = name if Puppet.respond_to? :settings # This is to reduce the amount of confusion in rspec # because it might have loaded defaults.rb before the globals were set # and thus have the wrong defaults for the current application Puppet.settings.set_value(:confdir, Puppet.run_mode.conf_dir, :mutable_defaults) Puppet.settings.set_value(:vardir, Puppet.run_mode.var_dir, :mutable_defaults) Puppet.settings.set_value(:name, Puppet.application_name.to_s, :mutable_defaults) Puppet.settings.set_value(:logdir, Puppet.run_mode.logopts, :mutable_defaults) Puppet.settings.set_value(:rundir, Puppet.run_mode.run_dir, :mutable_defaults) Puppet.settings.set_value(:run_mode, Puppet.run_mode.name.to_s, :mutable_defaults) end end # This is the main application entry point def run - exit_on_fail("initialize") { hook('preinit') { preinit } } - exit_on_fail("parse options") { hook('parse_options') { parse_options } } - exit_on_fail("parse configuration file") { Puppet.settings.parse } if should_parse_config? - exit_on_fail("prepare for execution") { hook('setup') { setup } } - exit_on_fail("run") { hook('run_command') { run_command } } + exit_on_fail("initialize") { hook('preinit') { preinit } } + exit_on_fail("parse options") { hook('parse_options') { parse_options } } + exit_on_fail("parse configuration file") { Puppet.settings.parse } if should_parse_config? + exit_on_fail("prepare for execution") { hook('setup') { setup } } + exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes } + exit_on_fail("run") { hook('run_command') { run_command } } end def main raise NotImplementedError, "No valid command or main" end def run_command main end def setup # Handle the logging settings if options[:debug] or options[:verbose] Puppet::Util::Log.newdestination(:console) if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end end Puppet::Util::Log.newdestination(:syslog) unless options[:setdest] end + def configure_indirector_routes + route_file = Puppet[:route_file] + if ::File.exists?(route_file) + routes = YAML.load_file(route_file) + application_routes = routes[name.to_s] + Puppet::Indirector.configure_routes(application_routes) if application_routes + end + end + def parse_options # Create an option parser option_parser = OptionParser.new(self.class.banner) # Add all global options to it. Puppet.settings.optparse_addargs([]).each do |option| option_parser.on(*option) do |arg| handlearg(option[0], arg) end end # Add options that are local to this application, which were # created using the "option()" metaprogramming method. If there # are any conflicts, this application's options will be favored. self.class.option_parser_commands.each do |options, fname| option_parser.on(*options) do |value| # Call the method that "option()" created. self.send(fname, value) end end - # scan command line. - begin - option_parser.parse!(self.command_line.args) - rescue OptionParser::ParseError => detail - $stderr.puts detail - $stderr.puts "Try 'puppet #{command_line.subcommand_name} --help'" - exit(1) - end + # Scan command line. We just hand any exceptions to our upper levels, + # rather than printing help and exiting, so that we can meaningfully + # respond with context-sensitive help if we want to. --daniel 2011-04-12 + option_parser.parse!(self.command_line.args) end def handlearg(opt, arg) # rewrite --[no-]option to --no-option if that's what was given if opt =~ /\[no-\]/ and !arg opt = opt.gsub(/\[no-\]/,'no-') end # otherwise remove the [no-] prefix to not confuse everybody opt = opt.gsub(/\[no-\]/, '') unless respond_to?(:handle_unknown) and send(:handle_unknown, opt, arg) # Puppet.settings.handlearg doesn't handle direct true/false :-) if arg.is_a?(FalseClass) arg = "false" elsif arg.is_a?(TrueClass) arg = "true" end Puppet.settings.handlearg(opt, arg) end end # this is used for testing def self.exit(code) exit(code) end def name self.class.to_s.sub(/.*::/,"").downcase.to_sym end def help "No help available for puppet #{name}" end private def exit_on_fail(message, code = 1) yield - rescue RuntimeError, NotImplementedError => detail + rescue ArgumentError, RuntimeError, NotImplementedError => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not #{message}: #{detail}" exit(code) end def hook(step,&block) Puppet::Plugins.send("before_application_#{step}",:application_object => self) x = yield Puppet::Plugins.send("after_application_#{step}",:application_object => self, :return_value => x) x end end end diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb index 2ee40227e..fc8616817 100644 --- a/lib/puppet/application/agent.rb +++ b/lib/puppet/application/agent.rb @@ -1,482 +1,481 @@ require 'puppet/application' class Puppet::Application::Agent < Puppet::Application should_parse_config run_mode :agent attr_accessor :args, :agent, :daemon, :host def preinit # Do an initial trap, so that cancels don't get a stack trace. Signal.trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end { :waitforcert => nil, :detailed_exitcodes => false, :verbose => false, :debug => false, :centrallogs => false, :setdest => false, :enable => false, :disable => false, :client => true, :fqdn => nil, :serve => [], :digest => :MD5, :fingerprint => false, }.each do |opt,val| options[opt] = val end @args = {} require 'puppet/daemon' @daemon = Puppet::Daemon.new @daemon.argv = ARGV.dup end option("--centrallogging") option("--disable") option("--enable") option("--debug","-d") option("--fqdn FQDN","-f") option("--test","-t") option("--verbose","-v") option("--fingerprint") option("--digest DIGEST") option("--serve HANDLER", "-s") do |arg| if Puppet::Network::Handler.handler(arg) options[:serve] << arg.to_sym else raise "Could not find handler for #{arg}" end end option("--no-client") do |arg| options[:client] = false end option("--detailed-exitcodes") do |arg| options[:detailed_exitcodes] = true end option("--logdest DEST", "-l DEST") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail puts detail.backtrace if Puppet[:debug] $stderr.puts detail.to_s end end option("--waitforcert WAITFORCERT", "-w") do |arg| options[:waitforcert] = arg.to_i end option("--port PORT","-p") do |arg| @args[:Port] = arg end def help <<-HELP puppet-agent(8) -- The puppet agent daemon ======== SYNOPSIS -------- Retrieves the client configuration from the puppet master and applies it to the local host. This service may be run as a daemon, run periodically using cron (or something similar), or run interactively for testing purposes. USAGE ----- puppet agent [-D|--daemonize|--no-daemonize] [-d|--debug] [--detailed-exitcodes] [--disable] [--enable] [-h|--help] [--certname ] [-l|--logdest syslog||console] [-o|--onetime] [--serve ] [-t|--test] [--noop] [--digest ] [--fingerprint] [-V|--version] [-v|--verbose] [-w|--waitforcert ] DESCRIPTION ----------- This is the main puppet client. Its job is to retrieve the local machine's configuration from a remote server and apply it. In order to successfully communicate with the remote server, the client must have a certificate signed by a certificate authority that the server trusts; the recommended method for this, at the moment, is to run a certificate authority as part of the puppet server (which is the default). The client will connect and request a signed certificate, and will continue connecting until it receives one. Once the client has a signed certificate, it will retrieve its configuration and apply it. USAGE NOTES ----------- 'puppet agent' does its best to find a compromise between interactive use and daemon use. Run with no arguments and no configuration, it will go into the background, attempt to get a signed certificate, and retrieve and apply its configuration every 30 minutes. Some flags are meant specifically for interactive use -- in particular, 'test', 'tags' or 'fingerprint' are useful. 'test' enables verbose logging, causes the daemon to stay in the foreground, exits if the server's configuration is invalid (this happens if, for instance, you've left a syntax error on the server), and exits after running the configuration once (rather than hanging around as a long-running process). 'tags' allows you to specify what portions of a configuration you want to apply. Puppet elements are tagged with all of the class or definition names that contain them, and you can use the 'tags' flag to specify one of these names, causing only configuration elements contained within that class or definition to be applied. This is very useful when you are testing new configurations -- for instance, if you are just starting to manage 'ntpd', you would put all of the new elements into an 'ntpd' class, and call puppet with '--tags ntpd', which would only apply that small portion of the configuration during your testing, rather than applying the whole thing. 'fingerprint' is a one-time flag. In this mode 'puppet agent' will run once and display on the console (and in the log) the current certificate (or certificate request) fingerprint. Providing the '--digest' option allows to use a different digest algorithm to generate the fingerprint. The main use is to verify that before signing a certificate request on the master, the certificate request the master received is the same as the one the client sent (to prevent against man-in-the-middle attacks when signing certificates). OPTIONS ------- Note that any configuration parameter that's valid in the configuration file is also a valid long argument. For example, 'server' is a valid configuration parameter, so you can specify '--server ' as an argument. See the configuration file documentation at http://docs.puppetlabs.com/references/stable/configuration.html for the full list of acceptable parameters. A commented list of all configuration options can also be generated by running puppet agent with '--genconfig'. * --daemonize: Send the process into the background. This is the default. * --no-daemonize: Do not send the process into the background. * --debug: Enable full debugging. * --digest: Change the certificate fingerprinting digest algorithm. The default is MD5. Valid values depends on the version of OpenSSL installed, but should always at least contain MD5, MD2, SHA1 and SHA256. * --detailed-exitcodes: Provide transaction information via exit codes. If this is enabled, an exit code of '2' means there were changes, and an exit code of '4' means that there were failures during the transaction. This option only makes sense in conjunction with --onetime. * --disable: Disable working on the local system. This puts a lock file in place, causing 'puppet agent' not to work on the system until the lock file is removed. This is useful if you are testing a configuration and do not want the central configuration to override the local state until everything is tested and committed. 'puppet agent' uses the same lock file while it is running, so no more than one 'puppet agent' process is working at a time. 'puppet agent' exits after executing this. * --enable: Enable working on the local system. This removes any lock file, causing 'puppet agent' to start managing the local system again (although it will continue to use its normal scheduling, so it might not start for another half hour). 'puppet agent' exits after executing this. * --certname: Set the certname (unique ID) of the client. The master reads this unique identifying string, which is usually set to the node's fully-qualified domain name, to determine which configurations the node will receive. Use this option to debug setup problems or implement unusual node identification schemes. * --help: Print this help message * --logdest: Where to send messages. Choose between syslog, the console, and a log file. Defaults to sending messages to syslog, or the console if debugging or verbosity is enabled. * --no-client: Do not create a config client. This will cause the daemon to run without ever checking for its configuration automatically, and only makes sense * --onetime: Run the configuration once. Runs a single (normally daemonized) Puppet run. Useful for interactively running puppet agent when used in conjunction with the --no-daemonize option. * --fingerprint: Display the current certificate or certificate signing request fingerprint and then exit. Use the '--digest' option to change the digest algorithm used. * --serve: Start another type of server. By default, 'puppet agent' will start a service handler that allows authenticated and authorized remote nodes to trigger the configuration to be pulled down and applied. You can specify any handler here that does not require configuration, e.g., filebucket, ca, or resource. The handlers are in 'lib/puppet/network/handler', and the names must match exactly, both in the call to 'serve' and in 'namespaceauth.conf'. * --test: Enable the most common options used for testing. These are 'onetime', 'verbose', 'ignorecache', 'no-daemonize', 'no-usecacheonfailure', 'detailed-exit-codes', 'no-splay', and 'show_diff'. * --noop: Use 'noop' mode where the daemon runs in a no-op or dry-run mode. This is useful for seeing what changes Puppet will make without actually executing the changes. * --verbose: Turn on verbose reporting. * --version: Print the puppet version number and exit. * --waitforcert: This option only matters for daemons that do not yet have certificates and it is enabled by default, with a value of 120 (seconds). This causes 'puppet agent' to connect to the server every 2 minutes and ask it to sign a certificate request. This is useful for the initial setup of a puppet client. You can turn off waiting for certificates by specifying a time of 0. EXAMPLE ------- $ puppet agent --server puppet.domain.com AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005, 2006 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def run_command return fingerprint if options[:fingerprint] return onetime if Puppet[:onetime] main end def fingerprint unless cert = host.certificate || host.certificate_request $stderr.puts "Fingerprint asked but no certificate nor certificate request have yet been issued" exit(1) return end unless fingerprint = cert.fingerprint(options[:digest]) raise ArgumentError, "Could not get fingerprint for digest '#{options[:digest]}'" end Puppet.notice fingerprint end def onetime unless options[:client] $stderr.puts "onetime is specified but there is no client" exit(43) return end @daemon.set_signal_traps begin report = @agent.run rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err detail.to_s end if not report exit(1) elsif options[:detailed_exitcodes] then exit(report.exit_status) else exit(0) end end def main Puppet.notice "Starting Puppet client version #{Puppet.version}" @daemon.start end # Enable all of the most common test options. def setup_test Puppet.settings.handlearg("--ignorecache") Puppet.settings.handlearg("--no-usecacheonfailure") Puppet.settings.handlearg("--no-splay") Puppet.settings.handlearg("--show_diff") Puppet.settings.handlearg("--no-daemonize") options[:verbose] = true Puppet[:onetime] = true options[:detailed_exitcodes] = true end # Handle the logging settings. def setup_logs if options[:debug] or options[:verbose] Puppet::Util::Log.newdestination(:console) if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end end Puppet::Util::Log.newdestination(:syslog) unless options[:setdest] end def enable_disable_client(agent) if options[:enable] agent.enable elsif options[:disable] agent.disable end exit(0) end def setup_listen unless FileTest.exists?(Puppet[:authconfig]) Puppet.err "Will not start without authorization file #{Puppet[:authconfig]}" exit(14) end handlers = nil if options[:serve].empty? handlers = [:Runner] else handlers = options[:serve] end require 'puppet/network/server' # No REST handlers yet. server = Puppet::Network::Server.new(:xmlrpc_handlers => handlers, :port => Puppet[:puppetport]) @daemon.server = server end def setup_host @host = Puppet::SSL::Host.new waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : 120) cert = @host.wait_for_cert(waitforcert) unless options[:fingerprint] end def setup setup_test if options[:test] setup_logs exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? # If noop is set, then also enable diffs Puppet[:show_diff] = true if Puppet[:noop] args[:Server] = Puppet[:server] if options[:fqdn] args[:FQDN] = options[:fqdn] Puppet[:certname] = options[:fqdn] end if options[:centrallogs] logdest = args[:Server] logdest += ":" + args[:Port] if args.include?(:Port) Puppet::Util::Log.newdestination(logdest) end Puppet.settings.use :main, :agent, :ssl # Always ignoreimport for agent. It really shouldn't even try to import, # but this is just a temporary band-aid. Puppet[:ignoreimport] = true # We need to specify a ca location for all of the SSL-related i # indirected classes to work; in fingerprint mode we just need # access to the local files and we don't need a ca. Puppet::SSL::Host.ca_location = options[:fingerprint] ? :none : :remote Puppet::Transaction::Report.indirection.terminus_class = :rest # we want the last report to be persisted locally Puppet::Transaction::Report.indirection.cache_class = :yaml # Override the default; puppetd needs this, usually. # You can still override this on the command-line with, e.g., :compiler. Puppet[:catalog_terminus] = :rest # Override the default. Puppet[:facts_terminus] = :facter Puppet::Resource::Catalog.indirection.cache_class = :yaml # We need tomake the client either way, we just don't start it # if --no-client is set. require 'puppet/agent' require 'puppet/configurer' @agent = Puppet::Agent.new(Puppet::Configurer) enable_disable_client(@agent) if options[:enable] or options[:disable] @daemon.agent = agent if options[:client] # It'd be nice to daemonize later, but we have to daemonize before the # waitforcert happens. @daemon.daemonize if Puppet[:daemonize] setup_host @objects = [] # This has to go after the certs are dealt with. if Puppet[:listen] unless Puppet[:onetime] setup_listen else Puppet.notice "Ignoring --listen on onetime run" end end end end diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 2b7c9f8fb..5779e799c 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -1,257 +1,243 @@ require 'puppet/application' class Puppet::Application::Apply < Puppet::Application should_parse_config option("--debug","-d") option("--execute EXECUTE","-e") do |arg| options[:code] = arg end option("--loadclasses","-L") option("--verbose","-v") option("--use-nodes") option("--detailed-exitcodes") option("--apply catalog", "-a catalog") do |arg| options[:catalog] = arg end option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end + option("--parseonly") do + puts "--parseonly has been removed. Please use 'puppet parser validate '" + exit 1 + end + def help <<-HELP puppet-apply(8) -- Apply Puppet manifests locally ======== SYNOPSIS -------- Applies a standalone Puppet manifest to the local system. USAGE ----- puppet apply [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose] [-e|--execute] [--detailed-exitcodes] [-l|--logdest ] [--apply ] DESCRIPTION ----------- This is the standalone puppet execution tool; use it to apply individual manifests. When provided with a modulepath, via command line or config file, puppet apply can effectively mimic the catalog that would be served by puppet master with access to the same modules, although there are some subtle differences. When combined with scheduling and an automated system for pushing manifests, this can be used to implement a serverless Puppet site. Most users should use 'puppet agent' and 'puppet master' for site-wide manifests. OPTIONS ------- Note that any configuration parameter that's valid in the configuration file is also a valid long argument. For example, 'modulepath' is a valid configuration parameter, so you can specify '--tags ,' as an argument. See the configuration file documentation at http://docs.puppetlabs.com/references/stable/configuration.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. * --detailed-exitcodes: Provide transaction information via exit codes. If this is enabled, an exit code of '2' means there were changes, and an exit code of '4' means that there were failures during the transaction. * --help: Print this help message * --loadclasses: Load any stored classes. 'puppet agent' 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. * --execute: Execute a specific piece of Puppet code * --verbose: Print extra information. * --apply: Apply a JSON catalog (such as one generated with 'puppet master --compile'). You can either specify a JSON file or pipe in JSON from standard input. EXAMPLE ------- $ puppet apply -l /tmp/manifest.log manifest.pp $ puppet apply --modulepath=/root/dev/modules -e "include ntpd::server" AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def run_command if options[:catalog] apply - elsif Puppet[:parseonly] - parseonly else main end end def apply if options[:catalog] == "-" text = $stdin.read else text = File.read(options[:catalog]) end begin catalog = Puppet::Resource::Catalog.convert_from(Puppet::Resource::Catalog.default_format,text) catalog = Puppet::Resource::Catalog.pson_create(catalog) unless catalog.is_a?(Puppet::Resource::Catalog) rescue => detail raise Puppet::Error, "Could not deserialize catalog from pson: #{detail}" end catalog = catalog.to_ral require 'puppet/configurer' configurer = Puppet::Configurer.new configurer.run :catalog => catalog end - def parseonly - # Set our code or file to use. - if options[:code] or command_line.args.length == 0 - Puppet[:code] = options[:code] || STDIN.read - else - Puppet[:manifest] = command_line.args.shift - end - begin - Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types - rescue => detail - Puppet.err detail - exit 1 - end - exit 0 - end - def main # Set our code or file to use. if options[:code] or command_line.args.length == 0 Puppet[:code] = options[:code] || STDIN.read else manifest = command_line.args.shift raise "Could not find file #{manifest}" unless File.exist?(manifest) Puppet.warning("Only one file can be applied per run. Skipping #{command_line.args.join(', ')}") if command_line.args.size > 0 Puppet[:manifest] = manifest end # Collect our facts. unless facts = Puppet::Node::Facts.indirection.find(Puppet[:certname]) raise "Could not find facts for #{Puppet[:certname]}" end # Find our Node unless node = Puppet::Node.indirection.find(Puppet[:certname]) raise "Could not find node #{Puppet[:certname]}" end # Merge in the facts. node.merge(facts.values) # Allow users to load the classes that puppet agent creates. if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) $stderr.puts "#{file} is not readable" exit(63) end node.classes = File.read(file).split(/[\s\n]+/) end end begin # Compile our catalog starttime = Time.now catalog = Puppet::Resource::Catalog.indirection.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral catalog.finalize catalog.retrieval_duration = Time.now - starttime require 'puppet/configurer' configurer = Puppet::Configurer.new report = configurer.run(:skip_plugin_download => true, :catalog => catalog) exit( options[:detailed_exitcodes] ? report.exit_status : 0 ) rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts detail.message exit(1) end end def setup exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? # If noop is set, then also enable diffs Puppet[:show_diff] = true if Puppet[:noop] Puppet::Util::Log.newdestination(:console) unless options[:logset] client = nil server = nil Signal.trap(:INT) do $stderr.puts "Exiting" exit(1) end # we want the last report to be persisted locally Puppet::Transaction::Report.indirection.cache_class = :yaml if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/application/catalog.rb b/lib/puppet/application/catalog.rb new file mode 100644 index 000000000..10ce05be7 --- /dev/null +++ b/lib/puppet/application/catalog.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Catalog < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb index f02fc893c..c08775380 100644 --- a/lib/puppet/application/cert.rb +++ b/lib/puppet/application/cert.rb @@ -1,224 +1,222 @@ require 'puppet/application' class Puppet::Application::Cert < Puppet::Application should_parse_config run_mode :master attr_accessor :all, :ca, :digest, :signed def subcommand @subcommand end def subcommand=(name) # Handle the nasty, legacy mapping of "clean" to "destroy". sub = name.to_sym @subcommand = (sub == :clean ? :destroy : sub) end option("--clean", "-c") do self.subcommand = "destroy" end option("--all", "-a") do @all = true end option("--digest DIGEST") do |arg| @digest = arg end option("--signed", "-s") do @signed = true end option("--debug", "-d") do |arg| Puppet::Util::Log.level = :debug end require 'puppet/ssl/certificate_authority/interface' Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject {|m| m == :destroy }.each do |method| option("--#{method}", "-#{method.to_s[0,1]}") do self.subcommand = method end end option("--verbose", "-v") do Puppet::Util::Log.level = :info end def help - puts <<-HELP + <<-HELP puppet-cert(8) -- Manage certificates and requests ======== SYNOPSIS -------- Standalone certificate authority. Capable of generating certificates, but mostly used for signing certificate requests from puppet clients. USAGE ----- puppet cert [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose] [-g|--generate] [-l|--list] [-s|--sign] [-r|--revoke] [-p|--print] [-c|--clean] [--verify] [--digest ] [--fingerprint] [host] DESCRIPTION ----------- Because the puppet master service defaults to not signing client certificate requests, this script is available for signing outstanding requests. It can be used to list outstanding requests and then either sign them individually or sign all of them. 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://docs.puppetlabs.com/references/stable/configuration.html for the full list of acceptable parameters. A commented list of all configuration options can also be generated by running puppet cert with '--genconfig'. * --all: Operate on all items. Currently only makes sense with '--sign', '--clean', or '--list'. * --digest: Set the digest for fingerprinting (defaults to md5). Valid values depends on your openssl and openssl ruby extension version, but should contain at least md5, sha1, md2, sha256. * --clean: Remove all files related to a host from puppet cert's storage. This is useful when rebuilding hosts, since new certificate signing requests will only be honored if puppet cert does not have a copy of a signed certificate for that host. The certificate of the host is also revoked. If '--all' is specified then all host certificates, both signed and unsigned, will be removed. * --debug: Enable full debugging. * --generate: Generate a certificate for a named client. A certificate/keypair will be generated for each client named on the command line. * --help: Print this help message * --list: List outstanding certificate requests. If '--all' is specified, signed certificates are also listed, prefixed by '+', and revoked or invalid certificates are prefixed by '-' (the verification outcome is printed in parenthesis). * --print: Print the full-text version of a host's certificate. * --fingerprint: Print the DIGEST (defaults to md5) fingerprint of a host's certificate. * --revoke: Revoke the certificate of a client. The certificate can be specified either by its serial number, given as a decimal number or a hexadecimal number prefixed by '0x', or by its hostname. The certificate is revoked by adding it to the Certificate Revocation List given by the 'cacrl' config parameter. Note that the puppetmasterd needs to be restarted after revoking certificates. * --sign: Sign an outstanding certificate request. Unless '--all' is specified, hosts must be listed after all flags. * --verbose: Enable verbosity. * --version: Print the puppet version number and exit. * --verify: Verify the named certificate against the local CA certificate. EXAMPLE ------- $ puppet cert -l culain.madstop.com $ puppet cert -s culain.madstop.com AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP - exit end def main if @all hosts = :all elsif @signed hosts = :signed else hosts = command_line.args.collect { |h| h.downcase } end begin @ca.apply(:revoke, :to => hosts) if subcommand == :destroy @ca.apply(subcommand, :to => hosts, :digest => @digest) rescue => detail puts detail.backtrace if Puppet[:trace] puts detail.to_s exit(24) end end def setup require 'puppet/ssl/certificate_authority' exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? Puppet::Util::Log.newdestination :console if [:generate, :destroy].include? subcommand Puppet::SSL::Host.ca_location = :local else Puppet::SSL::Host.ca_location = :only end begin @ca = Puppet::SSL::CertificateAuthority.new rescue => detail puts detail.backtrace if Puppet[:trace] puts detail.to_s exit(23) end end def parse_options # handle the bareword subcommand pattern. result = super unless self.subcommand then if sub = self.command_line.args.shift then self.subcommand = sub else help end end result end end diff --git a/lib/puppet/application/certificate.rb b/lib/puppet/application/certificate.rb new file mode 100644 index 000000000..eacb830b2 --- /dev/null +++ b/lib/puppet/application/certificate.rb @@ -0,0 +1,18 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Certificate < Puppet::Application::IndirectionBase + def setup + unless options[:ca_location] + raise ArgumentError, "You must have a CA location specified;\n" + + "use --ca-location to specify the location (remote, local, only)" + end + + location = Puppet::SSL::Host.ca_location + if location == :local && !Puppet::SSL::CertificateAuthority.ca? + self.class.run_mode("master") + self.set_run_mode self.class.run_mode + end + + super + end +end diff --git a/lib/puppet/application/certificate_request.rb b/lib/puppet/application/certificate_request.rb new file mode 100644 index 000000000..1b1b0830c --- /dev/null +++ b/lib/puppet/application/certificate_request.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Certificate_request < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/certificate_revocation_list.rb b/lib/puppet/application/certificate_revocation_list.rb new file mode 100644 index 000000000..60b9d97d6 --- /dev/null +++ b/lib/puppet/application/certificate_revocation_list.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Certificate_revocation_list < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/config.rb b/lib/puppet/application/config.rb new file mode 100644 index 000000000..a94441e7f --- /dev/null +++ b/lib/puppet/application/config.rb @@ -0,0 +1,4 @@ +require 'puppet/application/face_base' + +class Puppet::Application::Config < Puppet::Application::FaceBase +end diff --git a/lib/puppet/application/configurer.rb b/lib/puppet/application/configurer.rb new file mode 100644 index 000000000..6e86cd2d4 --- /dev/null +++ b/lib/puppet/application/configurer.rb @@ -0,0 +1,23 @@ +require 'puppet/application' +require 'puppet/face' + +class Puppet::Application::Configurer < Puppet::Application + should_parse_config + run_mode :agent + + option("--debug", "-d") + option("--verbose", "-v") + + def setup + if options[:debug] or options[:verbose] + Puppet::Util::Log.level = options[:debug] ? :debug : :info + end + + Puppet::Util::Log.newdestination(:console) + end + + def run_command + report = Puppet::Face[:configurer, '0.0.1'].synchronize(Puppet[:certname]) + Puppet::Face[:report, '0.0.1'].submit(report) + end +end diff --git a/lib/puppet/application/describe.rb b/lib/puppet/application/describe.rb index 79643159e..8ce20b652 100644 --- a/lib/puppet/application/describe.rb +++ b/lib/puppet/application/describe.rb @@ -1,257 +1,256 @@ require 'puppet/application' class Formatter def initialize(width) @width = width end def wrap(txt, opts) return "" unless txt && !txt.empty? work = (opts[:scrub] ? scrub(txt) : txt) indent = (opts[:indent] ? opts[:indent] : 0) textLen = @width - indent patt = Regexp.new("^(.{0,#{textLen}})[ \n]") prefix = " " * indent res = [] while work.length > textLen if work =~ patt res << $1 work.slice!(0, $MATCH.length) else res << work.slice!(0, textLen) end end res << work if work.length.nonzero? prefix + res.join("\n#{prefix}") end def header(txt, sep = "-") "\n#{txt}\n" + sep * txt.size end private def scrub(text) # For text with no carriage returns, there's nothing to do. return text if text !~ /\n/ indent = nil # If we can match an indentation, then just remove that same level of # indent from every line. if text =~ /^(\s+)/ indent = $1 return text.gsub(/^#{indent}/,'') else return text end end end class TypeDoc def initialize @format = Formatter.new(76) @types = {} Puppet::Type.loadall Puppet::Type.eachtype { |type| next if type.name == :component @types[type.name] = type } end def list_types puts "These are the types known to puppet:\n" @types.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |name| type = @types[name] s = type.doc.gsub(/\s+/, " ") n = s.index(".") if n.nil? s = ".. no documentation .." elsif n > 45 s = s[0, 45] + " ..." else s = s[0, n] end printf "%-15s - %s\n", name, s end end def format_type(name, opts) name = name.to_sym unless @types.has_key?(name) puts "Unknown type #{name}" return end type = @types[name] puts @format.header(name.to_s, "=") puts @format.wrap(type.doc, :indent => 0, :scrub => true) + "\n\n" puts @format.header("Parameters") if opts[:parameters] format_attrs(type, [:property, :param]) else list_attrs(type, [:property, :param]) end if opts[:meta] puts @format.header("Meta Parameters") if opts[:parameters] format_attrs(type, [:meta]) else list_attrs(type, [:meta]) end end if type.providers.size > 0 puts @format.header("Providers") if opts[:providers] format_providers(type) else list_providers(type) end end end # List details about attributes def format_attrs(type, attrs) docs = {} type.allattrs.each do |name| kind = type.attrtype(name) docs[name] = type.attrclass(name).doc if attrs.include?(kind) && name != :provider end docs.sort { |a,b| a[0].to_s <=> b[0].to_s }.each { |name, doc| print "\n- **#{name}**" if type.key_attributes.include?(name) and name != :name puts " (*namevar*)" else puts "" end puts @format.wrap(doc, :indent => 4, :scrub => true) } end # List the names of attributes def list_attrs(type, attrs) params = [] type.allattrs.each do |name| kind = type.attrtype(name) params << name.to_s if attrs.include?(kind) && name != :provider end puts @format.wrap(params.sort.join(", "), :indent => 4) end def format_providers(type) type.providers.sort { |a,b| a.to_s <=> b.to_s }.each { |prov| puts "\n- **#{prov}**" puts @format.wrap(type.provider(prov).doc, :indent => 4, :scrub => true) } end def list_providers(type) list = type.providers.sort { |a,b| a.to_s <=> b.to_s }.join(", ") puts @format.wrap(list, :indent => 4) end end class Puppet::Application::Describe < Puppet::Application banner "puppet describe [options] [type]" should_not_parse_config option("--short", "-s", "Only list parameters without detail") do |arg| options[:parameters] = false end option("--providers","-p") option("--list", "-l") option("--meta","-m") def help <<-HELP puppet-describe(8) -- Display help about resource types ======== SYNOPSIS -------- Prints help about Puppet resource types, providers, and metaparameters. USAGE ----- puppet describe [-h|--help] [-s|--short] [-p|--providers] [-l|--list] [-m|--meta] OPTIONS ------- * --help: Print this help text * --providers: Describe providers in detail for each type * --list: List all types * --meta: List all metaparameters * --short: List only parameters without detail EXAMPLE ------- $ puppet describe --list $ puppet describe file --providers $ puppet describe user -s -m AUTHOR ------ David Lutterkort COPYRIGHT --------- -Copyright (c) 2005 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def preinit options[:parameters] = true end def main doc = TypeDoc.new if options[:list] doc.list_types else options[:types].each { |name| doc.format_type(name, options) } end end def setup options[:types] = command_line.args.dup handle_help(nil) unless options[:list] || options[:types].size > 0 $stderr.puts "Warning: ignoring types when listing all types" if options[:list] && options[:types].size > 0 end end diff --git a/lib/puppet/application/doc.rb b/lib/puppet/application/doc.rb index 74811919e..a88f27c78 100644 --- a/lib/puppet/application/doc.rb +++ b/lib/puppet/application/doc.rb @@ -1,265 +1,264 @@ require 'puppet/application' class Puppet::Application::Doc < Puppet::Application should_not_parse_config run_mode :master attr_accessor :unknown_args, :manifest def preinit {:references => [], :mode => :text, :format => :to_markdown }.each do |name,value| options[name] = value end @unknown_args = [] @manifest = false end option("--all","-a") option("--outputdir OUTPUTDIR","-o") option("--verbose","-v") option("--debug","-d") option("--charset CHARSET") option("--format FORMAT", "-f") do |arg| method = "to_#{arg}" require 'puppet/util/reference' if Puppet::Util::Reference.method_defined?(method) options[:format] = method else raise "Invalid output format #{arg}" end end option("--mode MODE", "-m") do |arg| require 'puppet/util/reference' if Puppet::Util::Reference.modes.include?(arg) or arg.intern==:rdoc options[:mode] = arg.intern else raise "Invalid output mode #{arg}" end end option("--list", "-l") do |arg| require 'puppet/util/reference' puts Puppet::Util::Reference.references.collect { |r| Puppet::Util::Reference.reference(r).doc }.join("\n") exit(0) end option("--reference REFERENCE", "-r") do |arg| options[:references] << arg.intern end def help <<-HELP puppet-doc(8) -- Generate Puppet documentation and references ======== SYNOPSIS -------- Generates a reference for all Puppet types. Largely meant for internal Puppet Labs use. USAGE ----- puppet doc [-a|--all] [-h|--help] [-o|--outputdir ] [-m|--mode text|pdf|rdoc] [-r|--reference ] [--charset ] [] DESCRIPTION ----------- If mode is not 'rdoc', then this command generates a Markdown document describing all installed Puppet types or all allowable arguments to puppet executables. It is largely meant for internal use and is used to generate the reference document available on the Puppet Labs web site. In 'rdoc' mode, this command generates an html RDoc hierarchy describing the manifests that are in 'manifestdir' and 'modulepath' configuration directives. The generated documentation directory is doc by default but can be changed with the 'outputdir' option. If the command is run with the name of a manifest file as an argument, puppet doc will output a single manifest's documentation on stdout. OPTIONS ------- * --all: Output the docs for all of the reference types. In 'rdoc' modes, this also outputs documentation for all resources * --help: Print this help message * --outputdir: Specifies the directory where to output the rdoc documentation in 'rdoc' mode. * --mode: Determine the output mode. Valid modes are 'text', 'pdf' and 'rdoc'. The 'pdf' mode creates PDF formatted files in the /tmp directory. The default mode is 'text'. In 'rdoc' mode you must provide 'manifests-path' * --reference: Build a particular reference. Get a list of references by running 'puppet doc --list'. * --charset: Used only in 'rdoc' mode. It sets the charset used in the html files produced. EXAMPLE ------- $ puppet doc -r type > /tmp/type_reference.markdown or $ puppet doc --outputdir /tmp/rdoc --mode rdoc /path/to/manifests or $ puppet doc /etc/puppet/manifests/site.pp or $ puppet doc -m pdf -r configuration AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005-2007 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def handle_unknown( opt, arg ) @unknown_args << {:opt => opt, :arg => arg } true end def run_command return[:rdoc].include?(options[:mode]) ? send(options[:mode]) : other end def rdoc exit_code = 0 files = [] unless @manifest env = Puppet::Node::Environment.new files += env.modulepath files << File.dirname(env[:manifest]) end files += command_line.args Puppet.info "scanning: #{files.inspect}" Puppet.settings[:document_all] = options[:all] || false begin require 'puppet/util/rdoc' if @manifest Puppet::Util::RDoc.manifestdoc(files) else options[:outputdir] = "doc" unless options[:outputdir] Puppet::Util::RDoc.rdoc(options[:outputdir], files, options[:charset]) end rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not generate documentation: #{detail}" exit_code = 1 end exit exit_code end def other text = "" with_contents = options[:references].length <= 1 exit_code = 0 require 'puppet/util/reference' options[:references].sort { |a,b| a.to_s <=> b.to_s }.each do |name| raise "Could not find reference #{name}" unless section = Puppet::Util::Reference.reference(name) begin # Add the per-section text, but with no ToC text += section.send(options[:format], with_contents) rescue => detail puts detail.backtrace $stderr.puts "Could not generate reference #{name}: #{detail}" exit_code = 1 next end end text += Puppet::Util::Reference.footer unless with_contents # We've only got one reference if options[:mode] == :pdf Puppet::Util::Reference.pdf(text) else puts text end exit exit_code end def setup # sole manifest documentation if command_line.args.size > 0 options[:mode] = :rdoc @manifest = true end if options[:mode] == :rdoc setup_rdoc else setup_reference end end def setup_reference if options[:all] # Don't add dynamic references to the "all" list. require 'puppet/util/reference' options[:references] = Puppet::Util::Reference.references.reject do |ref| Puppet::Util::Reference.reference(ref).dynamic? end end options[:references] << :type if options[:references].empty? end def setup_rdoc(dummy_argument=:work_arround_for_ruby_GC_bug) # consume the unknown options # and feed them as settings if @unknown_args.size > 0 @unknown_args.each do |option| # force absolute path for modulepath when passed on commandline if option[:opt]=="--modulepath" or option[:opt] == "--manifestdir" option[:arg] = option[:arg].split(':').collect { |p| File.expand_path(p) }.join(':') end Puppet.settings.handlearg(option[:opt], option[:arg]) end end # Now parse the config Puppet.parse_config # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end Puppet::Util::Log.newdestination(:console) end end end diff --git a/lib/puppet/application/face_base.rb b/lib/puppet/application/face_base.rb new file mode 100644 index 000000000..2a048a532 --- /dev/null +++ b/lib/puppet/application/face_base.rb @@ -0,0 +1,158 @@ +require 'puppet/application' +require 'puppet/face' +require 'optparse' + +class Puppet::Application::FaceBase < Puppet::Application + should_parse_config + run_mode :agent + + option("--debug", "-d") do |arg| + Puppet::Util::Log.level = :debug + end + + option("--verbose", "-v") do + Puppet::Util::Log.level = :info + end + + option("--format FORMAT") do |arg| + @format = arg.to_sym + end + + option("--mode RUNMODE", "-r") do |arg| + raise "Invalid run mode #{arg}; supported modes are user, agent, master" unless %w{user agent master}.include?(arg) + self.class.run_mode(arg.to_sym) + set_run_mode self.class.run_mode + end + + + attr_accessor :face, :action, :type, :arguments, :format + attr_writer :exit_code + + # This allows you to set the exit code if you don't want to just exit + # immediately but you need to indicate a failure. + def exit_code + @exit_code || 0 + end + + # Override this if you need custom rendering. + def render(result) + render_method = Puppet::Network::FormatHandler.format(format).render_method + if render_method == "to_pson" + jj result + exit(0) + else + result.send(render_method) + end + end + + def preinit + super + Signal.trap(:INT) do + $stderr.puts "Cancelling Face" + exit(0) + end + end + + def parse_options + # We need to parse enough of the command line out early, to identify what + # the action is, so that we can obtain the full set of options to parse. + + # REVISIT: These should be configurable versions, through a global + # '--version' option, but we don't implement that yet... --daniel 2011-03-29 + @type = self.class.name.to_s.sub(/.+:/, '').downcase.to_sym + @face = Puppet::Face[@type, :current] + @format = @face.default_format + + # Now, walk the command line and identify the action. We skip over + # arguments based on introspecting the action and all, and find the first + # non-option word to use as the action. + action = nil + index = -1 + until @action or (index += 1) >= command_line.args.length do + item = command_line.args[index] + if item =~ /^-/ then + option = @face.options.find do |name| + item =~ /^-+#{name.to_s.gsub(/[-_]/, '[-_]')}(?:[ =].*)?$/ + end + if option then + option = @face.get_option(option) + # If we have an inline argument, just carry on. We don't need to + # care about optional vs mandatory in that case because we do a real + # parse later, and that will totally take care of raising the error + # when we get there. --daniel 2011-04-04 + if option.takes_argument? and !item.index('=') then + index += 1 unless + (option.optional_argument? and command_line.args[index + 1] =~ /^-/) + end + elsif option = find_global_settings_argument(item) then + unless Puppet.settings.boolean? option.name then + # As far as I can tell, we treat non-bool options as always having + # a mandatory argument. --daniel 2011-04-05 + index += 1 # ...so skip the argument. + end + else + raise OptionParser::InvalidOption.new(item.sub(/=.*$/, '')) + end + else + action = @face.get_action(item.to_sym) + if action.nil? then + raise OptionParser::InvalidArgument.new("#{@face} does not have an #{item} action") + end + @action = action + end + end + + unless @action + raise OptionParser::MissingArgument.new("No action given on the command line") + end + + # Now we can interact with the default option code to build behaviour + # around the full set of options we now know we support. + @action.options.each do |option| + option = @action.get_option(option) # make it the object. + self.class.option(*option.optparse) # ...and make the CLI parse it. + end + + # ...and invoke our parent to parse all the command line options. + super + end + + def find_global_settings_argument(item) + Puppet.settings.each do |name, object| + object.optparse_args.each do |arg| + next unless arg =~ /^-/ + # sadly, we have to emulate some of optparse here... + pattern = /^#{arg.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/ + pattern.match item and return object + end + end + return nil # nothing found. + end + + def setup + Puppet::Util::Log.newdestination :console + + @arguments = command_line.args + + # Note: because of our definition of where the action is set, we end up + # with it *always* being the first word of the remaining set of command + # line arguments. So, strip that off when we construct the arguments to + # pass down to the face action. --daniel 2011-04-04 + @arguments.delete_at(0) + + # We copy all of the app options to the end of the call; This allows each + # action to read in the options. This replaces the older model where we + # would invoke the action with options set as global state in the + # interface object. --daniel 2011-03-28 + @arguments << options + end + + + def main + # Call the method associated with the provided action (e.g., 'find'). + if result = @face.send(@action.name, *arguments) + puts render(result) + end + exit(exit_code) + end +end diff --git a/lib/puppet/application/faces.rb b/lib/puppet/application/faces.rb new file mode 100644 index 000000000..3dd3f0312 --- /dev/null +++ b/lib/puppet/application/faces.rb @@ -0,0 +1,88 @@ +require 'puppet/application' +require 'puppet/face' + +class Puppet::Application::Faces < Puppet::Application + + should_parse_config + run_mode :agent + + option("--debug", "-d") do |arg| + Puppet::Util::Log.level = :debug + end + + option("--help", "-h") do |arg| + puts "Usage: puppet faces [actions|terminuses] +Lists all available faces, and by default includes all available terminuses and actions. +" + end + + option("--verbose", "-v") do + Puppet::Util::Log.level = :info + end + + def list(*arguments) + if arguments.empty? + arguments = %w{terminuses actions} + end + faces.each do |name| + str = "#{name}:\n" + if arguments.include?("terminuses") + begin + terms = terminus_classes(name.to_sym) + str << "\tTerminuses: #{terms.join(", ")}\n" + rescue => detail + puts detail.backtrace if Puppet[:trace] + $stderr.puts "Could not load terminuses for #{name}: #{detail}" + end + end + + if arguments.include?("actions") + begin + actions = actions(name.to_sym) + str << "\tActions: #{actions.join(", ")}\n" + rescue => detail + puts detail.backtrace if Puppet[:trace] + $stderr.puts "Could not load actions for #{name}: #{detail}" + end + end + + print str + end + end + + attr_accessor :name, :arguments + + def main + list(*arguments) + end + + def setup + Puppet::Util::Log.newdestination :console + + load_applications # Call this to load all of the apps + + @arguments = command_line.args + @arguments ||= [] + end + + def faces + Puppet::Face.faces + end + + def terminus_classes(indirection) + Puppet::Indirector::Terminus.terminus_classes(indirection).collect { |t| t.to_s }.sort + end + + def actions(indirection) + return [] unless face = Puppet::Face[indirection, '0.0.1'] + face.load_actions + return face.actions.sort { |a, b| a.to_s <=> b.to_s } + end + + def load_applications + command_line.available_subcommands.each do |app| + command_line.require_application app + end + end +end + diff --git a/lib/puppet/application/facts.rb b/lib/puppet/application/facts.rb new file mode 100644 index 000000000..d18b21ea7 --- /dev/null +++ b/lib/puppet/application/facts.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Facts < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/file.rb b/lib/puppet/application/file.rb new file mode 100644 index 000000000..32a81c7c6 --- /dev/null +++ b/lib/puppet/application/file.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::File < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/filebucket.rb b/lib/puppet/application/filebucket.rb index 063d97db8..6d59ae40b 100644 --- a/lib/puppet/application/filebucket.rb +++ b/lib/puppet/application/filebucket.rb @@ -1,190 +1,189 @@ require 'puppet/application' class Puppet::Application::Filebucket < Puppet::Application should_not_parse_config option("--bucket BUCKET","-b") option("--debug","-d") option("--local","-l") option("--remote","-r") option("--verbose","-v") attr :args def help <<-HELP puppet-filebucket(8) -- Store and retrieve files in a filebucket ======== SYNOPSIS -------- A stand-alone Puppet filebucket client. USAGE ----- puppet filebucket [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose] [-l|--local] [-r|--remote] [-s|--server ] [-b|--bucket ] ... Puppet filebucket can operate in three modes, with only one mode per call: backup: Send one or more files to the specified file bucket. Each sent file is printed with its resulting md5 sum. get: Return the text associated with an md5 sum. The text is printed to stdout, and only one file can be retrieved at a time. restore: Given a file path and an md5 sum, store the content associated with the sum into the specified file path. You can specify an entirely new path to this argument; you are not restricted to restoring the content to its original location. DESCRIPTION ----------- This is a stand-alone filebucket client for sending files to a local or central filebucket. Note that 'filebucket' defaults to using a network-based filebucket available on the server named 'puppet'. To use this, you'll have to be running as a user with valid Puppet certificates. Alternatively, you can use your local file bucket by specifying '--local'. 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://docs.puppetlabs.com/references/stable/configuration.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 * --local: Use the local filebucket. This will use the default configuration information. * --remote: Use a remote filebucket. This will use the default configuration information. * --server: The server to send the file to, instead of locally. * --verbose: Print extra information. * --version: Print version information. EXAMPLE ------- $ puppet filebucket backup /etc/passwd /etc/passwd: 429b225650b912a2ee067b0a4cf1e949 $ puppet filebucket restore /tmp/passwd 429b225650b912a2ee067b0a4cf1e949 AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def run_command @args = command_line.args command = args.shift return send(command) if %w{get backup restore}.include? command help end def get md5 = args.shift out = @client.getfile(md5) print out end def backup args.each do |file| unless FileTest.exists?(file) $stderr.puts "#{file}: no such file" next end unless FileTest.readable?(file) $stderr.puts "#{file}: cannot read file" next end md5 = @client.backup(file) puts "#{file}: #{md5}" end end def restore file = args.shift md5 = args.shift @client.restore(file, md5) end def setup Puppet::Log.newdestination(:console) @client = nil @server = nil Signal.trap(:INT) do $stderr.puts "Cancelling" exit(1) end if options[:debug] Puppet::Log.level = :debug elsif options[:verbose] Puppet::Log.level = :info end # Now parse the config Puppet.parse_config exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? require 'puppet/file_bucket/dipper' begin if options[:local] or options[:bucket] path = options[:bucket] || Puppet[:bucketdir] @client = Puppet::FileBucket::Dipper.new(:Path => path) else @client = Puppet::FileBucket::Dipper.new(:Server => Puppet[:server]) end rescue => detail $stderr.puts detail puts detail.backtrace if Puppet[:trace] exit(1) end end end diff --git a/lib/puppet/application/help.rb b/lib/puppet/application/help.rb new file mode 100644 index 000000000..0d7767632 --- /dev/null +++ b/lib/puppet/application/help.rb @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +require 'puppet/application/face_base' + +class Puppet::Application::Help < Puppet::Application::FaceBase + # Meh. Disable the default behaviour, which is to inspect the + # string and return that – not so helpful. --daniel 2011-04-11 + def render(result) result end +end diff --git a/lib/puppet/application/indirection_base.rb b/lib/puppet/application/indirection_base.rb new file mode 100644 index 000000000..580a0999a --- /dev/null +++ b/lib/puppet/application/indirection_base.rb @@ -0,0 +1,4 @@ +require 'puppet/application/face_base' + +class Puppet::Application::IndirectionBase < Puppet::Application::FaceBase +end diff --git a/lib/puppet/application/inspect.rb b/lib/puppet/application/inspect.rb index e448cb9e8..30865cfc1 100644 --- a/lib/puppet/application/inspect.rb +++ b/lib/puppet/application/inspect.rb @@ -1,181 +1,179 @@ require 'puppet' require 'puppet/application' require 'puppet/file_bucket/dipper' class Puppet::Application::Inspect < Puppet::Application should_parse_config run_mode :agent option("--debug","-d") option("--verbose","-v") option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end def help <<-HELP puppet-inspect(8) -- Send an inspection report ======== SYNOPSIS -------- Prepares and submits an inspection report to the puppet master. USAGE ----- puppet inspect DESCRIPTION ----------- This command uses the cached catalog from the previous run of 'puppet agent' to determine which attributes of which resources have been marked as auditable with the 'audit' metaparameter. It then examines the current state of the system, writes the state of the specified resource attributes to a report, and submits the report to the puppet master. Puppet inspect does not run as a daemon, and must be run manually or from cron. OPTIONS ------- Any configuration setting which is valid in the configuration file is also a valid long argument, e.g. '--server=master.domain.com'. See the configuration file documentation at http://docs.puppetlabs.com/references/latest/configuration.html for the full list of acceptable settings. AUTHOR ------ Puppet Labs COPYRIGHT --------- - -Copyright (c) 2011 Puppet Labs, LLC -Licensed under the GNU General Public License version 2 +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def setup exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? raise "Inspect requires reporting to be enabled. Set report=true in puppet.conf to enable reporting." unless Puppet[:report] @report = Puppet::Transaction::Report.new("inspect") Puppet::Util::Log.newdestination(@report) Puppet::Util::Log.newdestination(:console) unless options[:logset] Signal.trap(:INT) do $stderr.puts "Exiting" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end Puppet::Transaction::Report.indirection.terminus_class = :rest Puppet::Resource::Catalog.indirection.terminus_class = :yaml end def run_command benchmark(:notice, "Finished inspection") do retrieval_starttime = Time.now unless catalog = Puppet::Resource::Catalog.indirection.find(Puppet[:certname]) raise "Could not find catalog for #{Puppet[:certname]}" end @report.configuration_version = catalog.version inspect_starttime = Time.now @report.add_times("config_retrieval", inspect_starttime - retrieval_starttime) if Puppet[:archive_files] dipper = Puppet::FileBucket::Dipper.new(:Server => Puppet[:archive_file_server]) end catalog.to_ral.resources.each do |ral_resource| audited_attributes = ral_resource[:audit] next unless audited_attributes status = Puppet::Resource::Status.new(ral_resource) begin audited_resource = ral_resource.to_resource rescue StandardError => detail puts detail.backtrace if Puppet[:trace] ral_resource.err "Could not inspect #{ral_resource}; skipping: #{detail}" audited_attributes.each do |name| event = ral_resource.event( :property => name, :status => "failure", :audited => true, :message => "failed to inspect #{name}" ) status.add_event(event) end else audited_attributes.each do |name| next if audited_resource[name].nil? # Skip :absent properties of :absent resources. Really, it would be nicer if the RAL returned nil for those, but it doesn't. ~JW if name == :ensure or audited_resource[:ensure] != :absent or audited_resource[name] != :absent event = ral_resource.event( :previous_value => audited_resource[name], :property => name, :status => "audit", :audited => true, :message => "inspected value is #{audited_resource[name].inspect}" ) status.add_event(event) end end end if Puppet[:archive_files] and ral_resource.type == :file and audited_attributes.include?(:content) path = ral_resource[:path] if File.readable?(path) begin dipper.backup(path) rescue StandardError => detail Puppet.warning detail end end end @report.add_resource_status(status) end finishtime = Time.now @report.add_times("inspect", finishtime - inspect_starttime) @report.finalize_report begin Puppet::Transaction::Report.indirection.save(@report) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not send report: #{detail}" end end end end diff --git a/lib/puppet/application/key.rb b/lib/puppet/application/key.rb new file mode 100644 index 000000000..57835b627 --- /dev/null +++ b/lib/puppet/application/key.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Key < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/kick.rb b/lib/puppet/application/kick.rb index da93c0182..536699442 100644 --- a/lib/puppet/application/kick.rb +++ b/lib/puppet/application/kick.rb @@ -1,353 +1,352 @@ require 'puppet/application' class Puppet::Application::Kick < Puppet::Application should_not_parse_config attr_accessor :hosts, :tags, :classes option("--all","-a") option("--foreground","-f") option("--debug","-d") option("--ping","-P") option("--test") option("--host HOST") do |arg| @hosts << arg end option("--tag TAG", "-t") do |arg| @tags << arg end option("--class CLASS", "-c") do |arg| @classes << arg end option("--no-fqdn", "-n") do |arg| options[:fqdn] = false end option("--parallel PARALLEL", "-p") do |arg| begin options[:parallel] = Integer(arg) rescue $stderr.puts "Could not convert #{arg.inspect} to an integer" exit(23) end end def help <<-HELP puppet-kick(8) -- Remotely control puppet agent ======== SYNOPSIS -------- Trigger a puppet agent run on a set of hosts. USAGE ----- puppet kick [-a|--all] [-c|--class ] [-d|--debug] [-f|--foreground] [-h|--help] [--host ] [--no-fqdn] [--ignoreschedules] [-t|--tag ] [--test] [-p|--ping] [ [...]] DESCRIPTION ----------- This script can be used to connect to a set of machines running 'puppet agent' and trigger them to run their configurations. The most common usage would be to specify a class of hosts and a set of tags, and 'puppet kick' would look up in LDAP all of the hosts matching that class, then connect to each host and trigger a run of all of the objects with the specified tags. If you are not storing your host configurations in LDAP, you can specify hosts manually. You will most likely have to run 'puppet kick' as root to get access to the SSL certificates. 'puppet kick' reads 'puppet master''s configuration file, so that it can copy things like LDAP settings. USAGE NOTES ----------- 'puppet kick' is useless unless 'puppet agent' is listening. See its documentation for more information, but the gist is that you must enable 'listen' on the 'puppet agent' daemon, either using '--listen' on the command line or adding 'listen = true' in its config file. In addition, you need to set the daemons up to specifically allow connections by creating the 'namespaceauth' file, normally at '/etc/puppet/namespaceauth.conf'. This file specifies who has access to each namespace; if you create the file you must add every namespace you want any Puppet daemon to allow -- it is currently global to all Puppet daemons. An example file looks like this: [fileserver] allow *.madstop.com [puppetmaster] allow *.madstop.com [puppetrunner] allow culain.madstop.com This is what you would install on your Puppet master; non-master hosts could leave off the 'fileserver' and 'puppetmaster' namespaces. 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://docs.puppetlabs.com/references/latest/configuration.html for the full list of acceptable parameters. A commented list of all configuration options can also be generated by running puppet master with '--genconfig'. * --all: Connect to all available hosts. Requires LDAP support at this point. * --class: Specify a class of machines to which to connect. This only works if you have LDAP configured, at the moment. * --debug: Enable full debugging. * --foreground: Run each configuration in the foreground; that is, when connecting to a host, do not return until the host has finished its run. The default is false. * --help: Print this help message * --host: A specific host to which to connect. This flag can be specified more than once. * --ignoreschedules: Whether the client should ignore schedules when running its configuration. This can be used to force the client to perform work it would not normally perform so soon. The default is false. * --parallel: How parallel to make the connections. Parallelization is provided by forking for each client to which to connect. The default is 1, meaning serial execution. * --tag: Specify a tag for selecting the objects to apply. Does not work with the --test option. * --test: Print the hosts you would connect to but do not actually connect. This option requires LDAP support at this point. * --ping: Do a ICMP echo against the target host. Skip hosts that don't respond to ping. EXAMPLE ------- $ sudo puppet kick -p 10 -t remotefile -t webserver host1 host2 AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def run_command @hosts += command_line.args options[:test] ? test : main end def test puts "Skipping execution in test mode" exit(0) end def main require 'puppet/network/client' Puppet.warning "Failed to load ruby LDAP library. LDAP functionality will not be available" unless Puppet.features.ldap? require 'puppet/util/ldap/connection' todo = @hosts.dup failures = [] # Now do the actual work go = true while go # If we don't have enough children in process and we still have hosts left to # do, then do the next host. if @children.length < options[:parallel] and ! todo.empty? host = todo.shift pid = fork do run_for_host(host) end @children[pid] = host else # Else, see if we can reap a process. begin pid = Process.wait if host = @children[pid] # Remove our host from the list of children, so the parallelization # continues working. @children.delete(pid) failures << host if $CHILD_STATUS.exitstatus != 0 print "#{host} finished with exit code #{$CHILD_STATUS.exitstatus}\n" else $stderr.puts "Could not find host for PID #{pid} with status #{$CHILD_STATUS.exitstatus}" end rescue Errno::ECHILD # There are no children left, so just exit unless there are still # children left to do. next unless todo.empty? if failures.empty? puts "Finished" exit(0) else puts "Failed: #{failures.join(", ")}" exit(3) end end end end end def run_for_host(host) if options[:ping] out = %x{ping -c 1 #{host}} unless $CHILD_STATUS == 0 $stderr.print "Could not contact #{host}\n" exit($CHILD_STATUS) end end require 'puppet/run' Puppet::Run.indirection.terminus_class = :rest port = Puppet[:puppetport] url = ["https://#{host}:#{port}", "production", "run", host].join('/') print "Triggering #{host}\n" begin run_options = { :tags => @tags, :background => ! options[:foreground], :ignoreschedules => options[:ignoreschedules] } run = Puppet::Run.indirection.save(Puppet::Run.new( run_options ), url) puts "Getting status" result = run.status puts "status is #{result}" rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Host #{host} failed: #{detail}\n" exit(2) end case result when "success"; exit(0) when "running" $stderr.puts "Host #{host} is already running" exit(3) else $stderr.puts "Host #{host} returned unknown answer '#{result}'" exit(12) end end def initialize(*args) super @hosts = [] @classes = [] @tags = [] end def preinit [:INT, :TERM].each do |signal| Signal.trap(signal) do $stderr.puts "Cancelling" exit(1) end end options[:parallel] = 1 options[:verbose] = true options[:fqdn] = true options[:ignoreschedules] = false options[:foreground] = false end def setup if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end # Now parse the config Puppet.parse_config if Puppet[:node_terminus] == "ldap" and (options[:all] or @classes) if options[:all] @hosts = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn]).collect { |node| node.name } puts "all: #{@hosts.join(", ")}" else @hosts = [] @classes.each do |klass| list = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn], :class => klass).collect { |node| node.name } puts "#{klass}: #{list.join(", ")}" @hosts += list end end elsif ! @classes.empty? $stderr.puts "You must be using LDAP to specify host classes" exit(24) end @children = {} # If we get a signal, then kill all of our children and get out. [:INT, :TERM].each do |signal| Signal.trap(signal) do Puppet.notice "Caught #{signal}; shutting down" @children.each do |pid, host| Process.kill("INT", pid) end waitall exit(1) end end end end diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb index 78499a92a..a90829ae0 100644 --- a/lib/puppet/application/master.rb +++ b/lib/puppet/application/master.rb @@ -1,239 +1,231 @@ require 'puppet/application' class Puppet::Application::Master < Puppet::Application should_parse_config run_mode :master option("--debug", "-d") option("--verbose", "-v") # internal option, only to be used by ext/rack/config.ru option("--rack") option("--compile host", "-c host") do |arg| options[:node] = arg end option("--logdest DEST", "-l DEST") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail puts detail.backtrace if Puppet[:debug] $stderr.puts detail.to_s end end + option("--parseonly") do + puts "--parseonly has been removed. Please use 'puppet parser validate '" + exit 1 + end + def help <<-HELP puppet-master(8) -- The puppet master daemon ======== SYNOPSIS -------- The central puppet server. Functions as a certificate authority by default. USAGE ----- puppet master [-D|--daemonize|--no-daemonize] [-d|--debug] [-h|--help] [-l|--logdest |console|syslog] [-v|--verbose] [-V|--version] [--compile ] DESCRIPTION ----------- This command starts an instance of puppet master, running as a daemon and using Ruby's built-in Webrick webserver. Puppet master can also be managed by other application servers; when this is the case, this executable is not used. 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://docs.puppetlabs.com/references/stable/configuration.html for the full list of acceptable parameters. A commented list of all configuration options can also be generated by running puppet master with '--genconfig'. * --daemonize: Send the process into the background. This is the default. * --no-daemonize: Do not send the process into the background. * --debug: Enable full debugging. * --help: Print this help message. * --logdest: Where to send messages. Choose between syslog, the console, and a log file. Defaults to sending messages to syslog, or the console if debugging or verbosity is enabled. * --verbose: Enable verbosity. * --version: Print the puppet version number and exit. * --compile: Compile a catalogue and output it in JSON from the puppet master. Uses facts contained in the $vardir/yaml/ directory to compile the catalog. EXAMPLE ------- puppet master AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def preinit Signal.trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end # Create this first-off, so we have ARGV require 'puppet/daemon' @daemon = Puppet::Daemon.new @daemon.argv = ARGV.dup end def run_command if options[:node] compile - elsif Puppet[:parseonly] - parseonly else main end end def compile Puppet::Util::Log.newdestination :console raise ArgumentError, "Cannot render compiled catalogs without pson support" unless Puppet.features.pson? begin unless catalog = Puppet::Resource::Catalog.indirection.find(options[:node]) raise "Could not compile catalog for #{options[:node]}" end jj catalog.to_resource rescue => detail $stderr.puts detail exit(30) end exit(0) end - def parseonly - begin - Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types - rescue => detail - Puppet.err detail - exit 1 - end - exit(0) - end - def main require 'etc' require 'puppet/file_serving/content' require 'puppet/file_serving/metadata' xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket] xmlrpc_handlers << :CA if Puppet[:ca] # Make sure we've got a localhost ssl cert Puppet::SSL::Host.localhost # And now configure our server to *only* hit the CA for data, because that's # all it will have write access to. Puppet::SSL::Host.ca_location = :only if Puppet::SSL::CertificateAuthority.ca? if Puppet.features.root? begin Puppet::Util.chuser rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not change user to #{Puppet[:user]}: #{detail}" exit(39) end end unless options[:rack] require 'puppet/network/server' @daemon.server = Puppet::Network::Server.new(:xmlrpc_handlers => xmlrpc_handlers) @daemon.daemonize if Puppet[:daemonize] else require 'puppet/network/http/rack' @app = Puppet::Network::HTTP::Rack.new(:xmlrpc_handlers => xmlrpc_handlers, :protocols => [:rest, :xmlrpc]) end Puppet.notice "Starting Puppet master version #{Puppet.version}" unless options[:rack] @daemon.start else return @app end end def setup # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end unless Puppet[:daemonize] or options[:rack] Puppet::Util::Log.newdestination(:console) options[:setdest] = true end end Puppet::Util::Log.newdestination(:syslog) unless options[:setdest] exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? Puppet.settings.use :main, :master, :ssl, :metrics # Cache our nodes in yaml. Currently not configurable. Puppet::Node.indirection.cache_class = :yaml # Configure all of the SSL stuff. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :local Puppet.settings.use :ca Puppet::SSL::CertificateAuthority.instance else Puppet::SSL::Host.ca_location = :none end end end diff --git a/lib/puppet/application/node.rb b/lib/puppet/application/node.rb new file mode 100644 index 000000000..38c1f8610 --- /dev/null +++ b/lib/puppet/application/node.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Node < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/parser.rb b/lib/puppet/application/parser.rb new file mode 100644 index 000000000..b6ec3c185 --- /dev/null +++ b/lib/puppet/application/parser.rb @@ -0,0 +1,5 @@ +require 'puppet/application/face_base' +require 'puppet/face' + +class Puppet::Application::Parser < Puppet::Application::FaceBase +end diff --git a/lib/puppet/application/queue.rb b/lib/puppet/application/queue.rb index de8aea32a..e56fde281 100644 --- a/lib/puppet/application/queue.rb +++ b/lib/puppet/application/queue.rb @@ -1,165 +1,164 @@ require 'puppet/application' require 'puppet/util' class Puppet::Application::Queue < Puppet::Application should_parse_config attr_accessor :daemon def preinit require 'puppet/daemon' @daemon = Puppet::Daemon.new @daemon.argv = ARGV.dup Puppet::Util::Log.newdestination(:console) # Do an initial trap, so that cancels don't get a stack trace. # This exits with exit code 1 Signal.trap(:INT) do $stderr.puts "Caught SIGINT; shutting down" exit(1) end # This is a normal shutdown, so code 0 Signal.trap(:TERM) do $stderr.puts "Caught SIGTERM; shutting down" exit(0) end { :verbose => false, :debug => false }.each do |opt,val| options[opt] = val end end option("--debug","-d") option("--verbose","-v") def help <<-HELP puppet-queue(8) -- Queuing daemon for asynchronous storeconfigs ======== SYNOPSIS -------- Retrieves serialized storeconfigs records from a queue and processes them in order. USAGE ----- puppet queue [-d|--debug] [-v|--verbose] DESCRIPTION ----------- This application runs as a daemon and processes storeconfigs data, retrieving the data from a stomp server message queue and writing it to a database. For more information, including instructions for properly setting up your puppet master and message queue, see the documentation on setting up asynchronous storeconfigs at: http://projects.puppetlabs.com/projects/1/wiki/Using_Stored_Configuration OPTIONS ------- Note that any configuration parameter that's valid in the configuration file is also a valid long argument. For example, 'server' is a valid configuration parameter, so you can specify '--server ' as an argument. See the configuration file documentation at http://docs.puppetlabs.com/references/stable/configuration.html for the full list of acceptable parameters. A commented list of all configuration options can also be generated by running puppet queue with '--genconfig'. * --debug: Enable full debugging. * --help: Print this help message * --verbose: Turn on verbose reporting. * --version: Print the puppet version number and exit. EXAMPLE ------- $ puppet queue AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2009 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def main require 'puppet/indirector/catalog/queue' # provides Puppet::Indirector::Queue.subscribe Puppet.notice "Starting puppetqd #{Puppet.version}" Puppet::Resource::Catalog::Queue.subscribe do |catalog| # Once you have a Puppet::Resource::Catalog instance, passing it to save should suffice # to put it through to the database via its active_record indirector (which is determined # by the terminus_class = :active_record setting above) Puppet::Util.benchmark(:notice, "Processing queued catalog for #{catalog.name}") do begin Puppet::Resource::Catalog.indirection.save(catalog) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not save queued catalog for #{catalog.name}: #{detail}" end end end Thread.list.each { |thread| thread.join } end # Handle the logging settings. def setup_logs if options[:debug] or options[:verbose] Puppet::Util::Log.newdestination(:console) if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end end end def setup unless Puppet.features.stomp? raise ArgumentError, "Could not load the 'stomp' library, which must be present for queueing to work. You must install the required library." end setup_logs exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? require 'puppet/resource/catalog' Puppet::Resource::Catalog.indirection.terminus_class = :active_record daemon.daemonize if Puppet[:daemonize] # We want to make sure that we don't have a cache # class set up, because if storeconfigs is enabled, # we'll get a loop of continually caching the catalog # for storage again. Puppet::Resource::Catalog.indirection.cache_class = nil end end diff --git a/lib/puppet/application/report.rb b/lib/puppet/application/report.rb new file mode 100644 index 000000000..f7f961edd --- /dev/null +++ b/lib/puppet/application/report.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Report < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb index 3995c285b..6ef87d68f 100644 --- a/lib/puppet/application/resource.rb +++ b/lib/puppet/application/resource.rb @@ -1,221 +1,220 @@ require 'puppet/application' class Puppet::Application::Resource < Puppet::Application should_not_parse_config attr_accessor :host, :extra_params def preinit @extra_params = [] @host = nil Facter.loadfacts end option("--debug","-d") option("--verbose","-v") option("--edit","-e") option("--host HOST","-H") do |arg| @host = arg end option("--types", "-t") do |arg| types = [] Puppet::Type.loadall Puppet::Type.eachtype do |t| next if t.name == :component types << t.name.to_s end puts types.sort exit end option("--param PARAM", "-p") do |arg| @extra_params << arg.to_sym end def help <<-HELP puppet-resource(8) -- The resource abstraction layer shell ======== SYNOPSIS -------- Uses the Puppet RAL to directly interact with the system. USAGE ----- puppet resource [-h|--help] [-d|--debug] [-v|--verbose] [-e|--edit] [-H|--host ] [-p|--param ] [-t|--types] [] [= ...] DESCRIPTION ----------- This command provides simple facilities for converting current system state into Puppet code, along with some ability to modify the current state using Puppet's RAL. By default, you must at least provide a type to list, in which case puppet resource will tell you everything it knows about all resources of that type. You can optionally specify an instance name, and puppet resource will only describe that single instance. If given a type, a name, and a series of = pairs, puppet resource will modify the state of the specified resource. Alternately, if given a type, a name, and the '--edit' flag, puppet resource will write its output to a file, open that file in an editor, and then apply the saved file as a Puppet transaction. 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://docs.puppetlabs.com/references/stable/configuration.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. * --edit: Write the results of the query to a file, open the file in an editor, and read the file back in as an executable Puppet manifest. * --host: When specified, connect to the resource server on the named host and retrieve the list of resouces of the type specified. * --help: Print this help message. * --param: Add more parameters to be outputted from queries. * --types: List all available types. * --verbose: Print extra information. EXAMPLE ------- This example uses `puppet resource` to return a Puppet configuration for the user `luke`: $ puppet resource user luke user { 'luke': home => '/home/luke', uid => '100', ensure => 'present', comment => 'Luke Kanies,,,', gid => '1000', shell => '/bin/bash', groups => ['sysadmin','audio','video','puppet'] } AUTHOR ------ Luke Kanies COPYRIGHT --------- -Copyright (c) 2005-2007 Puppet Labs, LLC Licensed under the GNU Public -License +Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def main args = command_line.args type = args.shift or raise "You must specify the type to display" typeobj = Puppet::Type.type(type) or raise "Could not find type #{type}" name = args.shift params = {} args.each do |setting| if setting =~ /^(\w+)=(.+)$/ params[$1] = $2 else raise "Invalid parameter setting #{setting}" end end raise "You cannot edit a remote host" if options[:edit] and @host properties = typeobj.properties.collect { |s| s.name } format = proc {|trans| trans.dup.collect do |param, value| if value.nil? or value.to_s.empty? trans.delete(param) elsif value.to_s == "absent" and param.to_s != "ensure" trans.delete(param) end trans.delete(param) unless properties.include?(param) or @extra_params.include?(param) end trans.to_manifest } if @host Puppet::Resource.indirection.terminus_class = :rest port = Puppet[:puppetport] key = ["https://#{host}:#{port}", "production", "resources", type, name].join('/') else key = [type, name].join('/') end text = if name if params.empty? [ Puppet::Resource.indirection.find( key ) ] else [ Puppet::Resource.indirection.save(Puppet::Resource.new( type, name, :parameters => params ), key) ] end else Puppet::Resource.indirection.search( key, {} ) end.map(&format).join("\n") if options[:edit] file = "/tmp/x2puppet-#{Process.pid}.pp" begin File.open(file, "w") do |f| f.puts text end ENV["EDITOR"] ||= "vi" system(ENV["EDITOR"], file) system("puppet -v #{file}") ensure #if FileTest.exists? file # File.unlink(file) #end end else puts text end end def setup Puppet::Util::Log.newdestination(:console) # Now parse the config Puppet.parse_config if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/application/resource_type.rb b/lib/puppet/application/resource_type.rb new file mode 100644 index 000000000..59594262c --- /dev/null +++ b/lib/puppet/application/resource_type.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Resource_type < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/application/status.rb b/lib/puppet/application/status.rb new file mode 100644 index 000000000..1c3ca054e --- /dev/null +++ b/lib/puppet/application/status.rb @@ -0,0 +1,4 @@ +require 'puppet/application/indirection_base' + +class Puppet::Application::Status < Puppet::Application::IndirectionBase +end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 989ef3f35..680762b94 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1,830 +1,832 @@ # The majority of the system configuration parameters are set in this file. module Puppet setdefaults(:main, :confdir => [Puppet.run_mode.conf_dir, "The main Puppet configuration directory. The default for this parameter is calculated based on the user. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it's running as any other user, it defaults to being in the user's home directory."], :vardir => [Puppet.run_mode.var_dir, "Where Puppet stores dynamic and growing data. The default for this parameter is calculated specially, like `confdir`_."], :name => [Puppet.application_name.to_s, "The name of the application, if we are running as one. The default is essentially $0 without the path or `.rb`."], :run_mode => [Puppet.run_mode.name.to_s, "The effective 'run mode' of the application: master, agent, or user."] ) setdefaults(:main, :logdir => Puppet.run_mode.logopts) setdefaults(:main, :trace => [false, "Whether to print stack traces on some errors"], :autoflush => { :default => false, :desc => "Whether log files should always flush to disk.", :hook => proc { |value| Log.autoflush = value } }, :syslogfacility => ["daemon", "What syslog facility to use when logging to syslog. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up."], :statedir => { :default => "$vardir/state", :mode => 01755, :desc => "The directory where Puppet state is stored. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)." }, :rundir => { :default => Puppet.run_mode.run_dir, :mode => 01777, :desc => "Where Puppet PID files are kept." }, :genconfig => [false, "Whether to just print a configuration to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI."], :genmanifest => [false, "Whether to just print a manifest to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI."], :configprint => ["", "Print the value of a specific configuration parameter. If a parameter is provided for this, then the value is printed and puppet exits. Comma-separate multiple values. For a list of all values, specify 'all'. This feature is only available in Puppet versions higher than 0.18.4."], :color => ["ansi", "Whether to use colors when logging to the console. Valid values are `ansi` (equivalent to `true`), `html` (mostly used during testing with TextMate), and `false`, which produces no color."], :mkusers => [false, "Whether to create the necessary user and group that puppet agent will run as."], :manage_internal_file_permissions => [true, "Whether Puppet should manage the owner, group, and mode of files it uses internally" ], :onetime => {:default => false, :desc => "Run the configuration once, rather than as a long-running daemon. This is useful for interactively running puppetd.", :short => 'o' }, :path => {:default => "none", :desc => "The shell search path. Defaults to whatever is inherited from the parent process.", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| ENV["PATH"] = "" if ENV["PATH"].nil? ENV["PATH"] = value unless value == "none" paths = ENV["PATH"].split(File::PATH_SEPARATOR) %w{/usr/sbin /sbin}.each do |path| ENV["PATH"] += File::PATH_SEPARATOR + path unless paths.include?(path) end value end }, :libdir => {:default => "$vardir/lib", :desc => "An extra search path for Puppet. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby's search path", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| $LOAD_PATH.delete(@oldlibdir) if defined?(@oldlibdir) and $LOAD_PATH.include?(@oldlibdir) @oldlibdir = value $LOAD_PATH << value end }, :ignoreimport => [false, "A parameter that can be used in commit hooks, since it enables you to parse-check a single file rather than requiring that all files exist."], :authconfig => [ "$confdir/namespaceauth.conf", "The configuration file that defines the rights to the different namespaces and methods. This can be used as a coarse-grained authorization system for both `puppet agent` and `puppet master`." ], :environment => {:default => "production", :desc => "The environment Puppet is running in. For clients (e.g., `puppet agent`) this determines the environment itself, which is used to find modules and much more. For servers (i.e., `puppet master`) this provides the default environment for nodes we know nothing about." }, :diff_args => ["-u", "Which arguments to pass to the diff command when printing differences between files."], :diff => ["diff", "Which diff command to use when printing differences between files."], :show_diff => [false, "Whether to print a contextual diff when files are being replaced. The diff is printed on stdout, so this option is meaningless unless you are running Puppet interactively. This feature currently requires the `diff/lcs` Ruby library."], :daemonize => { :default => true, :desc => "Send the process into the background. This is the default.", :short => "D" }, :maximum_uid => [4294967290, "The maximum allowed UID. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system. This is a hackish way to fail in a slightly more useful way when that happens."], + :route_file => ["$confdir/routes.yaml", "The YAML file containing indirector route configuration."], :node_terminus => ["plain", "Where to find information about nodes."], :catalog_terminus => ["compiler", "Where to get node catalogs. This is useful to change if, for instance, you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store."], :facts_terminus => { :default => Puppet.application_name.to_s == "master" ? 'yaml' : 'facter', :desc => "The node facts terminus.", :hook => proc do |value| require 'puppet/node/facts' if value.to_s == "rest" Puppet::Node::Facts.indirection.cache_class = :yaml end end }, :inventory_terminus => [ "$facts_terminus", "Should usually be the same as the facts terminus" ], :httplog => { :default => "$logdir/http.log", :owner => "root", :mode => 0640, :desc => "Where the puppet agent web server logs." }, :http_proxy_host => ["none", "The HTTP proxy host to use for outgoing connections. Note: You may need to use a FQDN for the server hostname when using a proxy."], :http_proxy_port => [3128, "The HTTP proxy port to use for outgoing connections"], :filetimeout => [ 15, "The minimum time to wait (in seconds) between checking for updates in configuration files. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk." ], :queue_type => ["stomp", "Which type of queue to use for asynchronous processing."], :queue_type => ["stomp", "Which type of queue to use for asynchronous processing."], :queue_source => ["stomp://localhost:61613/", "Which type of queue to use for asynchronous processing. If your stomp server requires authentication, you can include it in the URI as long as your stomp client library is at least 1.1.1"], :async_storeconfigs => {:default => false, :desc => "Whether to use a queueing system to provide asynchronous database integration. Requires that `puppetqd` be running and that 'PSON' support for ruby be installed.", :hook => proc do |value| if value # This reconfigures the terminii for Node, Facts, and Catalog Puppet.settings[:storeconfigs] = true # But then we modify the configuration Puppet::Resource::Catalog.indirection.cache_class = :queue else raise "Cannot disable asynchronous storeconfigs in a running process" end end }, :thin_storeconfigs => {:default => false, :desc => "Boolean; wether storeconfigs store in the database only the facts and exported resources. If true, then storeconfigs performance will be higher and still allow exported/collected resources, but other usage external to Puppet might not work", :hook => proc do |value| Puppet.settings[:storeconfigs] = true if value end }, :config_version => ["", "How to determine the configuration version. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server."], :zlib => [true, "Boolean; whether to use the zlib library", ], :prerun_command => ["", "A command to run before every agent run. If this command returns a non-zero return code, the entire Puppet run will fail."], :postrun_command => ["", "A command to run after every agent run. If this command returns a non-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run."], :freeze_main => [false, "Freezes the 'main' class, disallowing any code to be added to it. This essentially means that you can't have any code outside of a node, class, or definition other than in the site manifest."] ) hostname = Facter["hostname"].value domain = Facter["domain"].value if domain and domain != "" fqdn = [hostname, domain].join(".") else fqdn = hostname end Puppet.setdefaults( :main, # We have to downcase the fqdn, because the current ssl stuff (as oppsed to in master) doesn't have good facilities for # manipulating naming. :certname => {:default => fqdn.downcase, :desc => "The name to use when handling certificates. Defaults to the fully qualified domain name.", :call_on_define => true, # Call our hook with the default value, so we're always downcased :hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }}, :certdnsnames => ['', "The DNS names on the Server certificate as a colon-separated list. If it's anything other than an empty string, it will be used as an alias in the created certificate. By default, only the server gets an alias set up, and only for 'puppet'."], :certdir => { :default => "$ssldir/certs", :owner => "service", :desc => "The certificate directory." }, :ssldir => { :default => "$confdir/ssl", :mode => 0771, :owner => "service", :desc => "Where SSL certificates are kept." }, :publickeydir => { :default => "$ssldir/public_keys", :owner => "service", :desc => "The public key directory." }, :requestdir => { :default => "$ssldir/certificate_requests", :owner => "service", :desc => "Where host certificate requests are stored." }, :privatekeydir => { :default => "$ssldir/private_keys", :mode => 0750, :owner => "service", :desc => "The private key directory." }, :privatedir => { :default => "$ssldir/private", :mode => 0750, :owner => "service", :desc => "Where the client stores private certificate information." }, :passfile => { :default => "$privatedir/password", :mode => 0640, :owner => "service", :desc => "Where puppet agent stores the password for its private key. Generally unused." }, :hostcsr => { :default => "$ssldir/csr_$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificate requests." }, :hostcert => { :default => "$certdir/$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificates." }, :hostprivkey => { :default => "$privatekeydir/$certname.pem", :mode => 0600, :owner => "service", :desc => "Where individual hosts store and look for their private key." }, :hostpubkey => { :default => "$publickeydir/$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their public key." }, :localcacert => { :default => "$certdir/ca.pem", :mode => 0644, :owner => "service", :desc => "Where each client stores the CA certificate." }, :hostcrl => { :default => "$ssldir/crl.pem", :mode => 0644, :owner => "service", :desc => "Where the host's certificate revocation list can be found. This is distinct from the certificate authority's CRL." }, :certificate_revocation => [true, "Whether certificate revocation should be supported by downloading a Certificate Revocation List (CRL) to all clients. If enabled, CA chaining will almost definitely not work."] ) setdefaults( :ca, :ca_name => ["Puppet CA: $certname", "The name to use the Certificate Authority certificate."], :cadir => { :default => "$ssldir/ca", :owner => "service", :group => "service", :mode => 0770, :desc => "The root directory for the certificate authority." }, :cacert => { :default => "$cadir/ca_crt.pem", :owner => "service", :group => "service", :mode => 0660, :desc => "The CA certificate." }, :cakey => { :default => "$cadir/ca_key.pem", :owner => "service", :group => "service", :mode => 0660, :desc => "The CA private key." }, :capub => { :default => "$cadir/ca_pub.pem", :owner => "service", :group => "service", :desc => "The CA public key." }, :cacrl => { :default => "$cadir/ca_crl.pem", :owner => "service", :group => "service", :mode => 0664, :desc => "The certificate revocation list (CRL) for the CA. Will be used if present but otherwise ignored.", :hook => proc do |value| if value == 'false' Puppet.warning "Setting the :cacrl to 'false' is deprecated; Puppet will just ignore the crl if yours is missing" end end }, :caprivatedir => { :default => "$cadir/private", :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores private certificate information." }, :csrdir => { :default => "$cadir/requests", :owner => "service", :group => "service", :desc => "Where the CA stores certificate requests" }, :signeddir => { :default => "$cadir/signed", :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores signed certificates." }, :capass => { :default => "$caprivatedir/ca.pass", :owner => "service", :group => "service", :mode => 0660, :desc => "Where the CA stores the password for the private key" }, :serial => { :default => "$cadir/serial", :owner => "service", :group => "service", :mode => 0644, :desc => "Where the serial number for certificates is stored." }, :autosign => { :default => "$confdir/autosign.conf", :mode => 0644, :desc => "Whether to enable autosign. Valid values are true (which autosigns any key request, and is a very bad idea), false (which never autosigns any key request), and the path to a file, which uses that configuration file to determine which keys to sign."}, + :allow_duplicate_certs => [false, "Whether to allow a new certificate + request to overwrite an existing certificate."], :ca_days => ["", "How long a certificate should be valid. This parameter is deprecated, use ca_ttl instead"], :ca_ttl => ["5y", "The default TTL for new certificates; valid values must be an integer, optionally followed by one of the units 'y' (years of 365 days), 'd' (days), 'h' (hours), or 's' (seconds). The unit defaults to seconds. If this parameter is set, ca_days is ignored. Examples are '3600' (one hour) and '1825d', which is the same as '5y' (5 years) "], :ca_md => ["md5", "The type of hash used in certificates."], :req_bits => [2048, "The bit length of the certificates."], :keylength => [1024, "The bit length of keys."], :cert_inventory => { :default => "$cadir/inventory.txt", :mode => 0644, :owner => "service", :group => "service", :desc => "A Complete listing of all certificates" } ) # Define the config default. setdefaults( Puppet.settings[:name], :config => ["$confdir/puppet.conf", "The configuration file for #{Puppet[:name]}."], :pidfile => ["$rundir/$name.pid", "The pid file"], :bindaddress => ["", "The address a listening server should bind to. Mongrel servers default to 127.0.0.1 and WEBrick defaults to 0.0.0.0."], :servertype => {:default => "webrick", :desc => "The type of server to use. Currently supported options are webrick and mongrel. If you use mongrel, you will need a proxy in front of the process or processes, since Mongrel cannot speak SSL.", :call_on_define => true, # Call our hook with the default value, so we always get the correct bind address set. :hook => proc { |value| value == "webrick" ? Puppet.settings[:bindaddress] = "0.0.0.0" : Puppet.settings[:bindaddress] = "127.0.0.1" if Puppet.settings[:bindaddress] == "" } } ) setdefaults(:master, :user => ["puppet", "The user puppet master should run as."], :group => ["puppet", "The group puppet master should run as."], :manifestdir => ["$confdir/manifests", "Where puppet master looks for its manifests."], :manifest => ["$manifestdir/site.pp", "The entry-point manifest for puppet master."], :code => ["", "Code to parse directly. This is essentially only used by `puppet`, and should only be set if you're writing your own Puppet executable"], :masterlog => { :default => "$logdir/puppetmaster.log", :owner => "service", :group => "service", :mode => 0660, :desc => "Where puppet master logs. This is generally not used, since syslog is the default log destination." }, :masterhttplog => { :default => "$logdir/masterhttp.log", :owner => "service", :group => "service", :mode => 0660, :create => true, :desc => "Where the puppet master web server logs." }, :masterport => [8140, "Which port puppet master listens on."], - :parseonly => [false, "Just check the syntax of the manifests."], :node_name => ["cert", "How the puppetmaster determines the client's identity and sets the 'hostname', 'fqdn' and 'domain' facts for use in the manifest, in particular for determining which 'node' statement applies to the client. Possible values are 'cert' (use the subject's CN in the client's certificate) and 'facter' (use the hostname that the client reported in its facts)"], :bucketdir => { :default => "$vardir/bucket", :mode => 0750, :owner => "service", :group => "service", :desc => "Where FileBucket files are stored." }, :rest_authconfig => [ "$confdir/auth.conf", "The configuration file that defines the rights to the different rest indirections. This can be used as a fine-grained authorization system for `puppet master`." ], :ca => [true, "Wether the master should function as a certificate authority."], :modulepath => {:default => "$confdir/modules:/usr/share/puppet/modules", :desc => "The search path for modules as a colon-separated list of directories.", :type => :setting }, # We don't want this to be considered a file, since it's multiple files. :ssl_client_header => ["HTTP_X_CLIENT_DN", "The header containing an authenticated client's SSL DN. Only used with Mongrel. This header must be set by the proxy to the authenticated client's SSL DN (e.g., `/CN=puppet.puppetlabs.com`). See http://projects.puppetlabs.com/projects/puppet/wiki/Using_Mongrel for more information."], :ssl_client_verify_header => ["HTTP_X_CLIENT_VERIFY", "The header containing the status message of the client verification. Only used with Mongrel. This header must be set by the proxy to 'SUCCESS' if the client successfully authenticated, and anything else otherwise. See http://projects.puppetlabs.com/projects/puppet/wiki/Using_Mongrel for more information."], # To make sure this directory is created before we try to use it on the server, we need # it to be in the server section (#1138). :yamldir => {:default => "$vardir/yaml", :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which YAML data is stored, usually in a subdirectory."}, :server_datadir => {:default => "$vardir/server_data", :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which serialized data is stored, usually in a subdirectory."}, :reports => ["store", "The list of reports to generate. All reports are looked for in `puppet/reports/name.rb`, and multiple report names should be comma-separated (whitespace is okay)." ], :reportdir => {:default => "$vardir/reports", :mode => 0750, :owner => "service", :group => "service", :desc => "The directory in which to store reports received from the client. Each client gets a separate subdirectory."}, :reporturl => ["http://localhost:3000/reports", "The URL used by the http reports processor to send reports"], :fileserverconfig => ["$confdir/fileserver.conf", "Where the fileserver configuration is stored."], :strict_hostname_checking => [false, "Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs."] ) setdefaults(:metrics, :rrddir => {:default => "$vardir/rrd", :mode => 0750, :owner => "service", :group => "service", :desc => "The directory where RRD database files are stored. Directories for each reporting host will be created under this directory." }, :rrdinterval => ["$runinterval", "How often RRD should expect data. This should match how often the hosts report back to the server."] ) setdefaults(:agent, :localconfig => { :default => "$statedir/localconfig", :owner => "root", :mode => 0660, :desc => "Where puppet agent caches the local configuration. An extension indicating the cache format is added automatically."}, :statefile => { :default => "$statedir/state.yaml", :mode => 0660, :desc => "Where puppet agent and puppet master store state associated with the running configuration. In the case of puppet master, this file reflects the state discovered through interacting with clients." }, :clientyamldir => {:default => "$vardir/client_yaml", :mode => "750", :desc => "The directory in which client-side YAML data is stored."}, :client_datadir => {:default => "$vardir/client_data", :mode => "750", :desc => "The directory in which serialized data is stored on the client."}, :classfile => { :default => "$statedir/classes.txt", :owner => "root", :mode => 0644, :desc => "The file in which puppet agent stores a list of the classes associated with the retrieved configuration. Can be loaded in the separate `puppet` executable using the `--loadclasses` option."}, :puppetdlog => { :default => "$logdir/puppetd.log", :owner => "root", :mode => 0640, :desc => "The log file for puppet agent. This is generally not used." }, :server => ["puppet", "The server to which server puppet agent should connect"], :ignoreschedules => [false, "Boolean; whether puppet agent should ignore schedules. This is useful for initial puppet agent runs."], :puppetport => [8139, "Which port puppet agent listens on."], :noop => [false, "Whether puppet agent should be run in noop mode."], :runinterval => [1800, # 30 minutes "How often puppet agent applies the client configuration; in seconds."], :listen => [false, "Whether puppet agent should listen for connections. If this is true, then by default only the `runner` server is started, which allows remote authorized and authenticated nodes to connect and trigger `puppet agent` runs."], :ca_server => ["$server", "The server to use for certificate authority requests. It's a separate server because it cannot and does not need to horizontally scale."], :ca_port => ["$masterport", "The port to use for the certificate authority."], :catalog_format => { :default => "", :desc => "(Deprecated for 'preferred_serialization_format') What format to use to dump the catalog. Only supports 'marshal' and 'yaml'. Only matters on the client, since it asks the server for a specific format.", :hook => proc { |value| if value Puppet.warning "Setting 'catalog_format' is deprecated; use 'preferred_serialization_format' instead." Puppet.settings[:preferred_serialization_format] = value end } }, :preferred_serialization_format => ["pson", "The preferred means of serializing ruby instances for passing over the wire. This won't guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it."], :puppetdlockfile => [ "$statedir/puppetdlock", "A lock file to temporarily stop puppet agent from doing anything."], :usecacheonfailure => [true, "Whether to use the cached configuration when the remote configuration will not compile. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known-good one." ], :use_cached_catalog => [false, "Whether to only use the cached catalog rather than compiling a new catalog on every run. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired."], :ignorecache => [false, "Ignore cache and always recompile the configuration. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date - if the facts change or if the server changes." ], :downcasefacts => [false, "Whether facts should be made all lowercase when sent to the server."], :dynamicfacts => ["memorysize,memoryfree,swapsize,swapfree", "Facts that are dynamic; these facts will be ignored when deciding whether changed facts should result in a recompile. Multiple facts should be comma-separated."], :splaylimit => ["$runinterval", "The maximum time to delay before runs. Defaults to being the same as the run interval."], :splay => [false, "Whether to sleep for a pseudo-random (but consistent) amount of time before a run."], :clientbucketdir => { :default => "$vardir/clientbucket", :mode => 0750, :desc => "Where FileBucket files are stored locally." }, :configtimeout => [120, "How long the client should wait for the configuration to be retrieved before considering it a failure. This can help reduce flapping if too many clients contact the server at one time." ], :reportserver => { :default => "$server", :call_on_define => false, :desc => "(Deprecated for 'report_server') The server to which to send transaction reports.", :hook => proc do |value| Puppet.settings[:report_server] = value if value end }, :report_server => ["$server", "The server to send transaction reports to." ], :report_port => ["$masterport", "The port to communicate with the report_server." ], :inventory_server => ["$server", "The server to send facts to." ], :inventory_port => ["$masterport", "The port to communicate with the inventory_server." ], :report => [true, "Whether to send reports after every transaction." ], :lastrunfile => { :default => "$statedir/last_run_summary.yaml", :mode => 0660, :desc => "Where puppet agent stores the last run report summary in yaml format." }, :lastrunreport => { :default => "$statedir/last_run_report.yaml", :mode => 0660, :desc => "Where puppet agent stores the last run report in yaml format." }, :graph => [false, "Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick)."], :graphdir => ["$statedir/graphs", "Where to store dot-outputted graphs."], :http_compression => [false, "Allow http compression in REST communication with the master. This setting might improve performance for agent -> master communications over slow WANs. Your puppetmaster needs to support compression (usually by activating some settings in a reverse-proxy in front of the puppetmaster, which rules out webrick). It is harmless to activate this settings if your master doesn't support compression, but if it supports it, this setting might reduce performance on high-speed LANs."] ) setdefaults(:inspect, :archive_files => [false, "During an inspect run, whether to archive files whose contents are audited to a file bucket."], :archive_file_server => ["$server", "During an inspect run, the file bucket server to archive files to if archive_files is set."] ) # Plugin information. setdefaults( :main, :plugindest => ["$libdir", "Where Puppet should store plugins that it pulls down from the central server."], :pluginsource => ["puppet://$server/plugins", "From where to retrieve plugins. The standard Puppet `file` type is used for retrieval, so anything that is a valid file source can be used here."], :pluginsync => [false, "Whether plugins should be synced with the central server."], :pluginsignore => [".svn CVS .git", "What files to ignore when pulling down plugins."] ) # Central fact information. setdefaults( :main, :factpath => {:default => "$vardir/lib/facter:$vardir/facts", :desc => "Where Puppet should look for facts. Multiple directories should be colon-separated, like normal PATH variables.", :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter. :type => :setting, # Don't consider it a file, because it could be multiple colon-separated files :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }}, :factdest => ["$vardir/facts/", "Where Puppet should store facts that it pulls down from the central server."], :factsource => ["puppet://$server/facts/", "From where to retrieve facts. The standard Puppet `file` type is used for retrieval, so anything that is a valid file source can be used here."], :factsync => [false, "Whether facts should be synced with the central server."], :factsignore => [".svn CVS", "What files to ignore when pulling down facts."] ) setdefaults( :tagmail, :tagmap => ["$confdir/tagmail.conf", "The mapping between reporting tags and email addresses."], :sendmail => [which('sendmail') || '', "Where to find the sendmail binary with which to send email."], :reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."), "The 'from' email address for the reports."], :smtpserver => ["none", "The server through which to send email reports."] ) setdefaults( :rails, :dblocation => { :default => "$statedir/clientconfigs.sqlite3", :mode => 0660, :owner => "service", :group => "service", :desc => "The database cache for client configurations. Used for querying within the language." }, :dbadapter => [ "sqlite3", "The type of database to use." ], :dbmigrate => [ false, "Whether to automatically migrate the database." ], :dbname => [ "puppet", "The name of the database to use." ], :dbserver => [ "localhost", "The database server for caching. Only used when networked databases are used."], :dbport => [ "", "The database password for caching. Only used when networked databases are used."], :dbuser => [ "puppet", "The database user for caching. Only used when networked databases are used."], :dbpassword => [ "puppet", "The database password for caching. Only used when networked databases are used."], :dbconnections => [ '', "The number of database connections for networked databases. Will be ignored unless the value is a positive integer."], :dbsocket => [ "", "The database socket location. Only used when networked databases are used. Will be ignored if the value is an empty string."], :railslog => {:default => "$logdir/rails.log", :mode => 0600, :owner => "service", :group => "service", :desc => "Where Rails-specific logs are sent" }, :rails_loglevel => ["info", "The log level for Rails connections. The value must be a valid log level within Rails. Production environments normally use `info` and other environments normally use `debug`."] ) setdefaults( :couchdb, :couchdb_url => ["http://127.0.0.1:5984/puppet", "The url where the puppet couchdb database will be created"] ) setdefaults( :transaction, :tags => ["", "Tags to use to find resources. If this is set, then only resources tagged with the specified tags will be applied. Values must be comma-separated."], :evaltrace => [false, "Whether each resource should log when it is being evaluated. This allows you to interactively see exactly what is being done."], :summarize => [false, "Whether to print a transaction summary." ] ) setdefaults( :main, :external_nodes => ["none", "An external command that can produce node information. The output must be a YAML dump of a hash, and that hash must have one or both of `classes` and `parameters`, where `classes` is an array and `parameters` is a hash. For unknown nodes, the commands should exit with a non-zero exit code. This command makes it straightforward to store your node mapping information in other data sources like databases."]) setdefaults( :ldap, :ldapnodes => [false, "Whether to search for node configurations in LDAP. See http://projects.puppetlabs.com/projects/puppet/wiki/LDAP_Nodes for more information."], :ldapssl => [false, "Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side."], :ldaptls => [false, "Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side."], :ldapserver => ["ldap", "The LDAP server. Only used if `ldapnodes` is enabled."], :ldapport => [389, "The LDAP port. Only used if `ldapnodes` is enabled."], :ldapstring => ["(&(objectclass=puppetClient)(cn=%s))", "The search string used to find an LDAP node."], :ldapclassattrs => ["puppetclass", "The LDAP attributes to use to define Puppet classes. Values should be comma-separated."], :ldapstackedattrs => ["puppetvar", "The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree. Values should be comma-separated."], :ldapattrs => ["all", "The LDAP attributes to include when querying LDAP for nodes. All returned attributes are set as variables in the top-level scope. Multiple values should be comma-separated. The value 'all' returns all attributes."], :ldapparentattr => ["parentnode", "The attribute to use to define the parent node."], :ldapuser => ["", "The user to use to connect to LDAP. Must be specified as a full DN."], :ldappassword => ["", "The password to use to connect to LDAP."], :ldapbase => ["", "The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory."] ) setdefaults(:master, :storeconfigs => {:default => false, :desc => "Whether to store each client's configuration. This requires ActiveRecord from Ruby on Rails.", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| require 'puppet/node' require 'puppet/node/facts' if value require 'puppet/rails' raise "StoreConfigs not supported without ActiveRecord 2.1 or higher" unless Puppet.features.rails? Puppet::Resource::Catalog.indirection.cache_class = :active_record unless Puppet.settings[:async_storeconfigs] Puppet::Node::Facts.indirection.cache_class = :active_record Puppet::Node.indirection.cache_class = :active_record end end } ) # This doesn't actually work right now. setdefaults( :parser, :lexical => [false, "Whether to use lexical scoping (vs. dynamic)."], :templatedir => ["$vardir/templates", "Where Puppet looks for template files. Can be a list of colon-seperated directories." ] ) setdefaults( :puppetdoc, :document_all => [false, "Document all resources"] ) end diff --git a/lib/puppet/face.rb b/lib/puppet/face.rb new file mode 100644 index 000000000..f73b2fc3b --- /dev/null +++ b/lib/puppet/face.rb @@ -0,0 +1,12 @@ +# The public name of this feature is 'face', but we have hidden all the +# plumbing over in the 'interfaces' namespace to make clear the distinction +# between the two. +# +# This file exists to ensure that the public name is usable without revealing +# the details of the implementation; you really only need go look at anything +# under Interfaces if you are looking to extend the implementation. +# +# It isn't hidden to gratuitously hide things, just to make it easier to +# separate out the interests people will have. --daniel 2011-04-07 +require 'puppet/interface' +Puppet::Face = Puppet::Interface diff --git a/lib/puppet/face/catalog.rb b/lib/puppet/face/catalog.rb new file mode 100644 index 000000000..0dcde3591 --- /dev/null +++ b/lib/puppet/face/catalog.rb @@ -0,0 +1,40 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:catalog, '0.0.1') do + action(:apply) do + when_invoked do |catalog, options| + report = Puppet::Transaction::Report.new("apply") + report.configuration_version = catalog.version + + Puppet::Util::Log.newdestination(report) + + begin + benchmark(:notice, "Finished catalog run") do + catalog.apply(:report => report) + end + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err "Failed to apply catalog: #{detail}" + end + + report.finalize_report + report + end + end + + action(:download) do + when_invoked do |certname, facts, options| + Puppet::Resource::Catalog.indirection.terminus_class = :rest + facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))} + catalog = nil + retrieval_duration = thinmark do + catalog = Puppet::Face[:catalog, '0.0.1'].find(certname, facts_to_upload) + end + catalog = catalog.to_ral + catalog.finalize + catalog.retrieval_duration = retrieval_duration + catalog.write_class_file + catalog + end + end +end diff --git a/lib/puppet/face/catalog/select.rb b/lib/puppet/face/catalog/select.rb new file mode 100644 index 000000000..ba27117bc --- /dev/null +++ b/lib/puppet/face/catalog/select.rb @@ -0,0 +1,10 @@ +# Select and show a list of resources of a given type. +Puppet::Face.define(:catalog, '0.0.1') do + action :select do + when_invoked do |host, type, options| + catalog = Puppet::Resource::Catalog.indirection.find(host) + + catalog.resources.reject { |res| res.type != type }.each { |res| puts res } + end + end +end diff --git a/lib/puppet/face/certificate.rb b/lib/puppet/face/certificate.rb new file mode 100644 index 000000000..77e80f099 --- /dev/null +++ b/lib/puppet/face/certificate.rb @@ -0,0 +1,46 @@ +require 'puppet/face/indirector' +require 'puppet/ssl/host' + +Puppet::Face::Indirector.define(:certificate, '0.0.1') do + # REVISIT: This should use a pre-invoke hook to run the common code that + # needs to happen before we invoke any action; that would be much nicer than + # the "please repeat yourself" stuff found in here right now. + # + # option "--ca-location LOCATION" do + # type [:whatever, :location, :symbols] + # hook :before do |value| + # Puppet::SSL::Host.ca_location = value + # end + # end + # + # ...but should I pass the arguments as well? + # --daniel 2011-04-05 + option "--ca-location LOCATION" + + action :generate do + when_invoked do |name, options| + Puppet::SSL::Host.ca_location = options[:ca_location].to_sym + host = Puppet::SSL::Host.new(name) + host.generate_certificate_request + host.certificate_request.class.indirection.save(host.certificate_request) + end + end + + action :list do + when_invoked do |options| + Puppet::SSL::Host.ca_location = options[:ca_location].to_sym + Puppet::SSL::Host.indirection.search("*", { + :for => :certificate_request, + }).map { |h| h.inspect } + end + end + + action :sign do + when_invoked do |name, options| + Puppet::SSL::Host.ca_location = options[:ca_location].to_sym + host = Puppet::SSL::Host.new(name) + host.desired_state = 'signed' + Puppet::SSL::Host.indirection.save(host) + end + end +end diff --git a/lib/puppet/face/certificate_request.rb b/lib/puppet/face/certificate_request.rb new file mode 100644 index 000000000..1feba25ab --- /dev/null +++ b/lib/puppet/face/certificate_request.rb @@ -0,0 +1,4 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:certificate_request, '0.0.1') do +end diff --git a/lib/puppet/face/certificate_revocation_list.rb b/lib/puppet/face/certificate_revocation_list.rb new file mode 100644 index 000000000..6a75aa578 --- /dev/null +++ b/lib/puppet/face/certificate_revocation_list.rb @@ -0,0 +1,4 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:certificate_revocation_list, '0.0.1') do +end diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb new file mode 100644 index 000000000..45cb6b156 --- /dev/null +++ b/lib/puppet/face/config.rb @@ -0,0 +1,12 @@ +require 'puppet/face' + +Puppet::Face.define(:config, '0.0.1') do + action(:print) do + when_invoked do |*args| + options = args.pop + Puppet.settings[:configprint] = args.join(",") + Puppet.settings.print_config_options + nil + end + end +end diff --git a/lib/puppet/face/configurer.rb b/lib/puppet/face/configurer.rb new file mode 100644 index 000000000..74dfb854e --- /dev/null +++ b/lib/puppet/face/configurer.rb @@ -0,0 +1,12 @@ +require 'puppet/face' + +Puppet::Face.define(:configurer, '0.0.1') do + action(:synchronize) do + when_invoked do |certname, options| + facts = Puppet::Face[:facts, '0.0.1'].find(certname) + catalog = Puppet::Face[:catalog, '0.0.1'].download(certname, facts) + report = Puppet::Face[:catalog, '0.0.1'].apply(catalog) + report + end + end +end diff --git a/lib/puppet/face/facts.rb b/lib/puppet/face/facts.rb new file mode 100644 index 000000000..8668b2531 --- /dev/null +++ b/lib/puppet/face/facts.rb @@ -0,0 +1,18 @@ +require 'puppet/face/indirector' +require 'puppet/node/facts' + +Puppet::Face::Indirector.define(:facts, '0.0.1') do + set_default_format :yaml + + # Upload our facts to the server + action(:upload) do + when_invoked do |options| + Puppet::Node::Facts.indirection.terminus_class = :facter + facts = Puppet::Node::Facts.indirection.find(Puppet[:certname]) + Puppet::Node::Facts.indirection.terminus_class = :rest + Puppet::Node::Facts.indirection.save(facts) + Puppet.notice "Uploaded facts for '#{Puppet[:certname]}'" + nil + end + end +end diff --git a/lib/puppet/face/file.rb b/lib/puppet/face/file.rb new file mode 100644 index 000000000..1aa9462dd --- /dev/null +++ b/lib/puppet/face/file.rb @@ -0,0 +1,5 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:file, '0.0.1') do + set_indirection_name :file_bucket_file +end diff --git a/lib/puppet/face/help.rb b/lib/puppet/face/help.rb new file mode 100644 index 000000000..1c2da9e83 --- /dev/null +++ b/lib/puppet/face/help.rb @@ -0,0 +1,104 @@ +require 'puppet/face' +require 'puppet/util/command_line' +require 'pathname' +require 'erb' + +Puppet::Face.define(:help, '0.0.1') do + summary "Displays help about puppet subcommands" + + action(:help) do + summary "Display help about faces and their actions." + + option "--version VERSION" do + desc "Which version of the interface to show help for" + end + + when_invoked do |*args| + # Check our invocation, because we want varargs and can't do defaults + # yet. REVISIT: when we do option defaults, and positional options, we + # should rewrite this to use those. --daniel 2011-04-04 + options = args.pop + if options.nil? or args.length > 2 then + raise ArgumentError, "help only takes two (optional) arguments, a face name, and an action" + end + + version = :current + if options.has_key? :version then + if options[:version].to_s !~ /^current$/i then + version = options[:version] + else + if args.length == 0 then + raise ArgumentError, "version only makes sense when a face is given" + end + end + end + + # Name those parameters... + facename, actionname = args + + if facename then + if legacy_applications.include? facename then + actionname and raise ArgumentError, "Legacy subcommands don't take actions" + return Puppet::Application[facename].help + else + face = Puppet::Face[facename.to_sym, version] + actionname and action = face.get_action(actionname.to_sym) + end + end + + case args.length + when 0 then + template = erb 'global.erb' + when 1 then + face or fail ArgumentError, "Unable to load face #{facename}" + template = erb 'face.erb' + when 2 then + face or fail ArgumentError, "Unable to load face #{facename}" + action or fail ArgumentError, "Unable to load action #{actionname} from #{face}" + template = erb 'action.erb' + else + fail ArgumentError, "Too many arguments to help action" + end + + # Run the ERB template in our current binding, including all the local + # variables we established just above. --daniel 2011-04-11 + return template.result(binding) + end + end + + def erb(name) + template = (Pathname(__FILE__).dirname + "help" + name) + erb = ERB.new(template.read, nil, '%') + erb.filename = template.to_s + return erb + end + + def legacy_applications + # The list of applications, less those that are duplicated as a face. + Puppet::Util::CommandLine.available_subcommands.reject do |appname| + Puppet::Face.face? appname.to_sym, :current or + # ...this is a nasty way to exclude non-applications. :( + %w{face_base indirection_base}.include? appname + end.sort + end + + def horribly_extract_summary_from(appname) + begin + require "puppet/application/#{appname}" + help = Puppet::Application[appname].help.split("\n") + # Now we find the line with our summary, extract it, and return it. This + # depends on the implementation coincidence of how our pages are + # formatted. If we can't match the pattern we expect we return the empty + # string to ensure we don't blow up in the summary. --daniel 2011-04-11 + while line = help.shift do + if md = /^puppet-#{appname}\([^\)]+\) -- (.*)$/.match(line) then + return md[1] + end + end + rescue Exception + # Damn, but I hate this: we just ignore errors here, no matter what + # class they are. Meh. + end + return '' + end +end diff --git a/lib/puppet/face/help/action.erb b/lib/puppet/face/help/action.erb new file mode 100644 index 000000000..eaf131464 --- /dev/null +++ b/lib/puppet/face/help/action.erb @@ -0,0 +1,3 @@ +Use: puppet <%= face.name %> [options] <%= action.name %> [options] + +Summary: <%= action.summary %> diff --git a/lib/puppet/face/help/face.erb b/lib/puppet/face/help/face.erb new file mode 100644 index 000000000..efe5fd809 --- /dev/null +++ b/lib/puppet/face/help/face.erb @@ -0,0 +1,7 @@ +Use: puppet <%= face.name %> [options] [options] + +Available actions: +% face.actions.each do |actionname| +% action = face.get_action(actionname) + <%= action.name.to_s.ljust(16) %> <%= action.summary %> +% end diff --git a/lib/puppet/face/help/global.erb b/lib/puppet/face/help/global.erb new file mode 100644 index 000000000..f4c761b2b --- /dev/null +++ b/lib/puppet/face/help/global.erb @@ -0,0 +1,20 @@ +puppet [options] [options] + +Available subcommands, from Puppet Faces: +% Puppet::Face.faces.sort.each do |name| +% face = Puppet::Face[name, :current] + <%= face.name.to_s.ljust(16) %> <%= face.summary %> +% end + +% unless legacy_applications.empty? then # great victory when this is true! +Available applications, soon to be ported to Faces: +% legacy_applications.each do |appname| +% summary = horribly_extract_summary_from appname + <%= appname.to_s.ljust(16) %> <%= summary %> +% end +% end + +See 'puppet help ' for help on a specific subcommand action. +See 'puppet help ' for help on a specific subcommand. +See 'puppet man ' for the full man page. +Puppet v<%= Puppet::PUPPETVERSION %> diff --git a/lib/puppet/face/indirector.rb b/lib/puppet/face/indirector.rb new file mode 100644 index 000000000..f48611e4b --- /dev/null +++ b/lib/puppet/face/indirector.rb @@ -0,0 +1,94 @@ +require 'puppet' +require 'puppet/face' + +class Puppet::Face::Indirector < Puppet::Face + option "--terminus TERMINUS" do + desc "REVISIT: You can select a terminus, which has some bigger effect +that we should describe in this file somehow." + end + + def self.indirections + Puppet::Indirector::Indirection.instances.collect { |t| t.to_s }.sort + end + + def self.terminus_classes(indirection) + Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect { |t| t.to_s }.sort + end + + def call_indirection_method(method, *args) + options = args.last + options.has_key?(:terminus) and set_terminus(options[:terminus]) + + begin + result = indirection.__send__(method, *args) + rescue => detail + puts detail.backtrace if Puppet[:trace] + raise "Could not call '#{method}' on '#{indirection_name}': #{detail}" + end + + indirection.reset_terminus_class + return result + end + + action :destroy do + when_invoked { |*args| call_indirection_method(:destroy, *args) } + end + + action :find do + when_invoked { |*args| call_indirection_method(:find, *args) } + end + + action :save do + when_invoked { |*args| call_indirection_method(:save, *args) } + end + + action :search do + when_invoked { |*args| call_indirection_method(:search, *args) } + end + + # Print the configuration for the current terminus class + action :info do + when_invoked do |*args| + options = args.pop + options.has_key?(:terminus) and set_terminus(options[:terminus]) + + if t = indirection.terminus_class + puts "Run mode '#{Puppet.run_mode.name}': #{t}" + else + $stderr.puts "No default terminus class for run mode '#{Puppet.run_mode.name}'" + end + + indirection.reset_terminus_class + end + end + + attr_accessor :from + + def indirection_name + @indirection_name || name.to_sym + end + + # Here's your opportunity to override the indirection name. By default it + # will be the same name as the face. + def set_indirection_name(name) + @indirection_name = name + end + + # Return an indirection associated with a face, if one exists; + # One usually does. + def indirection + unless @indirection + @indirection = Puppet::Indirector::Indirection.instance(indirection_name) + @indirection or raise "Could not find terminus for #{indirection_name}" + end + @indirection + end + + def set_terminus(from) + begin + indirection.terminus_class = from + rescue => detail + raise "Could not set '#{indirection.name}' terminus to '#{from}' (#{detail}); valid terminus types are #{self.class.terminus_classes(indirection.name).join(", ") }" + end + end +end diff --git a/lib/puppet/face/key.rb b/lib/puppet/face/key.rb new file mode 100644 index 000000000..3a11ddb03 --- /dev/null +++ b/lib/puppet/face/key.rb @@ -0,0 +1,4 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:key, '0.0.1') do +end diff --git a/lib/puppet/face/node.rb b/lib/puppet/face/node.rb new file mode 100644 index 000000000..fd1a548d6 --- /dev/null +++ b/lib/puppet/face/node.rb @@ -0,0 +1,5 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:node, '0.0.1') do + set_default_format :yaml +end diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb new file mode 100644 index 000000000..c44810b99 --- /dev/null +++ b/lib/puppet/face/parser.rb @@ -0,0 +1,17 @@ +require 'puppet/face' +require 'puppet/parser' + +Puppet::Face.define(:parser, '0.0.1') do + action :validate do + when_invoked do |*args| + args.pop + files = args + files << Puppet[:manifest] if files.empty? + files.each do |file| + Puppet[:manifest] = file + Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types.clear + end + nil + end + end +end diff --git a/lib/puppet/face/report.rb b/lib/puppet/face/report.rb new file mode 100644 index 000000000..6e6f0b335 --- /dev/null +++ b/lib/puppet/face/report.rb @@ -0,0 +1,15 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:report, '0.0.1') do + action(:submit) do + when_invoked do |report, options| + begin + Puppet::Transaction::Report.terminus_class = :rest + report.save + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err "Could not send report: #{detail}" + end + end + end +end diff --git a/lib/puppet/face/resource.rb b/lib/puppet/face/resource.rb new file mode 100644 index 000000000..d162f728a --- /dev/null +++ b/lib/puppet/face/resource.rb @@ -0,0 +1,4 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:resource, '0.0.1') do +end diff --git a/lib/puppet/face/resource_type.rb b/lib/puppet/face/resource_type.rb new file mode 100644 index 000000000..0cdbd719f --- /dev/null +++ b/lib/puppet/face/resource_type.rb @@ -0,0 +1,4 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:resource_type, '0.0.1') do +end diff --git a/lib/puppet/face/status.rb b/lib/puppet/face/status.rb new file mode 100644 index 000000000..7085e7cd7 --- /dev/null +++ b/lib/puppet/face/status.rb @@ -0,0 +1,4 @@ +require 'puppet/face/indirector' + +Puppet::Face::Indirector.define(:status, '0.0.1') do +end diff --git a/lib/puppet/faces/help/action.erb b/lib/puppet/faces/help/action.erb new file mode 100644 index 000000000..eaf131464 --- /dev/null +++ b/lib/puppet/faces/help/action.erb @@ -0,0 +1,3 @@ +Use: puppet <%= face.name %> [options] <%= action.name %> [options] + +Summary: <%= action.summary %> diff --git a/lib/puppet/faces/help/face.erb b/lib/puppet/faces/help/face.erb new file mode 100644 index 000000000..efe5fd809 --- /dev/null +++ b/lib/puppet/faces/help/face.erb @@ -0,0 +1,7 @@ +Use: puppet <%= face.name %> [options] [options] + +Available actions: +% face.actions.each do |actionname| +% action = face.get_action(actionname) + <%= action.name.to_s.ljust(16) %> <%= action.summary %> +% end diff --git a/lib/puppet/faces/help/global.erb b/lib/puppet/faces/help/global.erb new file mode 100644 index 000000000..e123367a2 --- /dev/null +++ b/lib/puppet/faces/help/global.erb @@ -0,0 +1,20 @@ +puppet [options] [options] + +Available subcommands, from Puppet Faces: +% Puppet::Faces.faces.sort.each do |name| +% face = Puppet::Faces[name, :current] + <%= face.name.to_s.ljust(16) %> <%= face.summary %> +% end + +% unless legacy_applications.empty? then # great victory when this is true! +Available applications, soon to be ported to Faces: +% legacy_applications.each do |appname| +% summary = horribly_extract_summary_from appname + <%= appname.to_s.ljust(16) %> <%= summary %> +% end +% end + +See 'puppet help ' for help on a specific subcommand action. +See 'puppet help ' for help on a specific subcommand. +See 'puppet man ' for the full man page. +Puppet v<%= Puppet::PUPPETVERSION %> diff --git a/lib/puppet/feature/ssh.rb b/lib/puppet/feature/ssh.rb new file mode 100644 index 000000000..82fe19882 --- /dev/null +++ b/lib/puppet/feature/ssh.rb @@ -0,0 +1,4 @@ +require 'puppet/util/feature' + +Puppet.features.rubygems? +Puppet.features.add(:ssh, :libs => %{net/ssh}) diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index 9effc5cdd..7267ac7f3 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -1,35 +1,51 @@ # Manage indirections to termini. They are organized in terms of indirections - # - e.g., configuration, node, file, certificate -- and each indirection has one # or more terminus types defined. The indirection is configured via the # +indirects+ method, which will be called by the class extending itself # with this module. module Puppet::Indirector # LAK:FIXME We need to figure out how to handle documentation for the # different indirection types. require 'puppet/indirector/indirection' require 'puppet/indirector/terminus' require 'puppet/indirector/envelope' require 'puppet/network/format_handler' + def self.configure_routes(application_routes) + application_routes.each do |indirection_name, termini| + indirection_name = indirection_name.to_sym + terminus_name = termini["terminus"] + cache_name = termini["cache"] + + Puppet::Indirector::Terminus.terminus_classes(indirection_name) + + indirection = Puppet::Indirector::Indirection.instance(indirection_name) + raise "Indirection #{indirection_name} does not exist" unless indirection + + indirection.terminus_class = terminus_name if terminus_name + indirection.cache_class = cache_name if cache_name + end + end + # Declare that the including class indirects its methods to # this terminus. The terminus name must be the name of a Puppet # default, not the value -- if it's the value, then it gets # evaluated at parse time, which is before the user has had a chance # to override it. def indirects(indirection, options = {}) raise(ArgumentError, "Already handling indirection for #{@indirection.name}; cannot also handle #{indirection}") if @indirection # populate this class with the various new methods extend ClassMethods include Puppet::Indirector::Envelope extend Puppet::Network::FormatHandler # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node) # & hook the instantiated Terminus into this class (Node: @indirection = terminus) @indirection = Puppet::Indirector::Indirection.new(self, indirection, options) end module ClassMethods attr_reader :indirection end end diff --git a/lib/puppet/indirector/certificate_request/ca.rb b/lib/puppet/indirector/certificate_request/ca.rb index f4c924fe1..5d76ee52a 100644 --- a/lib/puppet/indirector/certificate_request/ca.rb +++ b/lib/puppet/indirector/certificate_request/ca.rb @@ -1,14 +1,22 @@ require 'puppet/indirector/ssl_file' require 'puppet/ssl/certificate_request' class Puppet::SSL::CertificateRequest::Ca < Puppet::Indirector::SslFile desc "Manage the CA collection of certificate requests on disk." store_in :csrdir def save(request) + if host = Puppet::SSL::Host.indirection.find(request.key) + if Puppet[:allow_duplicate_certs] + Puppet.notice "#{request.key} already has a #{host.state} certificate; new certificate will overwrite it" + else + raise "#{request.key} already has a #{host.state} certificate; ignoring certificate request" + end + end + result = super Puppet.notice "#{request.key} has a waiting certificate request" result end end diff --git a/lib/puppet/indirector/couch.rb b/lib/puppet/indirector/couch.rb index fae934fd8..243d33dd4 100644 --- a/lib/puppet/indirector/couch.rb +++ b/lib/puppet/indirector/couch.rb @@ -1,76 +1,78 @@ -raise "Couch terminus not supported without couchrest gem" unless Puppet.features.couchdb? - -require 'couchrest' class Puppet::Indirector::Couch < Puppet::Indirector::Terminus # The CouchRest database instance. One database instance per Puppet runtime # should be sufficient. # def self.db; @db ||= CouchRest.database! Puppet[:couchdb_url] end def db; self.class.db end def find(request) attributes_of get(request) end + def initialize(*args) + raise "Couch terminus not supported without couchrest gem" unless Puppet.features.couchdb? + super + end + # Create or update the couchdb document with the request's data hash. # def save(request) raise ArgumentError, "PUT does not accept options" unless request.options.empty? update(request) || create(request) end private # RKH:TODO: Do not depend on error handling, check if the document exists # first. (Does couchrest support this?) # def get(request) db.get(id_for(request)) rescue RestClient::ResourceNotFound Puppet.debug "No couchdb document with id: #{id_for(request)}" return nil end def update(request) doc = get request return unless doc doc.merge!(hash_from(request)) doc.save true end def create(request) db.save_doc hash_from(request) end # The attributes hash that is serialized to CouchDB as JSON. It includes # metadata that is used to help aggregate data in couchdb. Add # model-specific attributes in subclasses. # def hash_from(request) { "_id" => id_for(request), "puppet_type" => document_type_for(request) } end # The couchdb response stripped of metadata, used to instantiate the model # instance that is returned by save. # def attributes_of(response) response && response.reject{|k,v| k =~ /^(_rev|puppet_)/ } end def document_type_for(request) request.indirection_name end # The id used to store the object in couchdb. Implemented in subclasses. # def id_for(request) raise NotImplementedError end end diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index e50dc68ae..0d3997221 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -1,115 +1,125 @@ require 'net/http' require 'uri' require 'puppet/network/http_pool' require 'puppet/network/http/api/v1' require 'puppet/network/http/compression' # Access objects via REST class Puppet::Indirector::REST < Puppet::Indirector::Terminus include Puppet::Network::HTTP::API::V1 include Puppet::Network::HTTP::Compression.module class << self attr_reader :server_setting, :port_setting end # Specify the setting that we should use to get the server name. def self.use_server_setting(setting) @server_setting = setting end def self.server Puppet.settings[server_setting || :server] end # Specify the setting that we should use to get the port. def self.use_port_setting(setting) @port_setting = setting end def self.port Puppet.settings[port_setting || :masterport].to_i end # Figure out the content type, turn that into a format, and use the format # to extract the body of the response. def deserialize(response, multiple = false) case response.code when "404" return nil when /^2/ raise "No content type in http response; cannot parse" unless response['content-type'] content_type = response['content-type'].gsub(/\s*;.*$/,'') # strip any appended charset body = uncompress_body(response) # Convert the response to a deserialized object. if multiple model.convert_from_multiple(content_type, body) else model.convert_from(content_type, body) end else # Raise the http error if we didn't get a 'success' of some kind. raise convert_to_http_error(response) end end def convert_to_http_error(response) message = "Error #{response.code} on SERVER: #{(response.body||'').empty? ? response.message : uncompress_body(response)}" Net::HTTPError.new(message, response) end # Provide appropriate headers. def headers add_accept_encoding({"Accept" => model.supported_formats.join(", ")}) end def network(request) Puppet::Network::HttpPool.http_instance(request.server || self.class.server, request.port || self.class.port) end def find(request) - return nil unless result = deserialize(network(request).get(indirection2uri(request), headers)) + uri, body = request_to_uri_and_body(request) + uri_with_query_string = "#{uri}?#{body}" + http_connection = network(request) + # WEBrick in Ruby 1.9.1 only supports up to 1024 character lines in an HTTP request + # http://redmine.ruby-lang.org/issues/show/3991 + response = if "GET #{uri_with_query_string} HTTP/1.1\r\n".length > 1024 + http_connection.post(uri, body, headers) + else + http_connection.get(uri_with_query_string, headers) + end + result = deserialize response result.name = request.key if result.respond_to?(:name=) result end def head(request) response = network(request).head(indirection2uri(request), headers) case response.code when "404" return false when /^2/ return true else # Raise the http error if we didn't get a 'success' of some kind. raise convert_to_http_error(response) end end def search(request) unless result = deserialize(network(request).get(indirection2uri(request), headers), true) return [] end result end def destroy(request) raise ArgumentError, "DELETE does not accept options" unless request.options.empty? deserialize network(request).delete(indirection2uri(request), headers) end def save(request) raise ArgumentError, "PUT does not accept options" unless request.options.empty? deserialize network(request).put(indirection2uri(request), request.instance.render, headers.merge({ "Content-Type" => request.instance.mime })) end private def environment Puppet::Node::Environment.new end end diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb new file mode 100644 index 000000000..6570ebe46 --- /dev/null +++ b/lib/puppet/interface.rb @@ -0,0 +1,120 @@ +require 'puppet' +require 'puppet/util/autoload' + +class Puppet::Interface + require 'puppet/interface/face_collection' + + require 'puppet/interface/action_manager' + include Puppet::Interface::ActionManager + extend Puppet::Interface::ActionManager + + require 'puppet/interface/option_manager' + include Puppet::Interface::OptionManager + extend Puppet::Interface::OptionManager + + include Puppet::Util + + class << self + # This is just so we can search for actions. We only use its + # list of directories to search. + # Can't we utilize an external autoloader, or simply use the $LOAD_PATH? -pvb + def autoloader + @autoloader ||= Puppet::Util::Autoload.new(:application, "puppet/face") + end + + def faces + Puppet::Interface::FaceCollection.faces + end + + def face?(name, version) + Puppet::Interface::FaceCollection.face?(name, version) + end + + def register(instance) + Puppet::Interface::FaceCollection.register(instance) + end + + def define(name, version, &block) + if face?(name, version) + face = Puppet::Interface::FaceCollection[name, version] + else + face = self.new(name, version) + Puppet::Interface::FaceCollection.register(face) + # REVISIT: Shouldn't this be delayed until *after* we evaluate the + # current block, not done before? --daniel 2011-04-07 + face.load_actions + end + + face.instance_eval(&block) if block_given? + + return face + end + + def [](name, version) + unless face = Puppet::Interface::FaceCollection[name, version] + if current = Puppet::Interface::FaceCollection[name, :current] + raise Puppet::Error, "Could not find version #{version} of #{current}" + else + raise Puppet::Error, "Could not find Puppet Face #{name.inspect}" + end + end + face + end + end + + attr_accessor :default_format + + def set_default_format(format) + self.default_format = format.to_sym + end + + attr_accessor :summary + def summary(value = nil) + @summary = value unless value.nil? + @summary + end + + attr_reader :name, :version + + def initialize(name, version, &block) + unless Puppet::Interface::FaceCollection.validate_version(version) + raise ArgumentError, "Cannot create face #{name.inspect} with invalid version number '#{version}'!" + end + + @name = Puppet::Interface::FaceCollection.underscorize(name) + @version = version + @default_format = :pson + + instance_eval(&block) if block_given? + end + + # Try to find actions defined in other files. + def load_actions + path = "puppet/face/#{name}" + + loaded = [] + [path, "#{name}@#{version}/#{path}"].each do |path| + Puppet::Interface.autoloader.search_directories.each do |dir| + fdir = ::File.join(dir, path) + next unless FileTest.directory?(fdir) + + Dir.chdir(fdir) do + Dir.glob("*.rb").each do |file| + aname = file.sub(/\.rb/, '') + if loaded.include?(aname) + Puppet.debug "Not loading duplicate action '#{aname}' for '#{name}' from '#{fdir}/#{file}'" + next + end + loaded << aname + Puppet.debug "Loading action '#{aname}' for '#{name}' from '#{fdir}/#{file}'" + require "#{Dir.pwd}/#{aname}" + end + end + end + end + end + + def to_s + "Puppet::Face[#{name.inspect}, #{version.inspect}]" + end +end diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb new file mode 100644 index 000000000..db338e39e --- /dev/null +++ b/lib/puppet/interface/action.rb @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +require 'puppet/interface' +require 'puppet/interface/option' + +class Puppet::Interface::Action + def initialize(face, name, attrs = {}) + raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/ + @face = face + @name = name.to_sym + @options = {} + attrs.each do |k, v| send("#{k}=", v) end + end + + # This is not nice, but it is the easiest way to make us behave like the + # Ruby Method object rather than UnboundMethod. Duplication is vaguely + # annoying, but at least we are a shallow clone. --daniel 2011-04-12 + def __dup_and_rebind_to(to) + bound_version = self.dup + bound_version.instance_variable_set(:@face, to) + return bound_version + end + + attr_reader :name + def to_s() "#{@face}##{@name}" end + + attr_accessor :summary + + # Initially, this was defined to allow the @action.invoke pattern, which is + # a very natural way to invoke behaviour given our introspection + # capabilities. Heck, our initial plan was to have the faces delegate to + # the action object for invocation and all. + # + # It turns out that we have a binding problem to solve: @face was bound to + # the parent class, not the subclass instance, and we don't pass the + # appropriate context or change the binding enough to make this work. + # + # We could hack around it, by either mandating that you pass the context in + # to invoke, or try to get the binding right, but that has probably got + # subtleties that we don't instantly think of – especially around threads. + # + # So, we are pulling this method for now, and will return it to life when we + # have the time to resolve the problem. For now, you should replace... + # + # @action = @face.get_action(name) + # @action.invoke(arg1, arg2, arg3) + # + # ...with... + # + # @action = @face.get_action(name) + # @face.send(@action.name, arg1, arg2, arg3) + # + # I understand that is somewhat cumbersome, but it functions as desired. + # --daniel 2011-03-31 + # + # PS: This code is left present, but commented, to support this chunk of + # documentation, for the benefit of the reader. + # + # def invoke(*args, &block) + # @face.send(name, *args, &block) + # end + + def when_invoked=(block) + # We need to build an instance method as a wrapper, using normal code, to + # be able to expose argument defaulting between the caller and definer in + # the Ruby API. An extra method is, sadly, required for Ruby 1.8 to work. + # + # In future this also gives us a place to hook in additional behaviour + # such as calling out to the action instance to validate and coerce + # parameters, which avoids any exciting context switching and all. + # + # Hopefully we can improve this when we finally shuffle off the last of + # Ruby 1.8 support, but that looks to be a few "enterprise" release eras + # away, so we are pretty stuck with this for now. + # + # Patches to make this work more nicely with Ruby 1.9 using runtime + # version checking and all are welcome, but they can't actually help if + # the results are not totally hidden away in here. + # + # Incidentally, we though about vendoring evil-ruby and actually adjusting + # the internal C structure implementation details under the hood to make + # this stuff work, because it would have been cleaner. Which gives you an + # idea how motivated we were to make this cleaner. Sorry. --daniel 2011-03-31 + + internal_name = "#{@name} implementation, required on Ruby 1.8".to_sym + file = __FILE__ + "+eval" + line = __LINE__ + 1 + wrapper = "def #{@name}(*args, &block) + args << {} unless args.last.is_a? Hash + args << block if block_given? + self.__send__(#{internal_name.inspect}, *args) + end" + + if @face.is_a?(Class) + @face.class_eval do eval wrapper, nil, file, line end + @face.define_method(internal_name, &block) + else + @face.instance_eval do eval wrapper, nil, file, line end + @face.meta_def(internal_name, &block) + end + end + + def add_option(option) + option.aliases.each do |name| + if conflict = get_option(name) then + raise ArgumentError, "Option #{option} conflicts with existing option #{conflict}" + elsif conflict = @face.get_option(name) then + raise ArgumentError, "Option #{option} conflicts with existing option #{conflict} on #{@face}" + end + end + + option.aliases.each do |name| + @options[name] = option + end + + option + end + + def option?(name) + @options.include? name.to_sym + end + + def options + (@options.keys + @face.options).sort + end + + def get_option(name) + @options[name.to_sym] || @face.get_option(name) + end +end diff --git a/lib/puppet/interface/action_builder.rb b/lib/puppet/interface/action_builder.rb new file mode 100644 index 000000000..34bb3fa44 --- /dev/null +++ b/lib/puppet/interface/action_builder.rb @@ -0,0 +1,35 @@ +require 'puppet/interface' +require 'puppet/interface/action' + +class Puppet::Interface::ActionBuilder + attr_reader :action + + def self.build(face, name, &block) + raise "Action #{name.inspect} must specify a block" unless block + new(face, name, &block).action + end + + private + def initialize(face, name, &block) + @face = face + @action = Puppet::Interface::Action.new(face, name) + instance_eval(&block) + end + + # Ideally the method we're defining here would be added to the action, and a + # method on the face would defer to it, but we can't get scope correct, so + # we stick with this. --daniel 2011-03-24 + def when_invoked(&block) + raise "when_invoked on an ActionBuilder with no corresponding Action" unless @action + @action.when_invoked = block + end + + def option(*declaration, &block) + option = Puppet::Interface::OptionBuilder.build(@action, *declaration, &block) + @action.add_option(option) + end + + def summary(text) + @action.summary = text + end +end diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb new file mode 100644 index 000000000..d75697afa --- /dev/null +++ b/lib/puppet/interface/action_manager.rb @@ -0,0 +1,56 @@ +require 'puppet/interface/action_builder' + +module Puppet::Interface::ActionManager + # Declare that this app can take a specific action, and provide + # the code to do so. + def action(name, &block) + @actions ||= {} + raise "Action #{name} already defined for #{self}" if action?(name) + action = Puppet::Interface::ActionBuilder.build(self, name, &block) + @actions[action.name] = action + end + + # This is the short-form of an action definition; it doesn't use the + # builder, just creates the action directly from the block. + def script(name, &block) + @actions ||= {} + raise "Action #{name} already defined for #{self}" if action?(name) + @actions[name] = Puppet::Interface::Action.new(self, name, :when_invoked => block) + end + + def actions + @actions ||= {} + result = @actions.keys + + if self.is_a?(Class) and superclass.respond_to?(:actions) + result += superclass.actions + elsif self.class.respond_to?(:actions) + result += self.class.actions + end + result.sort + end + + def get_action(name) + @actions ||= {} + result = @actions[name.to_sym] + if result.nil? + if self.is_a?(Class) and superclass.respond_to?(:get_action) + found = superclass.get_action(name) + elsif self.class.respond_to?(:get_action) + found = self.class.get_action(name) + end + + if found then + # This is not the nicest way to make action equivalent to the Ruby + # Method object, rather than UnboundMethod, but it will do for now, + # and we only have to make this change in *one* place. --daniel 2011-04-12 + result = @actions[name.to_sym] = found.__dup_and_rebind_to(self) + end + end + return result + end + + def action?(name) + actions.include?(name.to_sym) + end +end diff --git a/lib/puppet/interface/face_collection.rb b/lib/puppet/interface/face_collection.rb new file mode 100644 index 000000000..591471d4b --- /dev/null +++ b/lib/puppet/interface/face_collection.rb @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +require 'puppet/interface' + +module Puppet::Interface::FaceCollection + SEMVER_VERSION = /^(\d+)\.(\d+)\.(\d+)([A-Za-z][0-9A-Za-z-]*|)$/ + + @faces = Hash.new { |hash, key| hash[key] = {} } + + def self.faces + unless @loaded + @loaded = true + $LOAD_PATH.each do |dir| + next unless FileTest.directory?(dir) + Dir.chdir(dir) do + Dir.glob("puppet/face/*.rb").collect { |f| f.sub(/\.rb/, '') }.each do |file| + iname = file.sub(/\.rb/, '') + begin + require iname + rescue Exception => detail + puts detail.backtrace if Puppet[:trace] + raise "Could not load #{iname} from #{dir}/#{file}: #{detail}" + end + end + end + end + end + return @faces.keys + end + + def self.validate_version(version) + !!(SEMVER_VERSION =~ version.to_s) + end + + def self.cmp_semver(a, b) + a, b = [a, b].map do |x| + parts = SEMVER_VERSION.match(x).to_a[1..4] + parts[0..2] = parts[0..2].map { |e| e.to_i } + parts + end + + cmp = a[0..2] <=> b[0..2] + if cmp == 0 + cmp = a[3] <=> b[3] + cmp = +1 if a[3].empty? && !b[3].empty? + cmp = -1 if b[3].empty? && !a[3].empty? + end + cmp + end + + def self.[](name, version) + @faces[underscorize(name)][version] if face?(name, version) + end + + def self.face?(name, version) + name = underscorize(name) + + # Note: be careful not to accidentally create the top level key, either, + # because it will result in confusion when people try to enumerate the + # list of valid faces later. --daniel 2011-04-11 + return true if @faces.has_key?(name) and @faces[name].has_key?(version) + + # We always load the current version file; the common case is that we have + # the expected version and any compatibility versions in the same file, + # the default. Which means that this is almost always the case. + # + # We use require to avoid executing the code multiple times, like any + # other Ruby library that we might want to use. --daniel 2011-04-06 + begin + require "puppet/face/#{name}" + + # If we wanted :current, we need to index to find that; direct version + # requests just work™ as they go. --daniel 2011-04-06 + if version == :current then + # We need to find current out of this. This is the largest version + # number that doesn't have a dedicated on-disk file present; those + # represent "experimental" versions of faces, which we don't fully + # support yet. + # + # We walk the versions from highest to lowest and take the first version + # that is not defined in an explicitly versioned file on disk as the + # current version. + # + # This constrains us to only ship experimental versions with *one* + # version in the file, not multiple, but given you can't reliably load + # them except by side-effect when you ignore that rule this seems safe + # enough... + # + # Given those constraints, and that we are not going to ship a versioned + # interface that is not :current in this release, we are going to leave + # these thoughts in place, and just punt on the actual versioning. + # + # When we upgrade the core to support multiple versions we can solve the + # problems then; as lazy as possible. + # + # We do support multiple versions in the same file, though, so we sort + # versions here and return the last item in that set. + # + # --daniel 2011-04-06 + latest_ver = @faces[name].keys.sort {|a, b| cmp_semver(a, b) }.last + @faces[name][:current] = @faces[name][latest_ver] + end + rescue LoadError => e + raise unless e.message =~ %r{-- puppet/face/#{name}$} + # ...guess we didn't find the file; return a much better problem. + end + + # Now, either we have the version in our set of faces, or we didn't find + # the version they were looking for. In the future we will support + # loading versioned stuff from some look-aside part of the Ruby load path, + # but we don't need that right now. + # + # So, this comment is a place-holder for that. --daniel 2011-04-06 + # + # Note: be careful not to accidentally create the top level key, either, + # because it will result in confusion when people try to enumerate the + # list of valid faces later. --daniel 2011-04-11 + return !! (@faces.has_key?(name) and @faces[name].has_key?(version)) + end + + def self.register(face) + @faces[underscorize(face.name)][face.version] = face + end + + def self.underscorize(name) + unless name.to_s =~ /^[-_a-z]+$/i then + raise ArgumentError, "#{name.inspect} (#{name.class}) is not a valid face name" + end + + name.to_s.downcase.split(/[-_]/).join('_').to_sym + end +end diff --git a/lib/puppet/interface/option.rb b/lib/puppet/interface/option.rb new file mode 100644 index 000000000..ccc2fbba7 --- /dev/null +++ b/lib/puppet/interface/option.rb @@ -0,0 +1,82 @@ +require 'puppet/interface' + +class Puppet::Interface::Option + attr_reader :parent + attr_reader :name + attr_reader :aliases + attr_reader :optparse + attr_accessor :desc + + def takes_argument? + !!@argument + end + def optional_argument? + !!@optional_argument + end + + def initialize(parent, *declaration, &block) + @parent = parent + @optparse = [] + + # Collect and sort the arguments in the declaration. + dups = {} + declaration.each do |item| + if item.is_a? String and item.to_s =~ /^-/ then + unless item =~ /^-[a-z]\b/ or item =~ /^--[^-]/ then + raise ArgumentError, "#{item.inspect}: long options need two dashes (--)" + end + @optparse << item + + # Duplicate checking... + name = optparse_to_name(item) + if dup = dups[name] then + raise ArgumentError, "#{item.inspect}: duplicates existing alias #{dup.inspect} in #{@parent}" + else + dups[name] = item + end + else + raise ArgumentError, "#{item.inspect} is not valid for an option argument" + end + end + + if @optparse.empty? then + raise ArgumentError, "No option declarations found while building" + end + + # Now, infer the name from the options; we prefer the first long option as + # the name, rather than just the first option. + @name = optparse_to_name(@optparse.find do |a| a =~ /^--/ end || @optparse.first) + @aliases = @optparse.map { |o| optparse_to_name(o) } + + # Do we take an argument? If so, are we consistent about it, because + # incoherence here makes our life super-difficult, and we can more easily + # relax this rule later if we find a valid use case for it. --daniel 2011-03-30 + @argument = @optparse.any? { |o| o =~ /[ =]/ } + if @argument and not @optparse.all? { |o| o =~ /[ =]/ } then + raise ArgumentError, "Option #{@name} is inconsistent about taking an argument" + end + + # Is our argument optional? The rules about consistency apply here, also, + # just like they do to taking arguments at all. --daniel 2011-03-30 + @optional_argument = @optparse.any? { |o| o.include? "[" } + if @optional_argument and not @optparse.all? { |o| o.include? "[" } then + raise ArgumentError, "Option #{@name} is inconsistent about the argument being optional" + end + end + + # to_s and optparse_to_name are roughly mirrored, because they are used to + # transform options to name symbols, and vice-versa. This isn't a full + # bidirectional transformation though. --daniel 2011-04-07 + def to_s + @name.to_s.tr('_', '-') + end + + def optparse_to_name(declaration) + unless found = declaration.match(/^-+(?:\[no-\])?([^ =]+)/) then + raise ArgumentError, "Can't find a name in the declaration #{declaration.inspect}" + end + name = found.captures.first.tr('-', '_') + raise "#{name.inspect} is an invalid option name" unless name.to_s =~ /^[a-z]\w*$/ + name.to_sym + end +end diff --git a/lib/puppet/interface/option_builder.rb b/lib/puppet/interface/option_builder.rb new file mode 100644 index 000000000..2240b3e4a --- /dev/null +++ b/lib/puppet/interface/option_builder.rb @@ -0,0 +1,25 @@ +require 'puppet/interface/option' + +class Puppet::Interface::OptionBuilder + attr_reader :option + + def self.build(face, *declaration, &block) + new(face, *declaration, &block).option + end + + private + def initialize(face, *declaration, &block) + @face = face + @option = Puppet::Interface::Option.new(face, *declaration) + block and instance_eval(&block) + @option + end + + # Metaprogram the simple DSL from the option class. + Puppet::Interface::Option.instance_methods.grep(/=$/).each do |setter| + next if setter =~ /^=/ # special case, darn it... + + dsl = setter.to_s.sub(/=$/, '') + define_method(dsl) do |value| @option.send(setter, value) end + end +end diff --git a/lib/puppet/interface/option_manager.rb b/lib/puppet/interface/option_manager.rb new file mode 100644 index 000000000..56df9760f --- /dev/null +++ b/lib/puppet/interface/option_manager.rb @@ -0,0 +1,56 @@ +require 'puppet/interface/option_builder' + +module Puppet::Interface::OptionManager + # Declare that this app can take a specific option, and provide + # the code to do so. + def option(*declaration, &block) + add_option Puppet::Interface::OptionBuilder.build(self, *declaration, &block) + end + + def add_option(option) + option.aliases.each do |name| + if conflict = get_option(name) then + raise ArgumentError, "Option #{option} conflicts with existing option #{conflict}" + end + + actions.each do |action| + action = get_action(action) + if conflict = action.get_option(name) then + raise ArgumentError, "Option #{option} conflicts with existing option #{conflict} on #{action}" + end + end + end + + option.aliases.each { |name| @options[name] = option } + option + end + + def options + @options ||= {} + result = @options.keys + + if self.is_a?(Class) and superclass.respond_to?(:options) + result += superclass.options + elsif self.class.respond_to?(:options) + result += self.class.options + end + result.sort + end + + def get_option(name) + @options ||= {} + result = @options[name.to_sym] + unless result then + if self.is_a?(Class) and superclass.respond_to?(:get_option) + result = superclass.get_option(name) + elsif self.class.respond_to?(:get_option) + result = self.class.get_option(name) + end + end + return result + end + + def option?(name) + options.include? name.to_sym + end +end diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 5fe143979..61307f01e 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -1,73 +1,81 @@ require 'puppet/network/http/api' module Puppet::Network::HTTP::API::V1 # How we map http methods and the indirection name in the URI # to an indirection method. METHOD_MAP = { "GET" => { :plural => :search, :singular => :find }, + "POST" => { + :singular => :find, + }, "PUT" => { :singular => :save }, "DELETE" => { :singular => :destroy }, "HEAD" => { :singular => :head } } def uri2indirection(http_method, uri, params) environment, indirection, key = uri.split("/", 4)[1..-1] # the first field is always nil because of the leading slash raise ArgumentError, "The environment must be purely alphanumeric, not '#{environment}'" unless environment =~ /^\w+$/ raise ArgumentError, "The indirection name must be purely alphanumeric, not '#{indirection}'" unless indirection =~ /^\w+$/ method = indirection_method(http_method, indirection) params[:environment] = environment raise ArgumentError, "No request key specified in #{uri}" if key == "" or key.nil? key = URI.unescape(key) [indirection, method, key, params] end def indirection2uri(request) indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s "/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}#{request.query_string}" end + def request_to_uri_and_body(request) + indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s + ["/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}", request.query_string.sub(/^\?/,'')] + end + def indirection_method(http_method, indirection) raise ArgumentError, "No support for http method #{http_method}" unless METHOD_MAP[http_method] unless method = METHOD_MAP[http_method][plurality(indirection)] raise ArgumentError, "No support for plural #{http_method} operations" end method end def pluralize(indirection) return(indirection == "status" ? "statuses" : indirection + "s") end def plurality(indirection) # NOTE This specific hook for facts is ridiculous, but it's a *many*-line # fix to not need this, and our goal is to move away from the complication # that leads to the fix being too long. return :singular if indirection == "facts" return :singular if indirection == "status" return :singular if indirection == "certificate_status" return :plural if indirection == "inventory" result = (indirection =~ /s$|_search$/) ? :plural : :singular indirection.sub!(/s$|_search$|es$/, '') result end end diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb index 9bd949a08..ce0401ad2 100644 --- a/lib/puppet/network/http_server/mongrel.rb +++ b/lib/puppet/network/http_server/mongrel.rb @@ -1,150 +1,130 @@ #!/usr/bin/env ruby # File: 06-11-14-mongrel_xmlrpc.rb # Author: Manuel Holtgrewe # # Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies # -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - # This file is based heavily on a file retrieved from # http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/ require 'rubygems' require 'mongrel' require 'xmlrpc/server' require 'puppet/network/xmlrpc/server' require 'puppet/network/http_server' require 'puppet/network/client_request' require 'puppet/network/handler' require 'resolv' # This handler can be hooked into Mongrel to accept HTTP requests. After # checking whether the request itself is sane, the handler forwards it # to an internal instance of XMLRPC::BasicServer to process it. # # You can access the server by calling the Handler's "xmlrpc_server" # attribute accessor method and add XMLRPC handlers there. For example: # #
 # handler = XmlRpcHandler.new
 # handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
 # 
module Puppet::Network class HTTPServer::Mongrel < ::Mongrel::HttpHandler attr_reader :xmlrpc_server def initialize(handlers) if Puppet[:debug] $mongrel_debug_client = true Puppet.debug 'Mongrel client debugging enabled. [$mongrel_debug_client = true].' end # Create a new instance of BasicServer. We are supposed to subclass it # but that does not make sense since we would not introduce any new # behaviour and we have to subclass Mongrel::HttpHandler so our handler # works for Mongrel. @xmlrpc_server = Puppet::Network::XMLRPCServer.new handlers.each do |name| unless handler = Puppet::Network::Handler.handler(name) raise ArgumentError, "Invalid handler #{name}" end @xmlrpc_server.add_handler(handler.interface, handler.new({})) end end # This method produces the same results as XMLRPC::CGIServer.serve # from Ruby's stdlib XMLRPC implementation. def process(request, response) # Make sure this has been a POST as required for XMLRPC. request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET if request_method != "POST" response.start(405) { |head, out| out.write("Method Not Allowed") } return end # Make sure the user has sent text/xml data. request_mime = request.params["CONTENT_TYPE"] || "text/plain" if parse_content_type(request_mime).first != "text/xml" response.start(400) { |head, out| out.write("Bad Request") } return end # Make sure there is data in the body at all. length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i if length <= 0 response.start(411) { |head, out| out.write("Length Required") } return end # Check the body to be valid. if request.body.nil? or request.body.size != length response.start(400) { |head, out| out.write("Bad Request") } return end info = client_info(request) # All checks above passed through response.start(200) do |head, out| head["Content-Type"] = "text/xml; charset=utf-8" begin out.write(@xmlrpc_server.process(request.body, info)) rescue => detail puts detail.backtrace raise end end end private def client_info(request) params = request.params ip = params["HTTP_X_FORWARDED_FOR"] ? params["HTTP_X_FORWARDED_FOR"].split(',').last.strip : params["REMOTE_ADDR"] # JJM #906 The following dn.match regular expression is forgiving # enough to match the two Distinguished Name string contents # coming from Apache, Pound or other reverse SSL proxies. if dn = params[Puppet[:ssl_client_header]] and dn_matchdata = dn.match(/^.*?CN\s*=\s*(.*)/) client = dn_matchdata[1].to_str valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS') else begin client = Resolv.getname(ip) rescue => detail Puppet.err "Could not resolve #{ip}: #{detail}" client = "unknown" end valid = false end info = Puppet::Network::ClientRequest.new(client, ip, valid) info end # Taken from XMLRPC::ParseContentType def parse_content_type(str) a, *b = str.split(";") return a.strip, *b end end end diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb index 77617e992..c8ebc9483 100644 --- a/lib/puppet/parser/ast/leaf.rb +++ b/lib/puppet/parser/ast/leaf.rb @@ -1,221 +1,221 @@ class Puppet::Parser::AST # The base class for all of the leaves of the parse trees. These # basically just have types and values. Both of these parameters # are simple values, not AST objects. class Leaf < AST attr_accessor :value, :type # Return our value. def evaluate(scope) @value end def match(value) @value == value end def to_s @value.to_s unless @value.nil? end end # The boolean class. True or false. Converts the string it receives # to a Ruby boolean. class Boolean < AST::Leaf # Use the parent method, but then convert to a real boolean. def initialize(hash) super unless @value == true or @value == false raise Puppet::DevError, "'#{@value}' is not a boolean" end @value end def to_s @value ? "true" : "false" end end # The base string class. class String < AST::Leaf def evaluate(scope) @value.dup end def to_s "\"#{@value}\"" end end # An uninterpreted string. class FlatString < AST::Leaf def evaluate(scope) @value end def to_s "\"#{@value}\"" end end class Concat < AST::Leaf def evaluate(scope) @value.collect { |x| x.evaluate(scope) }.collect{ |x| x == :undef ? '' : x }.join end def to_s "#{@value.map { |s| s.to_s.gsub(/^"(.*)"$/, '\1') }.join}" end end # The 'default' option on case statements and selectors. class Default < AST::Leaf; end # Capitalized words; used mostly for type-defaults, but also # get returned by the lexer any other time an unquoted capitalized # word is found. class Type < AST::Leaf; end # Lower-case words. class Name < AST::Leaf; end # double-colon separated class names class ClassName < AST::Leaf; end # undef values; equiv to nil class Undef < AST::Leaf; end # Host names, either fully qualified or just the short name, or even a regex class HostName < AST::Leaf def initialize(hash) super # Note that this is an AST::Regex, not a Regexp @value = @value.to_s.downcase unless @value.is_a?(Regex) if @value =~ /[^-\w.]/ raise Puppet::DevError, "'#{@value}' is not a valid hostname" end end # implementing eql? and hash so that when an HostName is stored # in a hash it has the same hashing properties as the underlying value def eql?(value) value = value.value if value.is_a?(HostName) @value.eql?(value) end def hash @value.hash end def to_s @value.to_s end end # A simple variable. This object is only used during interpolation; # the VarDef class is used for assignment. class Variable < Name # Looks up the value of the object in the scope tree (does # not include syntactical constructs, like '$' and '{}'). def evaluate(scope) parsewrap do - if (var = scope.lookupvar(@value, false)) == :undefined + if (var = scope.lookupvar(@value, :file => file, :line => line)) == :undefined var = :undef end var end end def to_s "\$#{value}" end end class HashOrArrayAccess < AST::Leaf attr_accessor :variable, :key def evaluate_container(scope) container = variable.respond_to?(:evaluate) ? variable.safeevaluate(scope) : variable - (container.is_a?(Hash) or container.is_a?(Array)) ? container : scope.lookupvar(container) + (container.is_a?(Hash) or container.is_a?(Array)) ? container : scope.lookupvar(container, :file => file, :line => line) end def evaluate_key(scope) key.respond_to?(:evaluate) ? key.safeevaluate(scope) : key end def array_index_or_key(object, key) if object.is_a?(Array) raise Puppet::ParserError, "#{key} is not an integer, but is used as an index of an array" unless key = Puppet::Parser::Scope.number?(key) end key end def evaluate(scope) object = evaluate_container(scope) accesskey = evaluate_key(scope) raise Puppet::ParseError, "#{variable} is not an hash or array when accessing it with #{accesskey}" unless object.is_a?(Hash) or object.is_a?(Array) object[array_index_or_key(object, accesskey)] end # Assign value to this hashkey or array index def assign(scope, value) object = evaluate_container(scope) accesskey = evaluate_key(scope) if object.is_a?(Hash) and object.include?(accesskey) raise Puppet::ParseError, "Assigning to the hash '#{variable}' with an existing key '#{accesskey}' is forbidden" end # assign to hash or array object[array_index_or_key(object, accesskey)] = value end def to_s "\$#{variable.to_s}[#{key.to_s}]" end end class Regex < AST::Leaf def initialize(hash) super @value = Regexp.new(@value) unless @value.is_a?(Regexp) end # we're returning self here to wrap the regexp and to be used in places # where a string would have been used, without modifying any client code. # For instance, in many places we have the following code snippet: # val = @val.safeevaluate(@scope) # if val.match(otherval) # ... # end # this way, we don't have to modify this test specifically for handling # regexes. def evaluate(scope) self end def evaluate_match(value, scope, options = {}) value = value.is_a?(String) ? value : value.to_s if matched = @value.match(value) scope.ephemeral_from(matched, options[:file], options[:line]) end matched end def match(value) @value.match(value) end def to_s "/#{@value.source}/" end end end diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb index 6de1860c8..b766311dd 100644 --- a/lib/puppet/parser/ast/vardef.rb +++ b/lib/puppet/parser/ast/vardef.rb @@ -1,33 +1,33 @@ require 'puppet/parser/ast/branch' class Puppet::Parser::AST # Define a variable. Stores the value in the current scope. class VarDef < AST::Branch associates_doc attr_accessor :name, :value, :append @settor = true # Look up our name and value, and store them appropriately. The # lexer strips off the syntax stuff like '$'. def evaluate(scope) value = @value.safeevaluate(scope) if name.is_a?(HashOrArrayAccess) name.assign(scope, value) else name = @name.safeevaluate(scope) parsewrap do - scope.setvar(name,value, :file => @file, :line => @line, :append => @append) + scope.setvar(name,value, :file => file, :line => line, :append => @append) end end end def each [@name,@value].each { |child| yield child } end end end diff --git a/lib/puppet/parser/functions/extlookup.rb b/lib/puppet/parser/functions/extlookup.rb index bc55410b9..5fbf26cec 100644 --- a/lib/puppet/parser/functions/extlookup.rb +++ b/lib/puppet/parser/functions/extlookup.rb @@ -1,157 +1,152 @@ require 'csv' module Puppet::Parser::Functions newfunction(:extlookup, :type => :rvalue, :doc => "This is a parser function to read data from external files, this version uses CSV files but the concept can easily be adjust for databases, yaml or any other queryable data source. The object of this is to make it obvious when it's being used, rather than magically loading data in when an module is loaded I prefer to look at the code and see statements like: $snmp_contact = extlookup(\"snmp_contact\") The above snippet will load the snmp_contact value from CSV files, this in its own is useful but a common construct in puppet manifests is something like this: case $domain { \"myclient.com\": { $snmp_contact = \"John Doe \" } default: { $snmp_contact = \"My Support \" } } Over time there will be a lot of this kind of thing spread all over your manifests and adding an additional client involves grepping through manifests to find all the places where you have constructs like this. This is a data problem and shouldn't be handled in code, a using this function you can do just that. First you configure it in site.pp: $extlookup_datadir = \"/etc/puppet/manifests/extdata\" $extlookup_precedence = [\"%{fqdn}\", \"domain_%{domain}\", \"common\"] The array tells the code how to resolve values, first it will try to find it in web1.myclient.com.csv then in domain_myclient.com.csv and finally in common.csv Now create the following data files in /etc/puppet/manifests/extdata: domain_myclient.com.csv: snmp_contact,John Doe root_contact,support@%{domain} client_trusted_ips,192.168.1.130,192.168.10.0/24 common.csv: snmp_contact,My Support root_contact,support@my.com Now you can replace the case statement with the simple single line to achieve the exact same outcome: $snmp_contact = extlookup(\"snmp_contact\") The above code shows some other features, you can use any fact or variable that is in scope by simply using %{varname} in your data files, you can return arrays by just having multiple values in the csv after the initial variable name. In the event that a variable is nowhere to be found a critical error will be raised that will prevent your manifest from compiling, this is to avoid accidentally putting in empty values etc. You can however specify a default value: $ntp_servers = extlookup(\"ntp_servers\", \"1.${country}.pool.ntp.org\") In this case it will default to \"1.${country}.pool.ntp.org\" if nothing is defined in any data file. You can also specify an additional data file to search first before any others at use time, for example: $version = extlookup(\"rsyslog_version\", \"present\", \"packages\") package{\"rsyslog\": ensure => $version } This will look for a version configured in packages.csv and then in the rest as configured by $extlookup_precedence if it's not found anywhere it will default to `present`, this kind of use case makes puppet a lot nicer for managing large amounts of packages since you do not need to edit a load of manifests to do simple things like adjust a desired version number. Precedence values can have variables embedded in them in the form %{fqdn}, you could for example do: $extlookup_precedence = [\"hosts/%{fqdn}\", \"common\"] This will result in /path/to/extdata/hosts/your.box.com.csv being searched. This is for back compatibility to interpolate variables with %. % interpolation is a workaround for a problem that has been fixed: Puppet variable interpolation at top scope used to only happen on each run.") do |args| key = args[0] default = args[1] datafile = args[2] raise Puppet::ParseError, ("extlookup(): wrong number of arguments (#{args.length}; must be <= 3)") if args.length > 3 - extlookup_datadir = lookupvar('extlookup_datadir') - extlookup_precedence = Array.new + extlookup_datadir = undef_as('',lookupvar('::extlookup_datadir')) - extlookup_precedence = lookupvar('extlookup_precedence').collect do |var| - var.gsub(/%\{(.+?)\}/) do |capture| - lookupvar($1) - end - end + extlookup_precedence = undef_as([],lookupvar('::extlookup_precedence')).collect { |var| var.gsub(/%\{(.+?)\}/) { lookupvar("::#{$1}") } } datafiles = Array.new # if we got a custom data file, put it first in the array of search files if datafile != "" datafiles << extlookup_datadir + "/#{datafile}.csv" if File.exists?(extlookup_datadir + "/#{datafile}.csv") end extlookup_precedence.each do |d| datafiles << extlookup_datadir + "/#{d}.csv" end desired = nil datafiles.each do |file| if desired.nil? if File.exists?(file) result = CSV.read(file).find_all do |r| r[0] == key end # return just the single result if theres just one, # else take all the fields in the csv and build an array if result.length > 0 if result[0].length == 2 val = result[0][1].to_s # parse %{}'s in the CSV into local variables using lookupvar() while val =~ /%\{(.+?)\}/ val.gsub!(/%\{#{$1}\}/, lookupvar($1)) end desired = val elsif result[0].length > 1 length = result[0].length cells = result[0][1,length] # Individual cells in a CSV result are a weird data type and throws # puppets yaml parsing, so just map it all to plain old strings desired = cells.map do |c| # parse %{}'s in the CSV into local variables using lookupvar() while c =~ /%\{(.+?)\}/ c.gsub!(/%\{#{$1}\}/, lookupvar($1)) end c.to_s end end end end end end desired || default or raise Puppet::ParseError, "No match found for '#{key}' in any data file during extlookup()" end end diff --git a/lib/puppet/parser/functions/fqdn_rand.rb b/lib/puppet/parser/functions/fqdn_rand.rb index 91157a148..93ab98bcd 100644 --- a/lib/puppet/parser/functions/fqdn_rand.rb +++ b/lib/puppet/parser/functions/fqdn_rand.rb @@ -1,12 +1,12 @@ Puppet::Parser::Functions::newfunction(:fqdn_rand, :type => :rvalue, :doc => "Generates random numbers based on the node's fqdn. Generated random values will be a range from 0 up to and excluding n, where n is the first parameter. The second argument specifies a number to add to the seed and is optional, for example: $random_number = fqdn_rand(30) $random_number_seed = fqdn_rand(30,30)") do |args| require 'digest/md5' max = args.shift - srand(Digest::MD5.hexdigest([lookupvar('fqdn'),args].join(':')).hex) + srand(Digest::MD5.hexdigest([lookupvar('::fqdn'),args].join(':')).hex) rand(max).to_s end diff --git a/lib/puppet/parser/functions/sha1.rb b/lib/puppet/parser/functions/sha1.rb index 10cc55cfe..1e7d5abe4 100644 --- a/lib/puppet/parser/functions/sha1.rb +++ b/lib/puppet/parser/functions/sha1.rb @@ -1,5 +1,5 @@ Puppet::Parser::Functions::newfunction(:sha1, :type => :rvalue, :doc => "Returns a SHA1 hash value from a provided string.") do |args| - require 'sha1' + require 'digest/sha1' Digest::SHA1.hexdigest(args[0]) end diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index 8339c51b7..d2bd06e94 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -1,796 +1,796 @@ # vim: syntax=ruby # the parser class Puppet::Parser::Parser token STRING DQPRE DQMID DQPOST token LBRACK RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE token FALSE EQUALS APPENDS 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 PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB token IN prechigh right NOT nonassoc UMINUS left IN MATCH NOMATCH left TIMES DIV left MINUS PLUS left LSHIFT RSHIFT left NOTEQUAL ISEQUAL left GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL left AND left OR preclow rule program: statements_and_declarations | nil statements_and_declarations: statement_or_declaration { result = ast AST::ASTArray, :children => (val[0] ? [val[0]] : []) } | statements_and_declarations statement_or_declaration { if val[1] val[0].push(val[1]) end result = val[0] } # statements is like statements_and_declarations, but it doesn't allow # nested definitions, classes, or nodes. statements: statements_and_declarations { val[0].each do |stmt| if stmt.is_a?(AST::TopLevelConstruct) error "Classes, definitions, and nodes may only appear at toplevel or inside other classes", \ :line => stmt.context[:line], :file => stmt.context[:file] end end result = val[0] } # The main list of valid statements statement_or_declaration: resource | virtualresource | collection | assignment | casestatement | ifstatement_begin | import | fstatement | definition | hostclass | nodedef | resourceoverride | append | relationship relationship: relationship_side edge relationship_side { result = AST::Relationship.new(val[0], val[2], val[1][:value], ast_context) } | relationship edge relationship_side { result = AST::Relationship.new(val[0], val[2], val[1][:value], ast_context) } relationship_side: resource | resourceref | collection edge: IN_EDGE | OUT_EDGE | IN_EDGE_SUB | OUT_EDGE_SUB fstatement: NAME LPAREN funcvalues RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :statement } | NAME LPAREN funcvalues COMMA RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :statement } | NAME LPAREN RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :statement } | NAME funcvalues { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[1], :ftype => :statement } funcvalues: namestring { result = aryfy(val[0]) } | resourceref { result = aryfy(val[0]) } | funcvalues COMMA namestring { val[0].push(val[2]) result = val[0] } | funcvalues COMMA resourceref { val[0].push(val[2]) result = val[0] } # 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 | hasharrayaccesses | CLASSNAME { result = ast AST::Name, :value => val[0][:value] } resource: classname LBRACE resourceinstances endsemi RBRACE { @lexer.commentpop result = ast(AST::Resource, :type => val[0], :instances => val[2]) } | 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 @lexer.commentpop result = ast(AST::ResourceDefaults, :type => val[0], :parameters => val[2]) } # Override a value set elsewhere in the configuration. resourceoverride: resourceref LBRACE anyparams endcomma RBRACE { @lexer.commentpop result = ast AST::ResourceOverride, :object => val[0], :parameters => 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]) and ! Puppet[:parseonly] + if (type == :exported and ! Puppet[:storeconfigs]) Puppet.warning addcontext("You cannot collect without storeconfigs being set") end error "Defaults are not virtualizable" if val[1].is_a? AST::ResourceDefaults method = type.to_s + "=" # Just mark our resource as exported and pass it through. val[1].send(method, true) 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 LBRACE anyparams endcomma RBRACE { @lexer.commentpop Puppet.warning addcontext("Collection names must now be capitalized") if val[0] =~ /^[a-z]/ 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] and ! Puppet[:parseonly] + if args[:form] == :exported and ! Puppet[:storeconfigs] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end args[:override] = val[3] result = ast AST::Collection, args } | 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] and ! Puppet[:parseonly] + if args[:form] == :exported and ! Puppet[:storeconfigs] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") 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 { result=val[0][:value] } | OR { result=val[0][:value] } collexpr: colllval ISEQUAL simplervalue { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val } | colllval NOTEQUAL simplervalue { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val } colllval: variable | name resourceinst: resourcename COLON params endcomma { result = ast AST::ResourceInstance, :title => val[0], :parameters => val[2] } resourceinstances: resourceinst { result = aryfy(val[0]) } | resourceinstances SEMIC resourceinst { val[0].push val[2] result = val[0] } endsemi: # nothing | SEMIC undef: UNDEF { result = ast AST::Undef, :value => :undef } name: NAME { result = ast AST::Name, :value => val[0][:value], :line => val[0][:line] } type: CLASSREF { result = ast AST::Type, :value => val[0][:value], :line => val[0][:line] } resourcename: quotedtext | name | type | selector | variable | array | hasharrayaccesses assignment: VARIABLE EQUALS expression { raise Puppet::ParseError, "Cannot assign to variables in other namespaces" if val[0][:value] =~ /::/ # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :line => val[0][:line] } | hasharrayaccess EQUALS expression { result = ast AST::VarDef, :name => val[0], :value => val[2] } append: VARIABLE APPENDS expression { variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :append => true, :line => val[0][:line] } params: # nothing { result = ast AST::ASTArray } | param { result = aryfy(val[0]) } | params COMMA param { val[0].push(val[2]) result = val[0] } param: NAME FARROW rvalue { result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2] } addparam: NAME PARROW rvalue { result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2], :add => true } anyparam: param | addparam anyparams: # nothing { result = ast AST::ASTArray } | anyparam { result = aryfy(val[0]) } | anyparams COMMA anyparam { val[0].push(val[2]) result = val[0] } rvalues: rvalue { result = aryfy(val[0]) } | rvalues comma rvalue { result = val[0].push(val[2]) } simplervalue: quotedtext | name | type | boolean | selector | variable rvalue: quotedtext | name | type | boolean | selector | variable | array | hash | hasharrayaccesses | resourceref | funcrvalue | undef # We currently require arguments in these functions. funcrvalue: NAME LPAREN funcvalues RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :rvalue } | NAME LPAREN RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :rvalue } quotedtext: STRING { result = ast AST::String, :value => val[0][:value], :line => val[0][:line] } | DQPRE dqrval { result = ast AST::Concat, :value => [ast(AST::String,val[0])]+val[1], :line => val[0][:line] } dqrval: expression dqtail { result = [val[0]] + val[1] } dqtail: DQPOST { result = [ast(AST::String,val[0])] } | DQMID dqrval { result = [ast(AST::String,val[0])] + val[1] } boolean: BOOLEAN { result = ast AST::Boolean, :value => val[0][:value], :line => val[0][:line] } resourceref: NAME LBRACK rvalues RBRACK { Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0][:value], :line => val[0][:line], :title => val[2] } | classref LBRACK rvalues RBRACK { result = ast AST::ResourceReference, :type => val[0], :title => val[2] } ifstatement_begin: IF ifstatement { result = val[1] } ifstatement: expression LBRACE statements RBRACE else { @lexer.commentpop args = { :test => val[0], :statements => val[2] } args[:else] = val[4] if val[4] result = ast AST::IfStatement, args } | expression LBRACE RBRACE else { @lexer.commentpop args = { :test => val[0], :statements => ast(AST::Nop) } args[:else] = val[3] if val[3] result = ast AST::IfStatement, args } else: # nothing | ELSIF ifstatement { result = ast AST::Else, :statements => val[1] } | ELSE LBRACE statements RBRACE { @lexer.commentpop result = ast AST::Else, :statements => val[2] } | ELSE LBRACE RBRACE { @lexer.commentpop result = ast AST::Else, :statements => ast(AST::Nop) } # Unlike yacc/bison, it seems racc # gives tons of shift/reduce warnings # with the following syntax: # # expression: ... # | expression arithop expressio { ... } # # arithop: PLUS | MINUS | DIVIDE | TIMES ... # # So I had to develop the expression by adding one rule # per operator :-( expression: rvalue | expression IN rvalue { result = ast AST::InOperator, :lval => val[0], :rval => val[2] } | expression MATCH regex { result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression NOMATCH regex { result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression PLUS expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression MINUS expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression DIV expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression TIMES expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression LSHIFT expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression RSHIFT expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | MINUS expression =UMINUS { result = ast AST::Minus, :value => val[1] } | expression NOTEQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression ISEQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression GREATERTHAN expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression GREATEREQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression LESSTHAN expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression LESSEQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | NOT expression { result = ast AST::Not, :value => val[1] } | expression AND expression { result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression OR expression { result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | LPAREN expression RPAREN { result = val[1] } casestatement: CASE rvalue LBRACE caseopts RBRACE { @lexer.commentpop result = ast AST::CaseStatement, :test => val[1], :options => val[3] } caseopts: caseopt { result = aryfy(val[0]) } | caseopts caseopt { val[0].push val[1] result = val[0] } caseopt: casevalues COLON LBRACE statements RBRACE { @lexer.commentpop result = ast AST::CaseOpt, :value => val[0], :statements => val[3] } | casevalues COLON LBRACE RBRACE { @lexer.commentpop result = ast( AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) } casevalues: selectlhand { result = aryfy(val[0]) } | casevalues COMMA selectlhand { val[0].push(val[2]) result = val[0] } selector: selectlhand QMARK svalues { result = ast AST::Selector, :param => val[0], :values => val[2] } svalues: selectval | LBRACE sintvalues endcomma RBRACE { @lexer.commentpop 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 | hasharrayaccess | DEFAULT { result = ast AST::Default, :value => val[0][:value], :line => val[0][:line] } | regex # These are only used for importing, and we don't interpolate there. string: STRING { result = [val[0][:value]] } strings: string | strings COMMA string { result = val[0] += val[2] } import: IMPORT strings { val[1].each do |file| import(file) end result = nil } # Disable definition inheritance for now. 8/27/06, luke #definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE { definition: DEFINE classname argumentlist LBRACE statements RBRACE { @lexer.commentpop result = Puppet::Parser::AST::Definition.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :code => val[4], :line => val[0][:line])) @lexer.indefine = false #} | DEFINE NAME argumentlist parent LBRACE RBRACE { } | DEFINE classname argumentlist LBRACE RBRACE { @lexer.commentpop result = Puppet::Parser::AST::Definition.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :line => val[0][:line])) @lexer.indefine = false } #hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE { hostclass: CLASS classname argumentlist classparent LBRACE statements_and_declarations RBRACE { @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop result = Puppet::Parser::AST::Hostclass.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :parent => val[3], :code => val[5], :line => val[0][:line])) } | CLASS classname argumentlist classparent LBRACE RBRACE { @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop result = Puppet::Parser::AST::Hostclass.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :parent => val[3], :line => val[0][:line])) } nodedef: NODE hostnames nodeparent LBRACE statements RBRACE { @lexer.commentpop result = Puppet::Parser::AST::Node.new(val[1], ast_context(true).merge(:parent => val[2], :code => val[4], :line => val[0][:line])) } | NODE hostnames nodeparent LBRACE RBRACE { @lexer.commentpop result = Puppet::Parser::AST::Node.new(val[1], ast_context(true).merge(:parent => val[2], :line => val[0][:line])) } classref: CLASSREF { result = val[0][:value] } classname: NAME { result = val[0][:value] } | CLASSNAME { result = val[0][:value] } | CLASS { result = "class" } # Multiple hostnames, as used for node names. These are all literal # strings, not AST objects. hostnames: nodename { result = [result] } | hostnames COMMA nodename { result = val[0] result << val[2] } nodename: hostname { result = ast AST::HostName, :value => val[0] } hostname: NAME { result = val[0][:value] } | STRING { result = val[0][:value] } | DEFAULT { result = val[0][:value] } | regex 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][:value], val[2]] } | NAME { Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value]] } | VARIABLE EQUALS rvalue { result = [val[0][:value], val[2]] } | VARIABLE { result = [val[0][:value]] } 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][:value], :line => val[0][:line] } array: LBRACK rvalues RBRACK { result = val[1] } | LBRACK rvalues COMMA RBRACK { result = val[1] } | LBRACK RBRACK { result = ast AST::ASTArray } comma: FARROW | COMMA endcomma: # nothing | COMMA { result = nil } regex: REGEX { result = ast AST::Regex, :value => val[0][:value] } hash: LBRACE hashpairs RBRACE { if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end } | LBRACE hashpairs COMMA RBRACE { if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end } | LBRACE RBRACE { result = ast AST::ASTHash } hashpairs: hashpair | hashpairs COMMA hashpair { if val[0].instance_of?(AST::ASTHash) result = val[0].merge(val[2]) else result = ast AST::ASTHash, :value => val[0] result.merge(val[2]) end } hashpair: key FARROW rvalue { result = ast AST::ASTHash, { :value => { val[0] => val[2] } } } key: NAME { result = val[0][:value] } | quotedtext { result = val[0] } hasharrayaccess: VARIABLE LBRACK rvalue RBRACK { result = ast AST::HashOrArrayAccess, :variable => val[0][:value], :key => val[2] } hasharrayaccesses: hasharrayaccess | hasharrayaccesses LBRACK rvalue RBRACK { result = ast AST::HashOrArrayAccess, :variable => val[0], :key => val[2] } end ---- header ---- require 'puppet' require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' module Puppet class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end class AlreadyImportedError < ImportError; end end ---- 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: diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index 300ddddd6..611398d14 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -1,2616 +1,2616 @@ # # DO NOT MODIFY!!!! # This file is automatically generated by Racc 1.4.6 # from Racc grammer file "". # require 'racc/parser.rb' require 'puppet' require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' module Puppet class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end class AlreadyImportedError < ImportError; end end module Puppet module Parser class Parser < Racc::Parser module_eval(<<'...end grammar.ra/module_eval...', 'grammar.ra', 789) # 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: ...end grammar.ra/module_eval... ##### State transition tables begin ### racc_action_table = [ 242, 243, 55, 68, 71, 299, 157, 55, 78, 117, 199, 68, 71, 181, 186, -169, 36, 112, 341, 113, 38, 55, 177, 179, 182, 185, 170, 10, 68, 71, 231, 158, 114, 292, 293, 356, 257, 368, 68, 71, 59, 198, 100, 61, 103, 59, 180, 184, 61, 198, 189, 172, 173, 174, 176, 178, 111, 183, 187, 59, 95, 66, 61, 60, 175, 79, 253, 154, 60, 88, 345, 66, 233, 229, 139, 79, 81, 231, 84, 88, 68, 71, 60, 225, 74, 99, 68, 71, 84, 60, 100, 90, 103, 203, 203, 292, 293, 223, 296, 60, 203, 117, 231, 78, 202, 202, 295, 240, 95, 209, 209, 202, 166, 66, 186, 170, 209, 79, 253, 66, 241, 88, 177, 79, 81, 334, 139, 88, 68, 71, 84, 310, 74, 99, 68, 71, 84, 357, 100, 90, 103, 60, 183, 187, 311, 358, 307, 60, 308, 175, 189, 172, 173, 174, 176, 178, 95, 183, 187, 36, 309, 66, 288, 38, 175, 79, 253, 66, 218, 88, 10, 79, 81, 219, 139, 88, 338, 312, 84, 336, 74, 99, 68, 71, 84, 218, 100, 90, 103, 60, 219, 173, 174, 217, 285, 60, 183, 187, 257, 284, 203, 68, 71, 175, 95, 100, 36, 103, 68, 71, 127, 202, 100, 316, 103, 66, 209, 10, 15, 79, 81, 173, 174, 88, 183, 187, 183, 187, 74, 99, 95, 175, 84, 175, 66, 90, 113, 338, 79, 81, 336, 66, 88, 60, 213, 79, 81, 74, 319, 88, 216, 84, 68, 71, 74, 99, 213, 49, 84, 68, 71, 90, 60, 100, 215, 103, 47, 48, 287, 60, 154, 286, 189, 172, 173, 174, 324, 68, 71, 183, 187, 100, 213, 103, 36, 66, 175, 327, 38, 79, 253, 314, 66, 88, 200, 10, 79, 81, 139, 95, 88, -171, 84, 68, 71, 74, 330, 78, 170, 84, 66, -172, 259, 60, 79, 81, 257, 258, 88, -173, 60, -168, -170, 74, 99, 70, 228, 84, 68, 71, 90, 36, 100, -167, 103, 127, 66, -167, 60, 161, 79, 81, 10, 15, 88, -169, -171, 297, 72, 74, 95, 257, 258, 84, 68, 71, 36, -172, 100, -168, 38, 66, 112, -170, 60, 79, 81, 10, 15, 88, -22, -22, -22, -22, 74, 99, 68, 71, 84, 290, 100, 90, 103, 292, 293, -167, 167, 66, 166, 60, 335, 79, 253, 229, -187, 88, 339, 68, 71, 161, 139, 100, 49, 103, 84, 43, 44, 40, 41, 66, 234, -169, -174, 79, 81, 60, 156, 88, 154, 68, 71, 354, 74, 100, 122, 103, 84, 122, 359, 161, 66, 60, -170, 53, 79, 81, 229, 60, 88, 366, 68, 71, 52, 74, 100, -168, 103, 84, 43, 44, 40, 41, 66, 232, 51, 373, 79, 81, 60, 374, 88, -169, 95, -167, -170, 74, 68, 71, -168, 84, 100, -172, 103, 66, 60, 376, 45, 79, 81, -222, 60, 88, -24, -24, -24, -24, 74, 99, 95, 234, 84, 379, 238, 90, 39, 384, 68, 71, 385, 66, 100, 60, 103, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, nil, nil, 84, nil, 95, 90, nil, nil, 68, 71, nil, nil, 100, 60, 103, 66, nil, nil, nil, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, 95, nil, 84, nil, nil, 90, nil, nil, 68, 71, nil, 66, 100, 60, 103, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, nil, nil, 84, nil, 95, 90, nil, nil, 68, 71, nil, nil, 100, 60, 103, 66, nil, nil, nil, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, 95, nil, 84, nil, nil, 90, nil, nil, 68, 71, nil, 66, 100, 60, 103, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, nil, nil, 84, nil, 95, 90, nil, nil, 68, 71, nil, nil, 100, 60, 103, 66, nil, nil, nil, 79, 81, nil, nil, 88, 68, 71, nil, nil, 74, 99, 68, 71, 84, nil, 100, 90, 103, nil, nil, nil, nil, 66, nil, 60, nil, 79, 81, nil, nil, 88, nil, nil, 95, nil, 74, 68, 71, 66, 84, 100, nil, 79, 253, 66, nil, 88, nil, 79, 81, 60, 139, 88, nil, nil, 84, nil, 74, 99, nil, nil, 84, 68, 71, 90, nil, 60, nil, nil, 66, nil, nil, 60, 79, 144, nil, nil, 88, nil, nil, nil, nil, 139, 68, 71, nil, 84, 100, nil, 103, nil, nil, nil, nil, nil, 66, nil, 60, nil, 79, 253, 68, 71, 88, nil, 100, nil, 103, 139, nil, nil, nil, 84, nil, nil, nil, 66, nil, nil, nil, 79, 81, nil, 60, 88, nil, 68, 71, nil, 74, nil, nil, nil, 84, 66, nil, nil, nil, 79, 81, nil, nil, 88, nil, 60, nil, nil, 74, nil, 261, nil, 84, 68, 71, nil, nil, 100, nil, 103, 66, nil, nil, 60, 79, 81, nil, nil, 88, nil, nil, nil, 72, 74, 68, 71, nil, 84, 100, nil, 103, nil, nil, nil, nil, nil, 66, nil, 60, nil, 79, 81, nil, nil, 88, nil, 95, nil, nil, 74, 68, 71, nil, 84, nil, nil, nil, 66, nil, nil, nil, 79, 81, nil, 60, 88, nil, nil, nil, nil, 74, 99, nil, 162, 84, 68, 71, 90, nil, 100, nil, 103, nil, 66, nil, 60, nil, 79, 81, nil, nil, 88, nil, nil, nil, 72, 74, 95, nil, nil, 84, 68, 71, nil, nil, 100, nil, 103, 66, nil, nil, 60, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, 95, nil, 84, 68, 71, 90, nil, 100, nil, 103, nil, 66, nil, 60, nil, 79, 81, 68, 71, 88, nil, nil, nil, 255, 74, 99, nil, nil, 84, nil, nil, 90, nil, nil, nil, nil, 66, nil, nil, 60, 79, 81, 68, 71, 88, nil, 100, nil, 103, 74, 66, nil, nil, 84, 79, 253, nil, nil, 88, nil, nil, 68, 71, 139, 60, nil, nil, 84, nil, nil, nil, nil, nil, nil, nil, 66, nil, nil, 60, 79, 81, nil, nil, 88, 328, nil, 68, 71, 74, nil, nil, nil, 84, nil, 66, nil, nil, nil, 79, 81, nil, nil, 88, 60, 68, 71, 72, 74, 100, 193, 103, 84, nil, nil, nil, nil, nil, nil, nil, 66, nil, nil, 60, 79, 253, 68, 71, 88, nil, 100, nil, 103, 139, nil, nil, nil, 84, 66, nil, nil, nil, 79, 81, nil, nil, 88, nil, 60, nil, nil, 74, 68, 71, nil, 84, 100, nil, 103, 66, nil, nil, nil, 79, 81, nil, 60, 88, nil, nil, nil, nil, 74, nil, 95, nil, 84, nil, 68, 71, nil, nil, 100, nil, 103, 66, nil, 60, nil, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, 95, nil, 84, 68, 71, 90, nil, 100, nil, 103, nil, 66, nil, 60, nil, 79, 81, 68, 71, 88, nil, nil, nil, nil, 74, 99, nil, nil, 84, nil, nil, 90, nil, nil, nil, nil, 66, nil, nil, 60, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 66, nil, nil, 84, 79, 81, nil, nil, 88, nil, nil, nil, 72, 74, 60, 68, 71, 84, nil, 100, nil, 103, 189, 172, 173, 174, 176, 178, 60, 183, 187, nil, 189, 172, 173, 174, 175, 95, nil, 183, 187, 68, 71, nil, nil, 100, 175, 103, 66, nil, nil, nil, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, 95, nil, 84, nil, nil, 90, nil, nil, 68, 71, nil, 66, 100, 60, 103, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, nil, nil, 84, nil, 95, 90, nil, nil, 68, 71, nil, nil, 100, 60, 103, 66, nil, nil, nil, 79, 81, nil, nil, 88, nil, nil, nil, nil, 74, 99, 95, nil, 84, nil, nil, 90, nil, nil, nil, nil, nil, 66, nil, 60, nil, 79, 81, 188, nil, 88, nil, nil, nil, nil, 74, 99, 181, 186, 84, nil, nil, 90, nil, nil, nil, 177, 179, 182, 185, 60, nil, 181, 186, nil, nil, nil, nil, nil, nil, nil, 177, 179, 182, 185, nil, nil, nil, nil, nil, 180, 184, nil, nil, 189, 172, 173, 174, 176, 178, nil, 183, 187, nil, nil, 180, 184, nil, 175, 189, 172, 173, 174, 176, 178, nil, 183, 187, 181, 186, nil, nil, nil, 175, nil, nil, 283, 177, 179, 182, 185, nil, nil, 181, 186, nil, nil, nil, nil, nil, nil, nil, 177, 179, 182, 185, nil, nil, nil, nil, nil, 180, 184, nil, nil, 189, 172, 173, 174, 176, 178, nil, 183, 187, nil, nil, nil, 184, nil, 175, 189, 172, 173, 174, 176, 178, nil, 183, 187, 181, 186, nil, nil, nil, 175, nil, nil, nil, 177, 179, 182, 185, nil, nil, 181, 186, nil, nil, nil, nil, nil, nil, nil, 177, 179, 182, 185, nil, nil, nil, nil, nil, 180, 184, nil, nil, 189, 172, 173, 174, 176, 178, nil, 183, 187, nil, nil, 180, 184, nil, 175, 189, 172, 173, 174, 176, 178, nil, 183, 187, 181, 186, nil, nil, nil, 175, nil, nil, nil, 177, 179, 182, 185, nil, nil, 26, 186, 32, 1, nil, 8, 11, nil, 18, 177, 23, nil, 29, nil, 2, nil, nil, 10, 15, nil, 186, nil, 189, 172, 173, 174, 176, 178, 177, 183, 187, nil, nil, nil, nil, 186, 175, 189, 172, 173, 174, 176, 178, 177, 183, 187, nil, nil, nil, nil, nil, 175, nil, nil, nil, nil, 189, 172, 173, 174, 176, 178, nil, 183, 187, 281, nil, nil, nil, nil, 175, 189, 172, 173, 174, 176, 178, nil, 183, 187, nil, nil, nil, nil, 377, 175, 26, nil, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, 26, 364, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, nil, 383, nil, 26, nil, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, 26, 306, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, nil, 381, nil, 26, nil, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, 26, 326, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, nil, nil, nil, 26, nil, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, 26, nil, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, 26, nil, 32, 1, nil, 8, 11, nil, 18, nil, 23, nil, 29, nil, 2, nil, nil, 10, 15, 189, 172, 173, 174, 176, 178, nil, 183, 187, nil, nil, nil, nil, nil, 175 ] racc_action_check = [ 164, 164, 18, 103, 103, 214, 56, 157, 81, 32, 103, 286, 286, 164, 164, 136, 11, 27, 286, 27, 11, 158, 164, 164, 164, 164, 81, 11, 300, 300, 154, 56, 27, 214, 214, 300, 332, 332, 189, 189, 18, 103, 189, 18, 189, 157, 164, 164, 157, 286, 164, 164, 164, 164, 164, 164, 27, 164, 164, 158, 189, 300, 158, 18, 164, 300, 300, 200, 157, 300, 294, 189, 154, 212, 300, 189, 189, 213, 300, 189, 369, 369, 158, 134, 189, 189, 186, 186, 189, 300, 186, 189, 186, 203, 291, 294, 294, 133, 206, 189, 111, 216, 144, 127, 203, 291, 206, 163, 186, 203, 291, 111, 262, 369, 273, 144, 111, 369, 369, 186, 163, 369, 273, 186, 186, 262, 369, 186, 357, 357, 369, 221, 186, 186, 185, 185, 186, 302, 185, 186, 185, 369, 264, 264, 221, 302, 218, 186, 219, 264, 273, 273, 273, 273, 273, 273, 185, 273, 273, 1, 220, 357, 197, 1, 273, 357, 357, 185, 122, 357, 1, 185, 185, 122, 357, 185, 281, 224, 357, 281, 185, 185, 176, 176, 185, 310, 176, 185, 176, 357, 310, 282, 282, 121, 192, 185, 282, 282, 192, 192, 114, 175, 175, 282, 176, 175, 46, 175, 336, 336, 46, 114, 336, 227, 336, 176, 114, 46, 46, 176, 176, 263, 263, 176, 265, 265, 263, 263, 176, 176, 336, 265, 176, 263, 175, 176, 119, 335, 175, 175, 335, 336, 175, 176, 229, 336, 336, 175, 230, 336, 116, 175, 215, 215, 336, 336, 232, 8, 336, 330, 330, 336, 175, 330, 115, 330, 8, 8, 194, 336, 234, 194, 267, 267, 267, 267, 235, 184, 184, 267, 267, 184, 113, 184, 225, 215, 267, 239, 225, 215, 215, 225, 330, 215, 110, 225, 330, 330, 215, 184, 330, 108, 215, 23, 23, 330, 250, 23, 253, 330, 184, 107, 169, 215, 184, 184, 169, 169, 184, 106, 330, 105, 102, 184, 184, 23, 140, 184, 26, 26, 184, 42, 26, 141, 26, 42, 23, 101, 184, 96, 23, 23, 42, 42, 23, 94, 89, 211, 23, 23, 26, 211, 211, 23, 228, 228, 33, 87, 228, 85, 33, 26, 83, 82, 23, 26, 26, 33, 33, 26, 31, 31, 31, 31, 26, 26, 29, 29, 26, 201, 29, 26, 29, 201, 201, 80, 77, 228, 75, 26, 280, 228, 228, 142, 74, 228, 284, 308, 308, 69, 228, 308, 66, 308, 228, 6, 6, 6, 6, 29, 289, 65, 64, 29, 29, 228, 53, 29, 52, 307, 307, 298, 29, 307, 50, 307, 29, 37, 305, 138, 308, 187, 143, 17, 308, 308, 321, 29, 308, 325, 174, 174, 13, 308, 174, 146, 174, 308, 3, 3, 3, 3, 307, 147, 12, 338, 307, 307, 308, 343, 307, 347, 174, 348, 350, 307, 179, 179, 351, 307, 179, 352, 179, 174, 183, 358, 5, 174, 174, 368, 307, 174, 4, 4, 4, 4, 174, 174, 179, 155, 174, 370, 159, 174, 2, 380, 173, 173, 382, 179, 173, 174, 173, 179, 179, nil, nil, 179, nil, nil, nil, nil, 179, 179, nil, nil, 179, nil, 173, 179, nil, nil, 45, 45, nil, nil, 45, 179, 45, 173, nil, nil, nil, 173, 173, nil, nil, 173, nil, nil, nil, nil, 173, 173, 45, nil, 173, nil, nil, 173, nil, nil, 172, 172, nil, 45, 172, 173, 172, 45, 45, nil, nil, 45, nil, nil, nil, nil, 45, 45, nil, nil, 45, nil, 172, 45, nil, nil, 47, 47, nil, nil, 47, 45, 47, 172, nil, nil, nil, 172, 172, nil, nil, 172, nil, nil, nil, nil, 172, 172, 47, nil, 172, nil, nil, 172, nil, nil, 48, 48, nil, 47, 48, 172, 48, 47, 47, nil, nil, 47, nil, nil, nil, nil, 47, 47, nil, nil, 47, nil, 48, 47, nil, nil, 49, 49, nil, nil, 49, 47, 49, 48, nil, nil, nil, 48, 48, nil, nil, 48, 296, 296, nil, nil, 48, 48, 177, 177, 48, nil, 177, 48, 177, nil, nil, nil, nil, 49, nil, 48, nil, 49, 49, nil, nil, 49, nil, nil, 177, nil, 49, 51, 51, 296, 49, 51, nil, 296, 296, 177, nil, 296, nil, 177, 177, 49, 296, 177, nil, nil, 296, nil, 177, 177, nil, nil, 177, 295, 295, 177, nil, 296, nil, nil, 51, nil, nil, 177, 51, 51, nil, nil, 51, nil, nil, nil, nil, 51, 231, 231, nil, 51, 231, nil, 231, nil, nil, nil, nil, nil, 295, nil, 51, nil, 295, 295, 233, 233, 295, nil, 233, nil, 233, 295, nil, nil, nil, 295, nil, nil, nil, 231, nil, nil, nil, 231, 231, nil, 295, 231, nil, 170, 170, nil, 231, nil, nil, nil, 231, 233, nil, nil, nil, 233, 233, nil, nil, 233, nil, 231, nil, nil, 233, nil, 170, nil, 233, 288, 288, nil, nil, 288, nil, 288, 170, nil, nil, 233, 170, 170, nil, nil, 170, nil, nil, nil, 170, 170, 182, 182, nil, 170, 182, nil, 182, nil, nil, nil, nil, nil, 288, nil, 170, nil, 288, 288, nil, nil, 288, nil, 182, nil, nil, 288, 70, 70, nil, 288, nil, nil, nil, 182, nil, nil, nil, 182, 182, nil, 288, 182, nil, nil, nil, nil, 182, 182, nil, 70, 182, 71, 71, 182, nil, 71, nil, 71, nil, 70, nil, 182, nil, 70, 70, nil, nil, 70, nil, nil, nil, 70, 70, 71, nil, nil, 70, 180, 180, nil, nil, 180, nil, 180, 71, nil, nil, 70, 71, 71, nil, nil, 71, nil, nil, nil, nil, 71, 71, 180, nil, 71, 112, 112, 71, nil, 112, nil, 112, nil, 180, nil, 71, nil, 180, 180, 167, 167, 180, nil, nil, nil, 167, 180, 180, nil, nil, 180, nil, nil, 180, nil, nil, nil, nil, 112, nil, nil, 180, 112, 112, 78, 78, 112, nil, 78, nil, 78, 112, 167, nil, nil, 112, 167, 167, nil, nil, 167, nil, nil, 240, 240, 167, 112, nil, nil, 167, nil, nil, nil, nil, nil, nil, nil, 78, nil, nil, 167, 78, 78, nil, nil, 78, 240, nil, 255, 255, 78, nil, nil, nil, 78, nil, 240, nil, nil, nil, 240, 240, nil, nil, 240, 78, 100, 100, 240, 240, 100, 100, 100, 240, nil, nil, nil, nil, nil, nil, nil, 255, nil, nil, 240, 255, 255, 260, 260, 255, nil, 260, nil, 260, 255, nil, nil, nil, 255, 100, nil, nil, nil, 100, 100, nil, nil, 100, nil, 255, nil, nil, 100, 181, 181, nil, 100, 181, nil, 181, 260, nil, nil, nil, 260, 260, nil, 100, 260, nil, nil, nil, nil, 260, nil, 181, nil, 260, nil, 90, 90, nil, nil, 90, nil, 90, 181, nil, 260, nil, 181, 181, nil, nil, 181, nil, nil, nil, nil, 181, 181, 90, nil, 181, 161, 161, 181, nil, 161, nil, 161, nil, 90, nil, 181, nil, 90, 90, 166, 166, 90, nil, nil, nil, nil, 90, 90, nil, nil, 90, nil, nil, 90, nil, nil, nil, nil, 161, nil, nil, 90, 161, 161, nil, nil, 161, nil, nil, nil, nil, 161, 166, nil, nil, 161, 166, 166, nil, nil, 166, nil, nil, nil, 166, 166, 161, 95, 95, 166, nil, 95, nil, 95, 277, 277, 277, 277, 277, 277, 166, 277, 277, nil, 269, 269, 269, 269, 277, 95, nil, 269, 269, 178, 178, nil, nil, 178, 269, 178, 95, nil, nil, nil, 95, 95, nil, nil, 95, nil, nil, nil, nil, 95, 95, 178, nil, 95, nil, nil, 95, nil, nil, 99, 99, nil, 178, 99, 95, 99, 178, 178, nil, nil, 178, nil, nil, nil, nil, 178, 178, nil, nil, 178, nil, 99, 178, nil, nil, 242, 242, nil, nil, 242, 178, 242, 99, nil, nil, nil, 99, 99, nil, nil, 99, nil, nil, nil, nil, 99, 99, 242, nil, 99, nil, nil, 99, nil, nil, nil, nil, nil, 242, nil, 99, nil, 242, 242, 91, nil, 242, nil, nil, nil, nil, 242, 242, 91, 91, 242, nil, nil, 242, nil, nil, nil, 91, 91, 91, 91, 242, nil, 129, 129, nil, nil, nil, nil, nil, nil, nil, 129, 129, 129, 129, nil, nil, nil, nil, nil, 91, 91, nil, nil, 91, 91, 91, 91, 91, 91, nil, 91, 91, nil, nil, 129, 129, nil, 91, 129, 129, 129, 129, 129, 129, nil, 129, 129, 190, 190, nil, nil, nil, 129, nil, nil, 190, 190, 190, 190, 190, nil, nil, 271, 271, nil, nil, nil, nil, nil, nil, nil, 271, 271, 271, 271, nil, nil, nil, nil, nil, 190, 190, nil, nil, 190, 190, 190, 190, 190, 190, nil, 190, 190, nil, nil, nil, 271, nil, 190, 271, 271, 271, 271, 271, 271, nil, 271, 271, 131, 131, nil, nil, nil, 271, nil, nil, nil, 131, 131, 131, 131, nil, nil, 132, 132, nil, nil, nil, nil, nil, nil, nil, 132, 132, 132, 132, nil, nil, nil, nil, nil, 131, 131, nil, nil, 131, 131, 131, 131, 131, 131, nil, 131, 131, nil, nil, 132, 132, nil, 131, 132, 132, 132, 132, 132, 132, nil, 132, 132, 275, 275, nil, nil, nil, 132, nil, nil, nil, 275, 275, 275, 275, nil, nil, 279, 276, 279, 279, nil, 279, 279, nil, 279, 276, 279, nil, 279, nil, 279, nil, nil, 279, 279, nil, 270, nil, 275, 275, 275, 275, 275, 275, 270, 275, 275, nil, nil, nil, nil, 272, 275, 276, 276, 276, 276, 276, 276, 272, 276, 276, nil, nil, nil, nil, nil, 276, nil, nil, nil, nil, 270, 270, 270, 270, 270, 270, nil, 270, 270, 188, nil, nil, nil, nil, 270, 272, 272, 272, 272, 272, 272, nil, 272, 272, nil, nil, nil, nil, 363, 272, 188, nil, 188, 188, nil, 188, 188, nil, 188, nil, 188, nil, 188, nil, 188, nil, nil, 188, 188, 363, 312, 363, 363, nil, 363, 363, nil, 363, nil, 363, nil, 363, nil, 363, nil, nil, 363, 363, nil, 376, nil, 312, nil, 312, 312, nil, 312, 312, nil, 312, nil, 312, nil, 312, nil, 312, nil, nil, 312, 312, 376, 217, 376, 376, nil, 376, 376, nil, 376, nil, 376, nil, 376, nil, 376, nil, nil, 376, 376, nil, 373, nil, 217, nil, 217, 217, nil, 217, 217, nil, 217, nil, 217, nil, 217, nil, 217, nil, nil, 217, 217, 373, 238, 373, 373, nil, 373, 373, nil, 373, nil, 373, nil, 373, nil, 373, nil, nil, 373, 373, nil, nil, nil, 238, nil, 238, 238, nil, 238, 238, nil, 238, nil, 238, nil, 238, nil, 238, nil, nil, 238, 238, 20, nil, 20, 20, nil, 20, 20, nil, 20, nil, 20, nil, 20, nil, 20, nil, nil, 20, 20, 0, nil, 0, 0, nil, 0, 0, nil, 0, nil, 0, nil, 0, nil, 0, nil, nil, 0, 0, 268, 268, 268, 268, 268, 268, nil, 268, 268, nil, nil, nil, nil, nil, 268 ] racc_action_pointer = [ 1726, 123, 450, 384, 418, 461, 341, nil, 251, nil, nil, -20, 446, 434, nil, nil, nil, 433, 0, nil, 1707, nil, nil, 301, nil, nil, 326, 11, nil, 374, nil, 306, 7, 320, nil, nil, nil, 403, nil, nil, nil, nil, 295, nil, nil, 520, 170, 576, 606, 632, 400, 679, 378, 416, nil, nil, -6, nil, nil, nil, nil, nil, nil, nil, 389, 388, 396, nil, nil, 393, 842, 867, nil, nil, 388, 376, nil, 363, 957, nil, 362, 2, 340, 356, nil, 336, nil, 334, nil, 323, 1092, 1294, nil, nil, 322, 1179, 333, nil, nil, 1235, 1019, 314, 299, 1, nil, 298, 296, 288, 278, nil, 286, 76, 918, 242, 176, 256, 238, nil, nil, 228, nil, 185, 133, nil, nil, nil, nil, 97, nil, 1309, nil, 1416, 1431, 90, 46, nil, -8, nil, 423, nil, 285, 310, 381, 409, 91, nil, 422, 433, nil, nil, nil, nil, nil, nil, 19, 477, nil, 5, 19, 484, nil, 1117, nil, 95, -4, nil, 1131, 932, nil, 305, 769, nil, 550, 494, 438, 199, 180, 654, 1205, 464, 893, 1066, 816, 411, 275, 132, 84, 368, 1565, 36, 1355, nil, 187, nil, 259, nil, nil, 151, nil, nil, 27, 333, nil, 69, nil, nil, 80, nil, nil, nil, nil, 340, 61, 66, -17, 250, 99, 1647, 131, 133, 135, 119, nil, nil, 169, 248, nil, 204, 352, 204, 239, 726, 216, 744, 230, 267, nil, nil, 1688, 280, 976, nil, 1261, nil, nil, nil, nil, nil, nil, nil, 295, nil, nil, 284, nil, 1001, nil, nil, nil, nil, 1040, nil, 100, 165, 81, 163, nil, 218, 1721, 1144, 1511, 1370, 1526, 96, nil, 1477, 1492, 1134, nil, 1479, 381, 145, 135, nil, 389, nil, 9, nil, 795, 398, nil, 70, nil, nil, 45, 705, 648, nil, 412, nil, 26, nil, 125, nil, nil, 419, nil, 417, 395, nil, 150, nil, 1606, nil, nil, nil, nil, nil, nil, nil, nil, 424, nil, nil, nil, 430, nil, nil, nil, nil, 257, nil, 25, nil, nil, 206, 206, nil, 447, nil, nil, nil, nil, 450, nil, nil, nil, 438, 440, nil, 441, 445, 448, nil, nil, nil, nil, 126, 467, nil, nil, nil, nil, 1584, nil, nil, nil, nil, 470, 78, 482, nil, nil, 1666, nil, nil, 1625, nil, nil, nil, 486, nil, 489, nil, nil, nil ] racc_action_default = [ -198, -235, -51, -19, -8, -235, -235, -9, -235, -10, -189, -190, -235, -23, -11, -187, -12, -235, -235, -13, -1, -14, -2, -188, -15, -3, -235, -235, -16, -235, -17, -6, -235, -235, -18, -7, -190, -198, -188, -52, -27, -28, -235, -25, -26, -235, -235, -235, -235, -235, -198, -86, -93, -235, -197, -195, -198, -191, -193, -194, -223, -196, -4, -42, -233, -43, -215, -176, -118, -44, -235, -235, -45, -34, -75, -32, -33, -235, -235, -123, -37, -74, -38, -235, -73, -39, -173, -40, -175, -41, -235, -235, -126, -108, -104, -235, -112, -133, -113, -235, -235, -105, -109, -235, -111, -106, -115, -107, -114, -110, -54, -198, -235, -86, -198, -235, -180, -177, -178, -235, -50, -235, -199, -200, -24, -21, -23, -188, -22, -84, -20, -83, -85, -235, -198, -79, -76, -87, -82, -75, -71, -77, -221, -80, -74, -69, -78, -235, -172, -171, -81, -91, -92, -94, -235, -221, 386, -235, -235, -235, -209, -235, -31, -235, -235, -119, -235, -235, -96, -235, -235, -143, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -150, -235, -218, -235, -231, -227, -235, -230, -226, -93, -235, -215, -198, -58, -60, -235, -67, -57, -74, -66, -235, -221, -235, -235, -235, -235, -235, -208, -206, -235, -235, -203, -232, -235, -235, -211, -235, -72, -222, -235, -235, -86, -235, -222, -235, -192, -210, -235, -235, -235, -29, -235, -121, -120, -36, -35, -174, -169, -167, -235, -170, -161, -74, -168, -235, -162, -219, -220, -124, -235, -117, -235, -138, -140, -139, -134, -141, -145, -142, -147, -152, -149, -146, -135, -151, -148, -144, -136, -5, -235, -129, -137, -153, -220, -216, -235, -224, -235, -221, -55, -235, -63, -62, -235, -235, -235, -125, -235, -56, -235, -155, -235, -159, -179, -235, -182, -235, -235, -201, -235, -202, -235, -213, -214, -212, -46, -70, -88, -47, -89, -221, -90, -95, -49, -235, -186, -234, -30, -122, -235, -164, -221, -97, -116, -129, -235, -128, -235, -217, -228, -225, -229, -235, -59, -61, -102, -98, -99, -64, -103, -100, -101, -65, -48, -156, -154, -235, -235, -181, -207, -205, -204, -235, -184, -68, -185, -166, -220, -235, -235, -127, -130, -235, -53, -160, -235, -183, -165, -163, -235, -132, -235, -158, -131, -157 ] racc_goto_table = [ 27, 13, 20, 222, 89, 92, 115, 37, 62, 5, 65, 196, 256, 145, 230, 150, 118, 50, 155, 301, 27, 13, 246, 75, 73, 57, 133, 235, 201, 5, 250, 214, 149, 119, 337, 165, 121, 344, 136, 69, 169, 120, 27, 126, 142, 22, 27, 126, 300, 134, 128, 89, 63, 151, 128, 168, 125, 65, 280, 124, 130, 42, 192, 124, 46, 349, 353, 138, 244, 87, 163, 73, 252, 369, 211, 332, 323, 168, 303, 116, 135, 140, 123, 224, 298, 56, 69, 305, 371, 168, 195, 159, 227, 237, 220, 123, 246, 148, 221, 63, 331, 160, 110, 315, 355, 194, 212, 17, 325, nil, nil, 86, nil, nil, nil, nil, 87, nil, 250, nil, 294, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 239, 86, nil, nil, nil, nil, nil, nil, nil, 89, 149, nil, nil, 89, 266, 65, 248, nil, nil, 65, 86, nil, nil, 343, nil, 303, 236, nil, 289, 245, nil, nil, 262, 73, nil, nil, nil, 82, 247, nil, nil, 226, nil, nil, 69, nil, nil, nil, 69, nil, 27, 13, 317, 362, 150, 365, 340, 63, 149, 5, nil, 63, 304, 151, 248, 143, 370, 85, 329, nil, 320, 149, 322, nil, 87, 148, 378, 136, 87, 27, 13, nil, 375, 89, 82, nil, 247, 321, 5, 65, nil, 54, 318, 313, 250, 146, nil, 151, 149, 333, 27, 13, nil, 245, 248, 380, 138, nil, 382, 5, nil, nil, nil, nil, 85, nil, 86, 86, 69, 135, 86, nil, nil, 148, nil, 210, 247, 342, 210, 62, nil, 63, 80, nil, nil, 195, 148, nil, 149, 149, nil, 27, 13, 149, 347, 347, 360, 361, 87, 248, 5, nil, nil, nil, nil, nil, nil, nil, nil, nil, 141, nil, nil, 148, nil, 86, 247, 247, nil, 367, nil, 247, nil, nil, 27, 13, 363, 372, 86, 80, 82, 251, nil, 5, 82, nil, 346, 346, nil, nil, 86, nil, nil, nil, nil, nil, nil, nil, nil, nil, 149, nil, nil, 352, 352, 86, 248, nil, 148, nil, 85, 254, 149, 62, 85, nil, nil, 210, 248, nil, 207, nil, nil, 207, nil, 27, 13, nil, 247, 251, 54, 54, nil, nil, 5, 27, 13, nil, 27, 13, 247, nil, 143, nil, 5, 86, 86, 5, nil, nil, 86, nil, nil, nil, 82, nil, 274, nil, nil, 254, 278, nil, nil, nil, nil, 148, nil, nil, nil, 251, nil, nil, 146, nil, nil, 80, 249, 148, nil, 80, nil, nil, nil, nil, 85, nil, nil, nil, nil, nil, nil, 129, nil, 131, 132, nil, nil, nil, nil, 254, nil, nil, nil, nil, nil, 210, nil, 86, nil, 350, 350, nil, 207, nil, 251, nil, nil, 164, nil, 86, nil, nil, nil, nil, 249, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 171, 141, nil, 351, 351, 190, nil, nil, 254, 191, nil, nil, nil, 80, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 249, nil, nil, nil, nil, nil, nil, 251, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 251, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 207, 254, nil, nil, 348, 348, nil, nil, nil, 249, nil, nil, nil, 254, nil, nil, nil, nil, 263, 264, 265, nil, 267, 268, 269, 270, 271, 272, 273, nil, 275, 276, 277, nil, nil, 282, 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, nil, nil, nil, nil, 249, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 249, nil, nil, nil, nil, nil, nil, nil, nil, nil, 164 ] racc_goto_check = [ 38, 22, 2, 83, 29, 63, 55, 33, 4, 53, 31, 86, 71, 48, 37, 51, 73, 33, 39, 67, 38, 22, 24, 23, 22, 79, 55, 37, 42, 53, 69, 42, 29, 38, 64, 61, 75, 43, 31, 32, 58, 6, 38, 22, 36, 3, 38, 22, 66, 75, 6, 29, 30, 54, 6, 55, 20, 31, 5, 8, 20, 21, 58, 8, 21, 47, 47, 32, 62, 28, 23, 22, 70, 59, 58, 72, 57, 55, 69, 74, 30, 34, 3, 76, 37, 77, 32, 5, 64, 55, 31, 78, 35, 80, 81, 3, 24, 28, 82, 30, 71, 3, 41, 84, 67, 85, 36, 1, 5, nil, nil, 50, nil, nil, nil, nil, 28, nil, 69, nil, 42, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 55, 50, nil, nil, nil, nil, nil, nil, nil, 29, 29, nil, nil, 29, 55, 31, 31, nil, nil, 31, 50, nil, nil, 37, nil, 69, 79, nil, 39, 22, nil, nil, 23, 22, nil, nil, nil, 26, 53, nil, nil, 3, nil, nil, 32, nil, nil, nil, 32, nil, 38, 22, 48, 83, 51, 37, 86, 30, 29, 53, nil, 30, 73, 54, 31, 26, 37, 27, 61, nil, 55, 29, 55, nil, 28, 28, 71, 31, 28, 38, 22, nil, 69, 29, 26, nil, 53, 36, 53, 31, nil, 65, 54, 33, 69, 27, nil, 54, 29, 55, 38, 22, nil, 22, 31, 5, 32, nil, 5, 53, nil, nil, nil, nil, 27, nil, 50, 50, 32, 30, 50, nil, nil, 28, nil, 26, 53, 55, 26, 4, nil, 30, 25, nil, nil, 31, 28, nil, 29, 29, nil, 38, 22, 29, 31, 31, 55, 55, 28, 31, 53, nil, nil, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, 28, nil, 50, 53, 53, nil, 55, nil, 53, nil, nil, 38, 22, 2, 63, 50, 25, 26, 26, nil, 53, 26, nil, 30, 30, nil, nil, 50, nil, nil, nil, nil, nil, nil, nil, nil, nil, 29, nil, nil, 28, 28, 50, 31, nil, 28, nil, 27, 27, 29, 4, 27, nil, nil, 26, 31, nil, 25, nil, nil, 25, nil, 38, 22, nil, 53, 26, 65, 65, nil, nil, 53, 38, 22, nil, 38, 22, 53, nil, 26, nil, 53, 50, 50, 53, nil, nil, 50, nil, nil, nil, 26, nil, 65, nil, nil, 27, 65, nil, nil, nil, nil, 28, nil, nil, nil, 26, nil, nil, 27, nil, nil, 25, 25, 28, nil, 25, nil, nil, nil, nil, 27, nil, nil, nil, nil, nil, nil, 52, nil, 52, 52, nil, nil, nil, nil, 27, nil, nil, nil, nil, nil, 26, nil, 50, nil, 26, 26, nil, 25, nil, 26, nil, nil, 52, nil, 50, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 52, 25, nil, 27, 27, 52, nil, nil, 27, 52, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 25, 27, nil, nil, 25, 25, nil, nil, nil, 25, nil, nil, nil, 27, nil, nil, nil, nil, 52, 52, 52, nil, 52, 52, 52, 52, 52, 52, 52, nil, 52, 52, 52, nil, nil, 52, 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, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, nil, nil, 52 ] racc_goto_pointer = [ nil, 107, 2, 45, -12, -130, 8, nil, 17, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 14, 58, 1, 0, -144, 247, 152, 182, 46, -19, 29, -13, 16, 6, 30, -48, -7, -128, 0, -34, nil, 75, -83, -254, nil, nil, nil, -230, -38, nil, 88, -36, 384, 9, 1, -23, nil, -158, -38, -259, nil, -36, -96, -21, -247, 211, -167, -196, nil, -137, -95, -155, -180, -16, 47, -1, -51, 67, 35, 7, -65, -28, -24, -119, -122, 2, -92, nil ] racc_goto_default = [ nil, nil, 279, 208, 25, nil, 31, 35, 4, 7, 9, 14, 16, 19, 21, 24, 28, 30, 34, 3, 6, nil, 98, nil, 76, 101, 102, 105, 107, 108, 93, 94, 96, 12, nil, nil, nil, nil, 83, nil, 33, nil, nil, 204, 291, 205, 206, nil, nil, 147, 106, 109, 91, 64, 137, 97, 152, 153, nil, 260, 104, nil, nil, nil, nil, 67, nil, nil, 302, 77, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 58, nil, nil, nil, nil, nil, nil, 197 ] racc_reduce_table = [ 0, 0, :racc_error, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 1, 71, :_reduce_3, 2, 71, :_reduce_4, 1, 74, :_reduce_5, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 3, 88, :_reduce_20, 3, 88, :_reduce_21, 1, 89, :_reduce_none, 1, 89, :_reduce_none, 1, 89, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 4, 82, :_reduce_29, 5, 82, :_reduce_30, 3, 82, :_reduce_31, 2, 82, :_reduce_32, 1, 92, :_reduce_33, 1, 92, :_reduce_34, 3, 92, :_reduce_35, 3, 92, :_reduce_36, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 93, :_reduce_45, 5, 75, :_reduce_46, 5, 75, :_reduce_47, 5, 75, :_reduce_48, 5, 86, :_reduce_49, 2, 76, :_reduce_50, 1, 109, :_reduce_51, 2, 109, :_reduce_52, 6, 77, :_reduce_53, 2, 77, :_reduce_54, 3, 110, :_reduce_55, 3, 110, :_reduce_56, 1, 111, :_reduce_none, 1, 111, :_reduce_none, 3, 111, :_reduce_59, 1, 112, :_reduce_none, 3, 112, :_reduce_61, 1, 113, :_reduce_62, 1, 113, :_reduce_63, 3, 114, :_reduce_64, 3, 114, :_reduce_65, 1, 115, :_reduce_none, 1, 115, :_reduce_none, 4, 117, :_reduce_68, 1, 103, :_reduce_69, 3, 103, :_reduce_70, 0, 104, :_reduce_none, 1, 104, :_reduce_none, 1, 119, :_reduce_73, 1, 94, :_reduce_74, 1, 96, :_reduce_75, 1, 118, :_reduce_none, 1, 118, :_reduce_none, 1, 118, :_reduce_none, 1, 118, :_reduce_none, 1, 118, :_reduce_none, 1, 118, :_reduce_none, 1, 118, :_reduce_none, 3, 78, :_reduce_83, 3, 78, :_reduce_84, 3, 87, :_reduce_85, 0, 105, :_reduce_86, 1, 105, :_reduce_87, 3, 105, :_reduce_88, 3, 123, :_reduce_89, 3, 125, :_reduce_90, 1, 126, :_reduce_none, 1, 126, :_reduce_none, 0, 108, :_reduce_93, 1, 108, :_reduce_94, 3, 108, :_reduce_95, 1, 127, :_reduce_96, 3, 127, :_reduce_97, 1, 116, :_reduce_none, 1, 116, :_reduce_none, 1, 116, :_reduce_none, 1, 116, :_reduce_none, 1, 116, :_reduce_none, 1, 116, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 4, 98, :_reduce_116, 3, 98, :_reduce_117, 1, 100, :_reduce_118, 2, 100, :_reduce_119, 2, 130, :_reduce_120, 1, 131, :_reduce_121, 2, 131, :_reduce_122, 1, 97, :_reduce_123, 4, 91, :_reduce_124, 4, 91, :_reduce_125, 2, 80, :_reduce_126, 5, 132, :_reduce_127, 4, 132, :_reduce_128, 0, 133, :_reduce_none, 2, 133, :_reduce_130, 4, 133, :_reduce_131, 3, 133, :_reduce_132, 1, 121, :_reduce_none, 3, 121, :_reduce_134, 3, 121, :_reduce_135, 3, 121, :_reduce_136, 3, 121, :_reduce_137, 3, 121, :_reduce_138, 3, 121, :_reduce_139, 3, 121, :_reduce_140, 3, 121, :_reduce_141, 3, 121, :_reduce_142, 2, 121, :_reduce_143, 3, 121, :_reduce_144, 3, 121, :_reduce_145, 3, 121, :_reduce_146, 3, 121, :_reduce_147, 3, 121, :_reduce_148, 3, 121, :_reduce_149, 2, 121, :_reduce_150, 3, 121, :_reduce_151, 3, 121, :_reduce_152, 3, 121, :_reduce_153, 5, 79, :_reduce_154, 1, 135, :_reduce_155, 2, 135, :_reduce_156, 5, 136, :_reduce_157, 4, 136, :_reduce_158, 1, 137, :_reduce_159, 3, 137, :_reduce_160, 3, 99, :_reduce_161, 1, 139, :_reduce_none, 4, 139, :_reduce_163, 1, 141, :_reduce_none, 3, 141, :_reduce_165, 3, 140, :_reduce_166, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_175, 1, 138, :_reduce_none, 1, 142, :_reduce_177, 1, 143, :_reduce_none, 3, 143, :_reduce_179, 2, 81, :_reduce_180, 6, 83, :_reduce_181, 5, 83, :_reduce_182, 7, 84, :_reduce_183, 6, 84, :_reduce_184, 6, 85, :_reduce_185, 5, 85, :_reduce_186, 1, 107, :_reduce_187, 1, 102, :_reduce_188, 1, 102, :_reduce_189, 1, 102, :_reduce_190, 1, 146, :_reduce_191, 3, 146, :_reduce_192, 1, 148, :_reduce_193, 1, 149, :_reduce_194, 1, 149, :_reduce_195, 1, 149, :_reduce_196, 1, 149, :_reduce_none, 0, 72, :_reduce_198, 0, 150, :_reduce_199, 1, 144, :_reduce_none, 3, 144, :_reduce_201, 3, 144, :_reduce_202, 1, 151, :_reduce_none, 3, 151, :_reduce_204, 3, 152, :_reduce_205, 1, 152, :_reduce_206, 3, 152, :_reduce_207, 1, 152, :_reduce_208, 1, 147, :_reduce_none, 2, 147, :_reduce_210, 1, 145, :_reduce_none, 2, 145, :_reduce_212, 1, 153, :_reduce_none, 1, 153, :_reduce_none, 1, 95, :_reduce_215, 3, 120, :_reduce_216, 4, 120, :_reduce_217, 2, 120, :_reduce_218, 1, 128, :_reduce_none, 1, 128, :_reduce_none, 0, 106, :_reduce_none, 1, 106, :_reduce_222, 1, 134, :_reduce_223, 3, 129, :_reduce_224, 4, 129, :_reduce_225, 2, 129, :_reduce_226, 1, 154, :_reduce_none, 3, 154, :_reduce_228, 3, 155, :_reduce_229, 1, 156, :_reduce_230, 1, 156, :_reduce_231, 4, 122, :_reduce_232, 1, 101, :_reduce_none, 4, 101, :_reduce_234 ] racc_reduce_n = 235 racc_shift_n = 386 racc_token_table = { false => 0, :error => 1, :STRING => 2, :DQPRE => 3, :DQMID => 4, :DQPOST => 5, :LBRACK => 6, :RBRACK => 7, :LBRACE => 8, :RBRACE => 9, :SYMBOL => 10, :FARROW => 11, :COMMA => 12, :TRUE => 13, :FALSE => 14, :EQUALS => 15, :APPENDS => 16, :LESSEQUAL => 17, :NOTEQUAL => 18, :DOT => 19, :COLON => 20, :LLCOLLECT => 21, :RRCOLLECT => 22, :QMARK => 23, :LPAREN => 24, :RPAREN => 25, :ISEQUAL => 26, :GREATEREQUAL => 27, :GREATERTHAN => 28, :LESSTHAN => 29, :IF => 30, :ELSE => 31, :IMPORT => 32, :DEFINE => 33, :ELSIF => 34, :VARIABLE => 35, :CLASS => 36, :INHERITS => 37, :NODE => 38, :BOOLEAN => 39, :NAME => 40, :SEMIC => 41, :CASE => 42, :DEFAULT => 43, :AT => 44, :LCOLLECT => 45, :RCOLLECT => 46, :CLASSNAME => 47, :CLASSREF => 48, :NOT => 49, :OR => 50, :AND => 51, :UNDEF => 52, :PARROW => 53, :PLUS => 54, :MINUS => 55, :TIMES => 56, :DIV => 57, :LSHIFT => 58, :RSHIFT => 59, :UMINUS => 60, :MATCH => 61, :NOMATCH => 62, :REGEX => 63, :IN_EDGE => 64, :OUT_EDGE => 65, :IN_EDGE_SUB => 66, :OUT_EDGE_SUB => 67, :IN => 68 } racc_nt_base = 69 racc_use_result_var = true 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", "STRING", "DQPRE", "DQMID", "DQPOST", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "SYMBOL", "FARROW", "COMMA", "TRUE", "FALSE", "EQUALS", "APPENDS", "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", "PLUS", "MINUS", "TIMES", "DIV", "LSHIFT", "RSHIFT", "UMINUS", "MATCH", "NOMATCH", "REGEX", "IN_EDGE", "OUT_EDGE", "IN_EDGE_SUB", "OUT_EDGE_SUB", "IN", "$start", "program", "statements_and_declarations", "nil", "statement_or_declaration", "statements", "resource", "virtualresource", "collection", "assignment", "casestatement", "ifstatement_begin", "import", "fstatement", "definition", "hostclass", "nodedef", "resourceoverride", "append", "relationship", "relationship_side", "edge", "resourceref", "funcvalues", "namestring", "name", "variable", "type", "boolean", "funcrvalue", "selector", "quotedtext", "hasharrayaccesses", "classname", "resourceinstances", "endsemi", "params", "endcomma", "classref", "anyparams", "at", "collectrhand", "collstatements", "collstatement", "colljoin", "collexpr", "colllval", "simplervalue", "resourceinst", "resourcename", "undef", "array", "expression", "hasharrayaccess", "param", "rvalue", "addparam", "anyparam", "rvalues", "comma", "hash", "dqrval", "dqtail", "ifstatement", "else", "regex", "caseopts", "caseopt", "casevalues", "selectlhand", "svalues", "selectval", "sintvalues", "string", "strings", "argumentlist", "classparent", "hostnames", "nodeparent", "nodename", "hostname", "nothing", "arguments", "argument", "classnameordefault", "hashpairs", "hashpair", "key" ] Racc_debug_parser = false ##### State transition tables end ##### # reduce 0 omitted # reduce 1 omitted # reduce 2 omitted module_eval(<<'.,.,', 'grammar.ra', 34) def _reduce_3(val, _values, result) result = ast AST::ASTArray, :children => (val[0] ? [val[0]] : []) result end .,., module_eval(<<'.,.,', 'grammar.ra', 37) def _reduce_4(val, _values, result) if val[1] val[0].push(val[1]) end result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 46) def _reduce_5(val, _values, result) val[0].each do |stmt| if stmt.is_a?(AST::TopLevelConstruct) error "Classes, definitions, and nodes may only appear at toplevel or inside other classes", \ :line => stmt.context[:line], :file => stmt.context[:file] end end result = val[0] result end .,., # 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 # reduce 17 omitted # reduce 18 omitted # reduce 19 omitted module_eval(<<'.,.,', 'grammar.ra', 72) def _reduce_20(val, _values, result) result = AST::Relationship.new(val[0], val[2], val[1][:value], ast_context) result end .,., module_eval(<<'.,.,', 'grammar.ra', 75) def _reduce_21(val, _values, result) result = AST::Relationship.new(val[0], val[2], val[1][:value], ast_context) result end .,., # reduce 22 omitted # reduce 23 omitted # reduce 24 omitted # reduce 25 omitted # reduce 26 omitted # reduce 27 omitted # reduce 28 omitted module_eval(<<'.,.,', 'grammar.ra', 83) def _reduce_29(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 90) def _reduce_30(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 96) def _reduce_31(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 103) def _reduce_32(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[1], :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 110) def _reduce_33(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 111) def _reduce_34(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 113) def _reduce_35(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 117) def _reduce_36(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., # reduce 37 omitted # reduce 38 omitted # reduce 39 omitted # reduce 40 omitted # reduce 41 omitted # reduce 42 omitted # reduce 43 omitted # reduce 44 omitted module_eval(<<'.,.,', 'grammar.ra', 132) def _reduce_45(val, _values, result) result = ast AST::Name, :value => val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 136) def _reduce_46(val, _values, result) @lexer.commentpop result = ast(AST::Resource, :type => val[0], :instances => val[2]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 139) def _reduce_47(val, _values, result) # This is a deprecated syntax. error "All resource specifications require names" result end .,., module_eval(<<'.,.,', 'grammar.ra', 142) def _reduce_48(val, _values, result) # a defaults setting for a type @lexer.commentpop result = ast(AST::ResourceDefaults, :type => val[0], :parameters => val[2]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 149) def _reduce_49(val, _values, result) @lexer.commentpop result = ast AST::ResourceOverride, :object => val[0], :parameters => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 156) def _reduce_50(val, _values, result) type = val[0] - if (type == :exported and ! Puppet[:storeconfigs]) and ! Puppet[:parseonly] + if (type == :exported and ! Puppet[:storeconfigs]) Puppet.warning addcontext("You cannot collect without storeconfigs being set") end error "Defaults are not virtualizable" if val[1].is_a? AST::ResourceDefaults method = type.to_s + "=" # Just mark our resource as exported and pass it through. val[1].send(method, true) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 172) def _reduce_51(val, _values, result) result = :virtual result end .,., module_eval(<<'.,.,', 'grammar.ra', 173) def _reduce_52(val, _values, result) result = :exported result end .,., module_eval(<<'.,.,', 'grammar.ra', 178) def _reduce_53(val, _values, result) @lexer.commentpop Puppet.warning addcontext("Collection names must now be capitalized") if val[0] =~ /^[a-z]/ 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] and ! Puppet[:parseonly] + if args[:form] == :exported and ! Puppet[:storeconfigs] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end args[:override] = val[3] result = ast AST::Collection, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 197) def _reduce_54(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] and ! Puppet[:parseonly] + if args[:form] == :exported and ! Puppet[:storeconfigs] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end result = ast AST::Collection, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 218) def _reduce_55(val, _values, result) if val[1] result = val[1] result.form = :virtual else result = :virtual end result end .,., module_eval(<<'.,.,', 'grammar.ra', 226) def _reduce_56(val, _values, result) if val[1] result = val[1] result.form = :exported else result = :exported end result end .,., # reduce 57 omitted # reduce 58 omitted module_eval(<<'.,.,', 'grammar.ra', 239) def _reduce_59(val, _values, result) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] result end .,., # reduce 60 omitted module_eval(<<'.,.,', 'grammar.ra', 244) def _reduce_61(val, _values, result) result = val[1] result.parens = true result end .,., module_eval(<<'.,.,', 'grammar.ra', 248) def _reduce_62(val, _values, result) result=val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 249) def _reduce_63(val, _values, result) result=val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 252) def _reduce_64(val, _values, result) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., module_eval(<<'.,.,', 'grammar.ra', 257) def _reduce_65(val, _values, result) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., # reduce 66 omitted # reduce 67 omitted module_eval(<<'.,.,', 'grammar.ra', 266) def _reduce_68(val, _values, result) result = ast AST::ResourceInstance, :title => val[0], :parameters => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 269) def _reduce_69(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 271) def _reduce_70(val, _values, result) val[0].push val[2] result = val[0] result end .,., # reduce 71 omitted # reduce 72 omitted module_eval(<<'.,.,', 'grammar.ra', 279) def _reduce_73(val, _values, result) result = ast AST::Undef, :value => :undef result end .,., module_eval(<<'.,.,', 'grammar.ra', 283) def _reduce_74(val, _values, result) result = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 287) def _reduce_75(val, _values, result) result = ast AST::Type, :value => val[0][:value], :line => val[0][:line] result end .,., # reduce 76 omitted # reduce 77 omitted # reduce 78 omitted # reduce 79 omitted # reduce 80 omitted # reduce 81 omitted # reduce 82 omitted module_eval(<<'.,.,', 'grammar.ra', 299) def _reduce_83(val, _values, result) raise Puppet::ParseError, "Cannot assign to variables in other namespaces" if val[0][:value] =~ /::/ # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 305) def _reduce_84(val, _values, result) result = ast AST::VarDef, :name => val[0], :value => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 309) def _reduce_85(val, _values, result) variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :append => true, :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 315) def _reduce_86(val, _values, result) result = ast AST::ASTArray result end .,., module_eval(<<'.,.,', 'grammar.ra', 317) def _reduce_87(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 319) def _reduce_88(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 324) def _reduce_89(val, _values, result) result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 328) def _reduce_90(val, _values, result) result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2], :add => true result end .,., # reduce 91 omitted # reduce 92 omitted module_eval(<<'.,.,', 'grammar.ra', 337) def _reduce_93(val, _values, result) result = ast AST::ASTArray result end .,., module_eval(<<'.,.,', 'grammar.ra', 339) def _reduce_94(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 341) def _reduce_95(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 345) def _reduce_96(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 346) def _reduce_97(val, _values, result) result = val[0].push(val[2]) result end .,., # reduce 98 omitted # reduce 99 omitted # reduce 100 omitted # reduce 101 omitted # reduce 102 omitted # reduce 103 omitted # reduce 104 omitted # reduce 105 omitted # reduce 106 omitted # reduce 107 omitted # reduce 108 omitted # reduce 109 omitted # reduce 110 omitted # reduce 111 omitted # reduce 112 omitted # reduce 113 omitted # reduce 114 omitted # reduce 115 omitted module_eval(<<'.,.,', 'grammar.ra', 370) def _reduce_116(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :rvalue result end .,., module_eval(<<'.,.,', 'grammar.ra', 375) def _reduce_117(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :rvalue result end .,., module_eval(<<'.,.,', 'grammar.ra', 381) def _reduce_118(val, _values, result) result = ast AST::String, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 382) def _reduce_119(val, _values, result) result = ast AST::Concat, :value => [ast(AST::String,val[0])]+val[1], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 384) def _reduce_120(val, _values, result) result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 386) def _reduce_121(val, _values, result) result = [ast(AST::String,val[0])] result end .,., module_eval(<<'.,.,', 'grammar.ra', 387) def _reduce_122(val, _values, result) result = [ast(AST::String,val[0])] + val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 390) def _reduce_123(val, _values, result) result = ast AST::Boolean, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 394) def _reduce_124(val, _values, result) Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0][:value], :line => val[0][:line], :title => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 397) def _reduce_125(val, _values, result) result = ast AST::ResourceReference, :type => val[0], :title => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 401) def _reduce_126(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 405) def _reduce_127(val, _values, result) @lexer.commentpop args = { :test => val[0], :statements => val[2] } args[:else] = val[4] if val[4] result = ast AST::IfStatement, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 416) def _reduce_128(val, _values, result) @lexer.commentpop args = { :test => val[0], :statements => ast(AST::Nop) } args[:else] = val[3] if val[3] result = ast AST::IfStatement, args result end .,., # reduce 129 omitted module_eval(<<'.,.,', 'grammar.ra', 429) def _reduce_130(val, _values, result) result = ast AST::Else, :statements => val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 432) def _reduce_131(val, _values, result) @lexer.commentpop result = ast AST::Else, :statements => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 436) def _reduce_132(val, _values, result) @lexer.commentpop result = ast AST::Else, :statements => ast(AST::Nop) result end .,., # reduce 133 omitted module_eval(<<'.,.,', 'grammar.ra', 454) def _reduce_134(val, _values, result) result = ast AST::InOperator, :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 457) def _reduce_135(val, _values, result) result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 460) def _reduce_136(val, _values, result) result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 463) def _reduce_137(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 466) def _reduce_138(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 469) def _reduce_139(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 472) def _reduce_140(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 475) def _reduce_141(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 478) def _reduce_142(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 481) def _reduce_143(val, _values, result) result = ast AST::Minus, :value => val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 484) def _reduce_144(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 487) def _reduce_145(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 490) def _reduce_146(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 493) def _reduce_147(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 496) def _reduce_148(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 499) def _reduce_149(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 502) def _reduce_150(val, _values, result) result = ast AST::Not, :value => val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 505) def _reduce_151(val, _values, result) result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 508) def _reduce_152(val, _values, result) result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 511) def _reduce_153(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 515) def _reduce_154(val, _values, result) @lexer.commentpop result = ast AST::CaseStatement, :test => val[1], :options => val[3] result end .,., module_eval(<<'.,.,', 'grammar.ra', 519) def _reduce_155(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 521) def _reduce_156(val, _values, result) val[0].push val[1] result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 526) def _reduce_157(val, _values, result) @lexer.commentpop result = ast AST::CaseOpt, :value => val[0], :statements => val[3] result end .,., module_eval(<<'.,.,', 'grammar.ra', 529) def _reduce_158(val, _values, result) @lexer.commentpop result = ast( AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) result end .,., module_eval(<<'.,.,', 'grammar.ra', 539) def _reduce_159(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 541) def _reduce_160(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 546) def _reduce_161(val, _values, result) result = ast AST::Selector, :param => val[0], :values => val[2] result end .,., # reduce 162 omitted module_eval(<<'.,.,', 'grammar.ra', 551) def _reduce_163(val, _values, result) @lexer.commentpop result = val[1] result end .,., # reduce 164 omitted module_eval(<<'.,.,', 'grammar.ra', 557) def _reduce_165(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', 566) def _reduce_166(val, _values, result) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., # reduce 167 omitted # reduce 168 omitted # reduce 169 omitted # reduce 170 omitted # reduce 171 omitted # reduce 172 omitted # reduce 173 omitted # reduce 174 omitted module_eval(<<'.,.,', 'grammar.ra', 578) def _reduce_175(val, _values, result) result = ast AST::Default, :value => val[0][:value], :line => val[0][:line] result end .,., # reduce 176 omitted module_eval(<<'.,.,', 'grammar.ra', 583) def _reduce_177(val, _values, result) result = [val[0][:value]] result end .,., # reduce 178 omitted module_eval(<<'.,.,', 'grammar.ra', 585) def _reduce_179(val, _values, result) result = val[0] += val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 588) def _reduce_180(val, _values, result) val[1].each do |file| import(file) end result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 598) def _reduce_181(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Definition.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :code => val[4], :line => val[0][:line])) @lexer.indefine = false #} | DEFINE NAME argumentlist parent LBRACE RBRACE { result end .,., module_eval(<<'.,.,', 'grammar.ra', 606) def _reduce_182(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Definition.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :line => val[0][:line])) @lexer.indefine = false result end .,., module_eval(<<'.,.,', 'grammar.ra', 614) def _reduce_183(val, _values, result) @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop result = Puppet::Parser::AST::Hostclass.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :parent => val[3], :code => val[5], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 621) def _reduce_184(val, _values, result) @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop result = Puppet::Parser::AST::Hostclass.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :parent => val[3], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 630) def _reduce_185(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Node.new(val[1], ast_context(true).merge(:parent => val[2], :code => val[4], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 635) def _reduce_186(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Node.new(val[1], ast_context(true).merge(:parent => val[2], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 639) def _reduce_187(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 641) def _reduce_188(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 642) def _reduce_189(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 643) def _reduce_190(val, _values, result) result = "class" result end .,., module_eval(<<'.,.,', 'grammar.ra', 648) def _reduce_191(val, _values, result) result = [result] result end .,., module_eval(<<'.,.,', 'grammar.ra', 651) def _reduce_192(val, _values, result) result = val[0] result << val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 656) def _reduce_193(val, _values, result) result = ast AST::HostName, :value => val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 659) def _reduce_194(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 660) def _reduce_195(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 661) def _reduce_196(val, _values, result) result = val[0][:value] result end .,., # reduce 197 omitted module_eval(<<'.,.,', 'grammar.ra', 665) def _reduce_198(val, _values, result) result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 669) def _reduce_199(val, _values, result) result = ast AST::ASTArray, :children => [] result end .,., # reduce 200 omitted module_eval(<<'.,.,', 'grammar.ra', 674) def _reduce_201(val, _values, result) result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 677) def _reduce_202(val, _values, result) result = val[1] result = [result] unless result[0].is_a?(Array) result end .,., # reduce 203 omitted module_eval(<<'.,.,', 'grammar.ra', 683) def _reduce_204(val, _values, result) result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 689) def _reduce_205(val, _values, result) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value], val[2]] result end .,., module_eval(<<'.,.,', 'grammar.ra', 693) def _reduce_206(val, _values, result) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value]] result end .,., module_eval(<<'.,.,', 'grammar.ra', 696) def _reduce_207(val, _values, result) result = [val[0][:value], val[2]] result end .,., module_eval(<<'.,.,', 'grammar.ra', 698) def _reduce_208(val, _values, result) result = [val[0][:value]] result end .,., # reduce 209 omitted module_eval(<<'.,.,', 'grammar.ra', 703) def _reduce_210(val, _values, result) result = val[1] result end .,., # reduce 211 omitted module_eval(<<'.,.,', 'grammar.ra', 708) def _reduce_212(val, _values, result) result = val[1] result end .,., # reduce 213 omitted # reduce 214 omitted module_eval(<<'.,.,', 'grammar.ra', 714) def _reduce_215(val, _values, result) result = ast AST::Variable, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 717) def _reduce_216(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 718) def _reduce_217(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 719) def _reduce_218(val, _values, result) result = ast AST::ASTArray result end .,., # reduce 219 omitted # reduce 220 omitted # reduce 221 omitted module_eval(<<'.,.,', 'grammar.ra', 725) def _reduce_222(val, _values, result) result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 728) def _reduce_223(val, _values, result) result = ast AST::Regex, :value => val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 732) def _reduce_224(val, _values, result) if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end result end .,., module_eval(<<'.,.,', 'grammar.ra', 739) def _reduce_225(val, _values, result) if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end result end .,., module_eval(<<'.,.,', 'grammar.ra', 745) def _reduce_226(val, _values, result) result = ast AST::ASTHash result end .,., # reduce 227 omitted module_eval(<<'.,.,', 'grammar.ra', 750) def _reduce_228(val, _values, result) if val[0].instance_of?(AST::ASTHash) result = val[0].merge(val[2]) else result = ast AST::ASTHash, :value => val[0] result.merge(val[2]) end result end .,., module_eval(<<'.,.,', 'grammar.ra', 759) def _reduce_229(val, _values, result) result = ast AST::ASTHash, { :value => { val[0] => val[2] } } result end .,., module_eval(<<'.,.,', 'grammar.ra', 762) def _reduce_230(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 763) def _reduce_231(val, _values, result) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 766) def _reduce_232(val, _values, result) result = ast AST::HashOrArrayAccess, :variable => val[0][:value], :key => val[2] result end .,., # reduce 233 omitted module_eval(<<'.,.,', 'grammar.ra', 771) def _reduce_234(val, _values, result) result = ast AST::HashOrArrayAccess, :variable => val[0], :key => val[2] result end .,., def _reduce_none(val, _values, result) val[0] end end # class Parser end # module Parser end # module Puppet diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index c007d4dbe..ace01bb4b 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -1,326 +1,326 @@ require 'puppet/resource' # The primary difference between this class and its # parent is that this class has rules on who can set # parameters class Puppet::Parser::Resource < Puppet::Resource require 'puppet/parser/resource/param' require 'puppet/util/tagging' require 'puppet/file_collection/lookup' require 'puppet/parser/yaml_trimmer' require 'puppet/resource/type_collection_helper' include Puppet::FileCollection::Lookup include Puppet::Resource::TypeCollectionHelper include Puppet::Util include Puppet::Util::MethodHelper include Puppet::Util::Errors include Puppet::Util::Logging include Puppet::Util::Tagging include Puppet::Parser::YamlTrimmer attr_accessor :source, :scope, :rails_id attr_accessor :virtual, :override, :translated, :catalog, :evaluated attr_reader :exported, :parameters # Determine whether the provided parameter name is a relationship parameter. def self.relationship_parameter?(name) @relationship_names ||= Puppet::Type.relationship_params.collect { |p| p.name } @relationship_names.include?(name) end # Set up some boolean test methods def translated?; !!@translated; end def override?; !!@override; end def evaluated?; !!@evaluated; end def [](param) param = symbolize(param) if param == :title return self.title end if @parameters.has_key?(param) @parameters[param].value else nil end end def []=(param, value) set_parameter(param, value) end def eachparam @parameters.each do |name, param| yield param end end def environment scope.environment end # Retrieve the associated definition and evaluate it. def evaluate return if evaluated? @evaluated = true if klass = resource_type and ! builtin_type? finish return klass.evaluate_code(self) elsif builtin? devfail "Cannot evaluate a builtin type (#{type})" else self.fail "Cannot find definition #{type}" end end # Mark this resource as both exported and virtual, # or remove the exported mark. def exported=(value) if value @virtual = true @exported = value else @exported = value end end # Do any finishing work on this object, called before evaluation or # before storage/translation. def finish return if finished? @finished = true add_defaults add_metaparams add_scope_tags validate end # Has this resource already been finished? def finished? @finished end def initialize(*args) raise ArgumentError, "Resources require a scope" unless args.last[:scope] super @source ||= scope.source end # Is this resource modeling an isomorphic resource type? def isomorphic? if builtin_type? return resource_type.isomorphic? else return true end end # Merge an override resource in. This will throw exceptions if # any overrides aren't allowed. def merge(resource) # Test the resource scope, to make sure the resource is even allowed # to override. unless self.source.object_id == resource.source.object_id || resource.source.child_of?(self.source) raise Puppet::ParseError.new("Only subclasses can override parameters", resource.line, resource.file) end # Some of these might fail, but they'll fail in the way we want. resource.parameters.each do |name, param| override_parameter(param) end end # Unless we're running >= 0.25, we're in compat mode. def metaparam_compatibility_mode? ! (catalog and ver = (catalog.client_version||'0.0.0').split(".") and (ver[0] > "0" or ver[1].to_i >= 25)) end def name self[:name] || self.title end # A temporary occasion, until I get paths in the scopes figured out. def path to_s end # Define a parameter in our resource. # if we ever receive a parameter named 'tag', set # the resource tags with its value. def set_parameter(param, value = nil) if ! value.nil? param = Puppet::Parser::Resource::Param.new( :name => param, :value => value, :source => self.source ) elsif ! param.is_a?(Puppet::Parser::Resource::Param) raise ArgumentError, "Must pass a parameter or all necessary values" end tag(*param.value) if param.name == :tag # And store it in our parameter hash. @parameters[param.name] = param end def to_hash @parameters.inject({}) do |hash, ary| param = ary[1] # Skip "undef" values. hash[param.name] = param.value if param.value != :undef hash end end # Create a Puppet::Resource instance from this parser resource. # We plan, at some point, on not needing to do this conversion, but # it's sufficient for now. def to_resource result = Puppet::Resource.new(type, title) to_hash.each do |p, v| if v.is_a?(Puppet::Resource) v = Puppet::Resource.new(v.type, v.title) elsif v.is_a?(Array) # flatten resource references arrays v = v.flatten if v.flatten.find { |av| av.is_a?(Puppet::Resource) } v = v.collect do |av| av = Puppet::Resource.new(av.type, av.title) if av.is_a?(Puppet::Resource) av end end # If the value is an array with only one value, then # convert it to a single value. This is largely so that # the database interaction doesn't have to worry about # whether it returns an array or a string. result[p] = if v.is_a?(Array) and v.length == 1 v[0] else v end end result.file = self.file result.line = self.line result.exported = self.exported result.virtual = self.virtual result.tag(*self.tags) result end # Translate our object to a transportable object. def to_trans return nil if virtual? to_resource.to_trans end # Convert this resource to a RAL resource. We hackishly go via the # transportable stuff. def to_ral to_resource.to_ral end private # Add default values from our definition. def add_defaults scope.lookupdefaults(self.type).each do |name, param| unless @parameters.include?(name) self.debug "Adding default for #{name}" @parameters[name] = param.dup end end end def add_backward_compatible_relationship_param(name) # Skip metaparams for which we get no value. - return unless val = scope.lookupvar(name.to_s, false) and val != :undefined + return unless val = scope.lookupvar(name.to_s) and val != :undefined # The default case: just set the value set_parameter(name, val) and return unless @parameters[name] # For relationship params, though, join the values (a la #446). @parameters[name].value = [@parameters[name].value, val].flatten end # Add any metaparams defined in our scope. This actually adds any metaparams # from any parent scope, and there's currently no way to turn that off. def add_metaparams compat_mode = metaparam_compatibility_mode? Puppet::Type.eachmetaparam do |name| next unless self.class.relationship_parameter?(name) add_backward_compatible_relationship_param(name) if compat_mode end end def add_scope_tags if scope_resource = scope.resource tag(*scope_resource.tags) end end # Accept a parameter from an override. def override_parameter(param) # This can happen if the override is defining a new parameter, rather # than replacing an existing one. (set_parameter(param) and return) unless current = @parameters[param.name] # The parameter is already set. Fail if they're not allowed to override it. unless param.source.child_of?(current.source) puts caller if Puppet[:trace] msg = "Parameter '#{param.name}' is already set on #{self}" msg += " by #{current.source}" if current.source.to_s != "" if current.file or current.line fields = [] fields << current.file if current.file fields << current.line.to_s if current.line msg += " at #{fields.join(":")}" end msg += "; cannot redefine" raise Puppet::ParseError.new(msg, param.line, param.file) end # If we've gotten this far, we're allowed to override. # Merge with previous value, if the parameter was generated with the +> # syntax. It's important that we use a copy of the new param instance # here, not the old one, and not the original new one, so that the source # is registered correctly for later overrides but the values aren't # implcitly shared when multiple resources are overrriden at once (see # ticket #3556). if param.add param = param.dup param.value = [current.value, param.value].flatten end set_parameter(param) end # Make sure the resource's parameters are all valid for the type. def validate @parameters.each do |name, param| validate_parameter(name) end rescue => detail fail Puppet::ParseError, detail.to_s end private def extract_parameters(params) params.each do |param| # Don't set the same parameter twice self.fail Puppet::ParseError, "Duplicate parameter '#{param.name}' for on #{self}" if @parameters[param.name] set_parameter(param) end end end diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 24f1d01f7..8de9d60b1 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -1,518 +1,448 @@ # The scope class, which handles storing and retrieving variables and types and # such. require 'puppet/parser/parser' require 'puppet/parser/templatewrapper' require 'puppet/transportable' require 'strscan' require 'puppet/resource/type_collection_helper' class Puppet::Parser::Scope include Puppet::Resource::TypeCollectionHelper require 'puppet/parser/resource' AST = Puppet::Parser::AST Puppet::Util.logmethods(self) include Enumerable include Puppet::Util::Errors - attr_accessor :level, :source, :resource + attr_accessor :source, :resource attr_accessor :base, :keyword attr_accessor :top, :translated, :compiler - attr_accessor :parent + attr_accessor :parent, :dynamic attr_reader :namespaces # thin wrapper around an ephemeral # symbol table. # when a symbol class Ephemeral def initialize(parent=nil) @symbols = {} @parent = parent end [:include?, :delete, :[]=].each do |m| define_method(m) do |*args| @symbols.send(m, *args) end end def [](name) unless @symbols.include?(name) or @parent.nil? @parent[name] else @symbols[name] end end end # A demeterific shortcut to the catalog. def catalog compiler.catalog end def environment compiler.environment end # Proxy accessors def host @compiler.node.name end # Is the value true? This allows us to control the definition of truth # in one place. def self.true?(value) (value != false and value != "" and value != :undef) end # Is the value a number?, return the correct object or nil if not a number def self.number?(value) return nil unless value.is_a?(Fixnum) or value.is_a?(Bignum) or value.is_a?(Float) or value.is_a?(String) if value.is_a?(String) if value =~ /^-?\d+(:?\.\d+|(:?\.\d+)?e\d+)$/ return value.to_f elsif value =~ /^0x[0-9a-f]+$/i return value.to_i(16) elsif value =~ /^0[0-7]+$/ return value.to_i(8) elsif value =~ /^-?\d+$/ return value.to_i else return nil end end # it is one of Fixnum,Bignum or Float value end # Add to our list of namespaces. def add_namespace(ns) return false if @namespaces.include?(ns) if @namespaces == [""] @namespaces = [ns] else @namespaces << ns end end # Remove this when rebasing def environment compiler.environment end - # Are we the top scope? - def topscope? - @level == 1 - end - def find_hostclass(name) known_resource_types.find_hostclass(namespaces, name) end def find_definition(name) known_resource_types.find_definition(namespaces, name) end def findresource(string, name = nil) compiler.findresource(string, name) end # Initialize our new scope. Defaults to having no parent. def initialize(hash = {}) if hash.include?(:namespace) if n = hash[:namespace] @namespaces = [n] end hash.delete(:namespace) else @namespaces = [""] end hash.each { |name, val| method = name.to_s + "=" if self.respond_to? method self.send(method, val) else raise Puppet::DevError, "Invalid scope argument #{name}" end } extend_with_functions_module @tags = [] # The symbol table for this scope. This is where we store variables. @symtable = {} # the ephemeral symbol tables # those should not persist long, and are used for the moment only # for $0..$xy capture variables of regexes # this is actually implemented as a stack, with each ephemeral scope # shadowing the previous one @ephemeral = [ Ephemeral.new ] # All of the defaults set for types. It's a hash of hashes, # with the first key being the type, then the second key being # the parameter. @defaults = Hash.new { |dhash,type| dhash[type] = {} } # The table for storing class singletons. This will only actually # be used by top scopes and node scopes. @class_scopes = {} end # Store the fact that we've evaluated a class, and store a reference to # the scope in which it was evaluated, so that we can look it up later. def class_set(name, scope) return parent.class_set(name,scope) if parent @class_scopes[name] = scope end # Return the scope associated with a class. This is just here so # that subclasses can set their parent scopes to be the scope of # their parent class, and it's also used when looking up qualified # variables. def class_scope(klass) # They might pass in either the class or class name k = klass.respond_to?(:name) ? klass.name : klass @class_scopes[k] || (parent && parent.class_scope(k)) end # Collect all of the defaults set at any higher scopes. # This is a different type of lookup because it's additive -- # it collects all of the defaults, with defaults in closer scopes # overriding those in later scopes. def lookupdefaults(type) values = {} # first collect the values from the parents unless parent.nil? parent.lookupdefaults(type).each { |var,value| values[var] = value } end # then override them with any current values # this should probably be done differently if @defaults.include?(type) @defaults[type].each { |var,value| values[var] = value } end #Puppet.debug "Got defaults for %s: %s" % # [type,values.inspect] values end # Look up a defined type. def lookuptype(name) find_definition(name) || find_hostclass(name) end - def lookup_qualified_var(name, usestring) - parts = name.split(/::/) - shortname = parts.pop - klassname = parts.join("::") - klass = find_hostclass(klassname) - unless klass - warning "Could not look up qualified variable '#{name}'; class #{klassname} could not be found" - return usestring ? "" : :undefined - end - unless kscope = class_scope(klass) - warning "Could not look up qualified variable '#{name}'; class #{klassname} has not been evaluated" - return usestring ? "" : :undefined - end - kscope.lookupvar(shortname, usestring) + def undef_as(x,v) + (v == :undefined) ? x : (v == :undef) ? x : v end - private :lookup_qualified_var + def qualified_scope(classname) + raise "class #{classname} could not be found" unless klass = find_hostclass(classname) + raise "class #{classname} has not been evaluated" unless kscope = class_scope(klass) + kscope + end + + private :qualified_scope - # Look up a variable. The simplest value search we do. Default to returning - # an empty string for missing values, but support returning a constant. - def lookupvar(name, usestring = true) + # Look up a variable. The simplest value search we do. + def lookupvar(name, options = {}) table = ephemeral?(name) ? @ephemeral.last : @symtable # If the variable is qualified, then find the specified scope and look the variable up there instead. - if name =~ /::/ - return lookup_qualified_var(name, usestring) - end - # We can't use "if table[name]" here because the value might be false - if ephemeral_include?(name) or table.include?(name) - if usestring and table[name] == :undef - return "" - else - return table[name] + if name =~ /^(.*)::(.+)$/ + begin + qualified_scope($1).lookupvar($2,options) + rescue RuntimeError => e + location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : '' + warning "Could not look up qualified variable '#{name}'; #{e.message}#{location}" + :undefined end - elsif self.parent - return parent.lookupvar(name, usestring) - elsif usestring - return "" + elsif ephemeral_include?(name) or table.include?(name) + # We can't use "if table[name]" here because the value might be false + if options[:dynamic] and self != compiler.topscope + location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : '' + Puppet.deprecation_warning "Dynamic lookup of $#{name}#{location} will not be supported in future versions. Use a fully-qualified variable name or parameterized classes." + end + table[name] + elsif parent + parent.lookupvar(name,options.merge(:dynamic => (dynamic || options[:dynamic]))) else - return :undefined + :undefined end end # Return a hash containing our variables and their values, optionally (and # by default) including the values defined in our parent. Local values # shadow parent values. def to_hash(recursive = true) target = parent.to_hash(recursive) if recursive and parent target ||= Hash.new @symtable.keys.each { |name| value = @symtable[name] if value == :undef target.delete(name) else target[name] = value end } target end def namespaces @namespaces.dup end # Create a new scope and set these options. def newscope(options = {}) compiler.newscope(self, options) end def parent_module_name return nil unless @parent return nil unless @parent.source @parent.source.module_name end # Return the list of scopes up to the top scope, ordered with our own first. # This is used for looking up variables and defaults. def scope_path if parent [self, parent.scope_path].flatten.compact else [self] end end # Set defaults for a type. The typename should already be downcased, # so that the syntax is isolated. We don't do any kind of type-checking # here; instead we let the resource do it when the defaults are used. def setdefaults(type, params) table = @defaults[type] # if we got a single param, it'll be in its own array params = [params] unless params.is_a?(Array) params.each { |param| #Puppet.debug "Default for %s is %s => %s" % # [type,ary[0].inspect,ary[1].inspect] if table.include?(param.name) raise Puppet::ParseError.new("Default already defined for #{type} { #{param.name} }; cannot redefine", param.line, param.file) end table[param.name] = param } end # Set a variable in the current scope. This will override settings # in scopes above, but will not allow variables in the current scope # to be reassigned. def setvar(name,value, options = {}) table = options[:ephemeral] ? @ephemeral.last : @symtable - #Puppet.debug "Setting %s to '%s' at level %s mode append %s" % - # [name.inspect,value,self.level, append] if table.include?(name) unless options[:append] error = Puppet::ParseError.new("Cannot reassign variable #{name}") else error = Puppet::ParseError.new("Cannot append, variable #{name} is defined in this scope") end error.file = options[:file] if options[:file] error.line = options[:line] if options[:line] raise error end unless options[:append] table[name] = value else # append case # lookup the value in the scope if it exists and insert the var - table[name] = lookupvar(name) + table[name] = undef_as('',lookupvar(name)) # concatenate if string, append if array, nothing for other types case value when Array table[name] += value when Hash raise ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported" unless value.is_a?(Hash) table[name].merge!(value) else table[name] << value end end end - # Return an interpolated string. - def strinterp(string, file = nil, line = nil) - # Most strings won't have variables in them. - ss = StringScanner.new(string) - out = "" - while not ss.eos? - if ss.scan(/^\$\{((\w*::)*\w+|[0-9]+)\}|^\$([0-9])|^\$((\w*::)*\w+)/) - # If it matches the backslash, then just retun the dollar sign. - if ss.matched == '\\$' - out << '$' - else # look the variable up - # make sure $0-$9 are lookupable only if ephemeral - var = ss[1] || ss[3] || ss[4] - if var and var =~ /^[0-9]+$/ and not ephemeral_include?(var) - next - end - out << lookupvar(var).to_s || "" - end - elsif ss.scan(/^\\(.)/) - # Puppet.debug("Got escape: pos:%d; m:%s" % [ss.pos, ss.matched]) - case ss[1] - when 'n' - out << "\n" - when 't' - out << "\t" - when 's' - out << " " - when '\\' - out << '\\' - when '$' - out << '$' - else - str = "Unrecognised escape sequence '#{ss.matched}'" - str += " in file #{file}" if file - str += " at line #{line}" if line - Puppet.warning str - out << ss.matched - end - elsif ss.scan(/^\$/) - out << '$' - elsif ss.scan(/^\\\n/) # an escaped carriage return - next - else - tmp = ss.scan(/[^\\$]+/) - # Puppet.debug("Got other: pos:%d; m:%s" % [ss.pos, tmp]) - unless tmp - error = Puppet::ParseError.new("Could not parse string #{string.inspect}") - {:file= => file, :line= => line}.each do |m,v| - error.send(m, v) if v - end - raise error - end - out << tmp - end - end - - out - end - # Return the tags associated with this scope. It's basically # just our parents' tags, plus our type. We don't cache this value # because our parent tags might change between calls. def tags resource.tags end # Used mainly for logging def to_s "Scope(#{@resource})" end # Undefine a variable; only used for testing. def unsetvar(var) table = ephemeral?(var) ? @ephemeral.last : @symtable table.delete(var) if table.include?(var) end # remove ephemeral scope up to level def unset_ephemeral_var(level=:all) if level == :all @ephemeral = [ Ephemeral.new ] else (@ephemeral.size - level).times do @ephemeral.pop end end end # check if name exists in one of the ephemeral scope. def ephemeral_include?(name) @ephemeral.reverse.each do |eph| return true if eph.include?(name) end false end # is name an ephemeral variable? def ephemeral?(name) name =~ /^\d+$/ end def ephemeral_level @ephemeral.size end def new_ephemeral @ephemeral.push(Ephemeral.new(@ephemeral.last)) end def ephemeral_from(match, file = nil, line = nil) raise(ArgumentError,"Invalid regex match data") unless match.is_a?(MatchData) new_ephemeral setvar("0", match[0], :file => file, :line => line, :ephemeral => true) match.captures.each_with_index do |m,i| setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true) end end def find_resource_type(type) # It still works fine without the type == 'class' short-cut, but it is a lot slower. return nil if ["class", "node"].include? type.to_s.downcase find_builtin_resource_type(type) || find_defined_resource_type(type) end def find_builtin_resource_type(type) Puppet::Type.type(type.to_s.downcase.to_sym) end def find_defined_resource_type(type) environment.known_resource_types.find_definition(namespaces, type.to_s.downcase) end def resolve_type_and_titles(type, titles) raise ArgumentError, "titles must be an array" unless titles.is_a?(Array) case type.downcase when "class" # resolve the titles titles = titles.collect do |a_title| hostclass = find_hostclass(a_title) hostclass ? hostclass.name : a_title end when "node" # no-op else # resolve the type resource_type = find_resource_type(type) type = resource_type.name if resource_type end return [type, titles] end private def extend_with_functions_module extend Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.root) extend Puppet::Parser::Functions.environment_module(compiler ? environment : nil) end end diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 6864aa1a9..180a03dc9 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -1,115 +1,115 @@ # A simple wrapper for templates, so they don't have full access to # the scope objects. require 'puppet/parser/files' require 'erb' class Puppet::Parser::TemplateWrapper attr_writer :scope attr_reader :file attr_accessor :string include Puppet::Util Puppet::Util.logmethods(self) def initialize(scope) @__scope__ = scope end def scope @__scope__ end + def script_line + # find which line in the template (if any) we were called from + caller.find { |l| l =~ /#{file}:/ }.first[/:(\d+):/,1] + end + # Should return true if a variable is defined, false if it is not def has_variable?(name) - if scope.lookupvar(name.to_s, false) != :undefined - true - else - false - end + scope.lookupvar(name.to_s, :file => file, :line => script_line) != :undefined end # Allow templates to access the defined classes def classes scope.catalog.classes end # Allow templates to access the tags defined in the current scope def tags scope.tags end # Allow templates to access the all the defined tags def all_tags scope.catalog.tags end # Ruby treats variables like methods, so we used to expose variables # within scope to the ERB code via method_missing. As per RedMine #1427, # though, this means that conflicts between methods in our inheritance # tree (Kernel#fork) and variable names (fork => "yes/no") could arise. # # Worse, /new/ conflicts could pop up when a new kernel or object method # was added to Ruby, causing templates to suddenly fail mysteriously when # Ruby was upgraded. # # To ensure that legacy templates using unqualified names work we retain # the missing_method definition here until we declare the syntax finally # dead. def method_missing(name, *args) - # We have to tell lookupvar to return :undefined to us when - # appropriate; otherwise it converts to "". - value = scope.lookupvar(name.to_s, false) + value = scope.lookupvar(name.to_s,:file => file,:line => script_line) if value != :undefined return value else # Just throw an error immediately, instead of searching for # other missingmethod things or whatever. - raise Puppet::ParseError, "Could not find value for '#{name}'" + raise Puppet::ParseError.new("Could not find value for '#{name}'",@file,script_line) end end def file=(filename) unless @file = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s) raise Puppet::ParseError, "Could not find template '#{filename}'" end # We'll only ever not have a parser in testing, but, eh. scope.known_resource_types.watch_file(file) @string = File.read(file) end def result(string = nil) if string self.string = string template_source = "inline template" else template_source = file end # Expose all the variables in our scope as instance variables of the # current object, making it possible to access them without conflict # to the regular methods. benchmark(:debug, "Bound template variables for #{template_source}") do scope.to_hash.each { |name, value| if name.kind_of?(String) realname = name.gsub(/[^\w]/, "_") else realname = name end instance_variable_set("@#{realname}", value) } end result = nil benchmark(:debug, "Interpolated template #{template_source}") do template = ERB.new(self.string, 0, "-") + template.filename = file result = template.result(binding) end result end def to_s "template[#{(file ? file : "inline")}]" end end diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index 5488c6674..a16f54bd7 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -1,387 +1,383 @@ -#-- -# Copyright (C) 2008 Red Hat Inc. # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. +# Copyright 2011 Bryan Kearney # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# You should have received a copy of the GNU General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# http://www.apache.org/licenses/LICENSE-2.0 # -# Author: Bryan Kearney +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. require 'augeas' if Puppet.features.augeas? require 'strscan' Puppet::Type.type(:augeas).provide(:augeas) do include Puppet::Util confine :true => Puppet.features.augeas? has_features :parse_commands, :need_to_run?,:execute_changes SAVE_NOOP = "noop" SAVE_OVERWRITE = "overwrite" COMMANDS = { "set" => [ :path, :string ], "setm" => [ :path, :string, :string ], "rm" => [ :path ], "clear" => [ :path ], "mv" => [ :path, :path ], "insert" => [ :string, :string, :path ], "get" => [ :path, :comparator, :string ], "defvar" => [ :string, :path ], "defnode" => [ :string, :path, :string ], "match" => [ :path, :glob ], "size" => [:comparator, :int], "include" => [:string], "not_include" => [:string], "==" => [:glob], "!=" => [:glob] } COMMANDS["ins"] = COMMANDS["insert"] COMMANDS["remove"] = COMMANDS["rm"] COMMANDS["move"] = COMMANDS["mv"] attr_accessor :aug # Extracts an 2 dimensional array of commands which are in the # form of command path value. # The input can be # - A string with one command # - A string with many commands per line # - An array of strings. def parse_commands(data) context = resource[:context] # Add a trailing / if it is not there if (context.length > 0) context << "/" if context[-1, 1] != "/" end data = data.split($/) if data.is_a?(String) data = data.flatten args = [] data.each do |line| line.strip! next if line.nil? || line.empty? argline = [] sc = StringScanner.new(line) cmd = sc.scan(/\w+|==|!=/) formals = COMMANDS[cmd] fail("Unknown command #{cmd}") unless formals argline << cmd narg = 0 formals.each do |f| sc.skip(/\s+/) narg += 1 if f == :path start = sc.pos nbracket = 0 inSingleTick = false inDoubleTick = false begin sc.skip(/([^\]\[\s\\'"]|\\.)+/) ch = sc.getch nbracket += 1 if ch == "[" nbracket -= 1 if ch == "]" inSingleTick = !inSingleTick if ch == "'" inDoubleTick = !inDoubleTick if ch == "\"" fail("unmatched [") if nbracket < 0 end until ((nbracket == 0 && !inSingleTick && !inDoubleTick && (ch =~ /\s/)) || sc.eos?) len = sc.pos - start len -= 1 unless sc.eos? unless p = sc.string[start, len] fail("missing path argument #{narg} for #{cmd}") end # Rip off any ticks if they are there. p = p[1, (p.size - 2)] if p[0,1] == "'" || p[0,1] == "\"" p.chomp!("/") if p[0,1] != '$' && p[0,1] != "/" argline << context + p else argline << p end elsif f == :string delim = sc.peek(1) if delim == "'" || delim == "\"" sc.getch argline << sc.scan(/([^\\#{delim}]|(\\.))*/) sc.getch else argline << sc.scan(/[^\s]+/) end fail("missing string argument #{narg} for #{cmd}") unless argline[-1] elsif f == :comparator argline << sc.scan(/(==|!=|=~|<|<=|>|>=)/) unless argline[-1] puts sc.rest fail("invalid comparator for command #{cmd}") end elsif f == :int argline << sc.scan(/\d+/).to_i elsif f== :glob argline << sc.rest end end args << argline end args end def open_augeas unless @aug flags = Augeas::NONE flags = Augeas::TYPE_CHECK if resource[:type_check] == :true flags |= Augeas::NO_MODL_AUTOLOAD if resource[:incl] root = resource[:root] load_path = resource[:load_path] debug("Opening augeas with root #{root}, lens path #{load_path}, flags #{flags}") @aug = Augeas::open(root, load_path,flags) debug("Augeas version #{get_augeas_version} is installed") if get_augeas_version >= "0.3.6" if resource[:incl] aug.set("/augeas/load/Xfm/lens", resource[:lens]) aug.set("/augeas/load/Xfm/incl", resource[:incl]) aug.load end end @aug end def close_augeas if @aug @aug.close debug("Closed the augeas connection") @aug = nil end end # Used by the need_to_run? method to process get filters. Returns # true if there is a match, false if otherwise # Assumes a syntax of get /files/path [COMPARATOR] value def process_get(cmd_array) return_value = false #validate and tear apart the command fail ("Invalid command: #{cmd_array.join(" ")}") if cmd_array.length < 4 cmd = cmd_array.shift path = cmd_array.shift comparator = cmd_array.shift arg = cmd_array.join(" ") #check the value in augeas result = @aug.get(path) || '' case comparator when "!=" return_value = (result != arg) when "=~" regex = Regexp.new(arg) return_value = (result =~ regex) else return_value = (result.send(comparator, arg)) end !!return_value end # Used by the need_to_run? method to process match filters. Returns # true if there is a match, false if otherwise def process_match(cmd_array) return_value = false #validate and tear apart the command fail("Invalid command: #{cmd_array.join(" ")}") if cmd_array.length < 3 cmd = cmd_array.shift path = cmd_array.shift # Need to break apart the clause clause_array = parse_commands(cmd_array.shift)[0] verb = clause_array.shift #Get the values from augeas result = @aug.match(path) || [] fail("Error trying to match path '#{path}'") if (result == -1) # Now do the work case verb when "size" fail("Invalid command: #{cmd_array.join(" ")}") if clause_array.length != 2 comparator = clause_array.shift arg = clause_array.shift case comparator when "!=" return_value = !(result.size.send(:==, arg)) else return_value = (result.size.send(comparator, arg)) end when "include" arg = clause_array.shift return_value = result.include?(arg) when "not_include" arg = clause_array.shift return_value = !result.include?(arg) when "==" begin arg = clause_array.shift new_array = eval arg return_value = (result == new_array) rescue fail("Invalid array in command: #{cmd_array.join(" ")}") end when "!=" begin arg = clause_array.shift new_array = eval arg return_value = (result != new_array) rescue fail("Invalid array in command: #{cmd_array.join(" ")}") end end !!return_value end def get_augeas_version @aug.get("/augeas/version") || "" end def set_augeas_save_mode(mode) @aug.set("/augeas/save", mode) end def files_changed? saved_files = @aug.match("/augeas/events/saved") saved_files.size > 0 end # Determines if augeas acutally needs to run. def need_to_run? force = resource[:force] return_value = true begin open_augeas filter = resource[:onlyif] unless filter == "" cmd_array = parse_commands(filter)[0] command = cmd_array[0]; begin case command when "get"; return_value = process_get(cmd_array) when "match"; return_value = process_match(cmd_array) end rescue SystemExit,NoMemoryError raise rescue Exception => e fail("Error sending command '#{command}' with params #{cmd_array[1..-1].inspect}/#{e.message}") end end unless force # If we have a verison of augeas which is at least 0.3.6 then we # can make the changes now, see if changes were made, and # actually do the save. if return_value and get_augeas_version >= "0.3.6" debug("Will attempt to save and only run if files changed") set_augeas_save_mode(SAVE_NOOP) do_execute_changes save_result = @aug.save saved_files = @aug.match("/augeas/events/saved") if save_result and not files_changed? debug("Skipping because no files were changed") return_value = false else debug("Files changed, should execute") end end end ensure close_augeas end return_value end def execute_changes # Re-connect to augeas, and re-execute the changes begin open_augeas set_augeas_save_mode(SAVE_OVERWRITE) if get_augeas_version >= "0.3.6" do_execute_changes success = @aug.save fail("Save failed with return code #{success}") if success != true ensure close_augeas end :executed end # Actually execute the augeas changes. def do_execute_changes commands = parse_commands(resource[:changes]) commands.each do |cmd_array| fail("invalid command #{cmd_array.join[" "]}") if cmd_array.length < 2 command = cmd_array[0] cmd_array.shift begin case command when "set" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.set(cmd_array[0], cmd_array[1]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) when "setm" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.setm(cmd_array[0], cmd_array[1], cmd_array[2]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) when "rm", "remove" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.rm(cmd_array[0]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) when "clear" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.clear(cmd_array[0]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) when "insert", "ins" label = cmd_array[0] where = cmd_array[1] path = cmd_array[2] case where when "before"; before = true when "after"; before = false else fail("Invalid value '#{where}' for where param") end debug("sending command '#{command}' with params #{[label, where, path].inspect}") rv = aug.insert(path, label, before) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) when "defvar" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.defvar(cmd_array[0], cmd_array[1]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) when "defnode" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.defnode(cmd_array[0], cmd_array[1], cmd_array[2]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (!rv) when "mv", "move" debug("sending command '#{command}' with params #{cmd_array.inspect}") rv = aug.mv(cmd_array[0], cmd_array[1]) fail("Error sending command '#{command}' with params #{cmd_array.inspect}") if (rv == -1) else fail("Command '#{command}' is not supported") end rescue SystemExit,NoMemoryError raise rescue Exception => e fail("Error sending command '#{command}' with params #{cmd_array.inspect}/#{e.message}") end end end end diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb index 8a347b331..a554363c8 100755 --- a/lib/puppet/provider/cron/crontab.rb +++ b/lib/puppet/provider/cron/crontab.rb @@ -1,206 +1,200 @@ require 'puppet/provider/parsedfile' tab = case Facter.value(:operatingsystem) when "Solaris" :suntab when "AIX" :aixtab else :crontab end - Puppet::Type.type(:cron).provide( - :crontab, - :parent => Puppet::Provider::ParsedFile, - :default_target => ENV["USER"] || "root", - - :filetype => tab -) do +Puppet::Type.type(:cron).provide(:crontab, :parent => Puppet::Provider::ParsedFile, :default_target => ENV["USER"] || "root", :filetype => tab) do commands :crontab => "crontab" text_line :comment, :match => %r{^#}, :post_parse => proc { |record| record[:name] = $1 if record[:line] =~ /Puppet Name: (.+)\s*$/ } text_line :blank, :match => %r{^\s*$} text_line :environment, :match => %r{^\w+=} record_line :freebsd_special, :fields => %w{special command}, :match => %r{^@(\w+)\s+(.+)$}, :pre_gen => proc { |record| record[:special] = "@" + record[:special] } crontab = record_line :crontab, :fields => %w{minute hour monthday month weekday command}, :match => %r{^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}, :optional => %w{minute hour weekday month monthday}, :absent => "*" class << crontab def numeric_fields fields - [:command] end # Do some post-processing of the parsed record. Basically just # split the numeric fields on ','. def post_parse(record) numeric_fields.each do |field| if val = record[field] and val != :absent record[field] = record[field].split(",") end end end # Join the fields back up based on ','. def pre_gen(record) numeric_fields.each do |field| if vals = record[field] and vals.is_a?(Array) record[field] = vals.join(",") end end end # Add name and environments as necessary. def to_line(record) str = "" str = "# Puppet Name: #{record[:name]}\n" if record[:name] if record[:environment] and record[:environment] != :absent and record[:environment] != [:absent] record[:environment].each do |env| str += env + "\n" end end if record[:special] str += "@#{record[:special]} #{record[:command]}" else str += join(record) end str end end # Return the header placed at the top of each generated file, warning # users that modifying this file manually is probably a bad idea. def self.header %{# HEADER: This file was autogenerated at #{Time.now} by puppet. # HEADER: While it can still be managed manually, it is definitely not recommended. # HEADER: Note particularly that the comments starting with 'Puppet Name' should # HEADER: not be deleted, as doing so could cause duplicate cron jobs.\n} end # See if we can match the record against an existing cron job. def self.match(record, resources) resources.each do |name, resource| # Match the command first, since it's the most important one. next unless record[:target] == resource.value(:target) next unless record[:command] == resource.value(:command) # Then check the @special stuff if record[:special] next unless resource.value(:special) == record[:special] end # Then the normal fields. matched = true record_type(record[:record_type]).fields.each do |field| next if field == :command next if field == :special if record[field] and ! resource.value(field) #Puppet.info "Cron is missing %s: %s and %s" % # [field, record[field].inspect, resource.value(field).inspect] matched = false break end if ! record[field] and resource.value(field) #Puppet.info "Hash is missing %s: %s and %s" % # [field, resource.value(field).inspect, record[field].inspect] matched = false break end # Yay differing definitions of absent. next if (record[field] == :absent and resource.value(field) == "*") # Everything should be in the form of arrays, not the normal text. next if (record[field] == resource.value(field)) #Puppet.info "Did not match %s: %s vs %s" % # [field, resource.value(field).inspect, record[field].inspect] matched = false break end return resource if matched end false end # Collapse name and env records. def self.prefetch_hook(records) name = nil envs = nil result = records.each { |record| case record[:record_type] when :comment if record[:name] name = record[:name] record[:skip] = true # Start collecting env values envs = [] end when :environment # If we're collecting env values (meaning we're in a named cronjob), # store the line and skip the record. if envs envs << record[:line] record[:skip] = true end when :blank # nothing else if name record[:name] = name name = nil end if envs.nil? or envs.empty? record[:environment] = :absent else # Collect all of the environment lines, and mark the records to be skipped, # since their data is included in our crontab record. record[:environment] = envs # And turn off env collection again envs = nil end end }.reject { |record| record[:skip] } result end def self.to_file(records) text = super # Apparently Freebsd will "helpfully" add a new TZ line to every # single cron line, but not in all cases (e.g., it doesn't do it # on my machine). This is my attempt to fix it so the TZ lines don't # multiply. if text =~ /(^TZ=.+\n)/ tz = $1 text.sub!(tz, '') text = tz + text end text end def user=(user) @property_hash[:user] = user @property_hash[:target] = user end def user @property_hash[:user] || @property_hash[:target] end end diff --git a/lib/puppet/provider/group/directoryservice.rb b/lib/puppet/provider/group/directoryservice.rb index 97fee883d..e11284898 100644 --- a/lib/puppet/provider/group/directoryservice.rb +++ b/lib/puppet/provider/group/directoryservice.rb @@ -1,26 +1,12 @@ -# Created by Jeff McCune on 2007-07-22 -# Copyright (c) 2007. All rights reserved. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation (version 2 of the License) -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA - require 'puppet/provider/nameservice/directoryservice' Puppet::Type.type(:group).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do desc "Group management using DirectoryService on OS X. " commands :dscl => "/usr/bin/dscl" confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin has_feature :manages_members end diff --git a/lib/puppet/provider/group/groupadd.rb b/lib/puppet/provider/group/groupadd.rb index 82ed4c0c7..bcc08d9f7 100644 --- a/lib/puppet/provider/group/groupadd.rb +++ b/lib/puppet/provider/group/groupadd.rb @@ -1,29 +1,32 @@ require 'puppet/provider/nameservice/objectadd' Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameService::ObjectAdd do desc "Group management via `groupadd` and its ilk. The default for most platforms " commands :add => "groupadd", :delete => "groupdel", :modify => "groupmod" + has_feature :system_groups + verify :gid, "GID must be an integer" do |value| value.is_a? Integer end def addcmd cmd = [command(:add)] if gid = @resource.should(:gid) unless gid == :absent cmd << flag(:gid) << gid end end cmd << "-o" if @resource.allowdupe? + cmd << "-r" if @resource.system? cmd << @resource[:name] cmd end end diff --git a/spec/unit/transportable_spec.rb b/lib/puppet/provider/interface/base.rb similarity index 100% copy from spec/unit/transportable_spec.rb copy to lib/puppet/provider/interface/base.rb diff --git a/lib/puppet/provider/interface/cisco.rb b/lib/puppet/provider/interface/cisco.rb new file mode 100644 index 000000000..f3bd202e9 --- /dev/null +++ b/lib/puppet/provider/interface/cisco.rb @@ -0,0 +1,33 @@ +require 'puppet/util/network_device/cisco/device' +require 'puppet/provider/network_device' + +Puppet::Type.type(:interface).provide :cisco, :parent => Puppet::Provider::NetworkDevice do + + desc "Cisco switch/router provider for interface." + + mk_resource_methods + + def self.lookup(url, name) + interface = nil + network_gear = Puppet::Util::NetworkDevice::Cisco::Device.new(url) + network_gear.command do |ng| + interface = network_gear.interface(name) + end + interface + end + + def initialize(*args) + super + end + + def flush + device.command do |device| + device.new_interface(name).update(former_properties, properties) + end + super + end + + def device + @device ||= Puppet::Util::NetworkDevice::Cisco::Device.new(resource[:device_url]) + end +end diff --git a/lib/puppet/provider/mcx/mcxcontent.rb b/lib/puppet/provider/mcx/mcxcontent.rb index 3ad437b53..0c0061278 100644 --- a/lib/puppet/provider/mcx/mcxcontent.rb +++ b/lib/puppet/provider/mcx/mcxcontent.rb @@ -1,185 +1,166 @@ -#-- -# Copyright (C) 2008 Jeffrey J McCune. - -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Author: Jeff McCune - require 'tempfile' Puppet::Type.type(:mcx).provide :mcxcontent, :parent => Puppet::Provider do desc "MCX Settings management using DirectoryService on OS X. This provider manages the entire MCXSettings attribute available to some directory services nodes. This management is 'all or nothing' in that discrete application domain key value pairs are not managed by this provider. It is recommended to use WorkGroup Manager to configure Users, Groups, Computers, or ComputerLists, then use 'ralsh mcx' to generate a puppet manifest from the resulting configuration. Original Author: Jeff McCune (mccune.jeff@gmail.com) " # This provides a mapping of puppet types to DirectoryService # type strings. TypeMap = { :user => "Users", :group => "Groups", :computer => "Computers", :computerlist => "ComputerLists", } class MCXContentProviderException < Exception end commands :dscl => "/usr/bin/dscl" confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin def self.instances mcx_list = [] TypeMap.keys.each do |ds_type| ds_path = "/Local/Default/#{TypeMap[ds_type]}" output = dscl 'localhost', '-list', ds_path member_list = output.split member_list.each do |ds_name| content = mcxexport(ds_type, ds_name) if content.empty? Puppet.debug "/#{TypeMap[ds_type]}/#{ds_name} has no MCX data." else # This node has MCX data. mcx_list << self.new( :name => "/#{TypeMap[ds_type]}/#{ds_name}", :ds_type => ds_type, :ds_name => ds_name, :content => content ) end end end mcx_list end def self.mcxexport(ds_type, ds_name) ds_t = TypeMap[ds_type] ds_n = ds_name.to_s ds_path = "/Local/Default/#{ds_t}/#{ds_n}" dscl 'localhost', '-mcxexport', ds_path end def create self.content=(resource[:content]) end def destroy ds_parms = get_dsparams ds_t = TypeMap[ds_parms[:ds_type]] ds_n = ds_parms[:ds_name].to_s ds_path = "/Local/Default/#{ds_t}/#{ds_n}" dscl 'localhost', '-mcxdelete', ds_path end def exists? begin has_mcx? rescue Puppet::ExecutionFailure => e return false end end def content ds_parms = get_dsparams self.class.mcxexport(ds_parms[:ds_type], ds_parms[:ds_name]) end def content=(value) # dscl localhost -mcximport ds_parms = get_dsparams mcximport(ds_parms[:ds_type], ds_parms[:ds_name], resource[:content]) end private def has_mcx? !content.empty? end def mcximport(ds_type, ds_name, val) ds_t = TypeMap[ds_type] ds_path = "/Local/Default/#{ds_t}/#{ds_name}" tmp = Tempfile.new('puppet_mcx') begin tmp << val tmp.flush dscl 'localhost', '-mcximport', ds_path, tmp.path ensure tmp.close tmp.unlink end end # Given the resource name string, parse ds_type out. def parse_type(name) ds_type = name.split('/')[1] unless ds_type raise MCXContentProviderException, "Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter." end # De-pluralize and downcase. ds_type = ds_type.chop.downcase.to_sym unless TypeMap.key? ds_type raise MCXContentProviderException, "Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter." end ds_type end # Given the resource name string, parse ds_name out. def parse_name(name) ds_name = name.split('/')[2] unless ds_name raise MCXContentProviderException, "Could not parse ds_name from resource name '#{name}'. Specify with ds_name parameter." end ds_name end # Gather ds_type and ds_name from resource or parse it out of the name. def get_dsparams ds_type = resource[:ds_type] ds_type ||= parse_type(resource[:name]) raise MCXContentProviderException unless TypeMap.keys.include? ds_type.to_sym ds_name = resource[:ds_name] ds_name ||= parse_name(resource[:name]) { :ds_type => ds_type.to_sym, :ds_name => ds_name, } end end diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb index 2e3480985..c1139a679 100644 --- a/lib/puppet/provider/nameservice/directoryservice.rb +++ b/lib/puppet/provider/nameservice/directoryservice.rb @@ -1,548 +1,534 @@ -# Created by Jeff McCune on 2007-07-22 -# Copyright (c) 2007. All rights reserved. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation (version 2 of the License) -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA - require 'puppet' require 'puppet/provider/nameservice' require 'facter/util/plist' require 'cgi' class Puppet::Provider::NameService class DirectoryService < Puppet::Provider::NameService # JJM: Dive into the singleton_class class << self # JJM: This allows us to pass information when calling # Puppet::Type.type # e.g. Puppet::Type.type(:user).provide :directoryservice, :ds_path => "Users" # This is referenced in the get_ds_path class method attr_writer :ds_path attr_writer :macosx_version_major end initvars commands :dscl => "/usr/bin/dscl" commands :dseditgroup => "/usr/sbin/dseditgroup" commands :sw_vers => "/usr/bin/sw_vers" confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin # JJM 2007-07-25: This map is used to map NameService attributes to their # corresponding DirectoryService attribute names. # See: http://images.apple.com/server/docs.Open_Directory_v10.4.pdf # JJM: Note, this is de-coupled from the Puppet::Type, and must # be actively maintained. There may also be collisions with different # types (Users, Groups, Mounts, Hosts, etc...) @@ds_to_ns_attribute_map = { 'RecordName' => :name, 'PrimaryGroupID' => :gid, 'NFSHomeDirectory' => :home, 'UserShell' => :shell, 'UniqueID' => :uid, 'RealName' => :comment, 'Password' => :password, 'GeneratedUID' => :guid, 'IPAddress' => :ip_address, 'ENetAddress' => :en_address, 'GroupMembership' => :members, } # JJM The same table as above, inverted. @@ns_to_ds_attribute_map = { :name => 'RecordName', :gid => 'PrimaryGroupID', :home => 'NFSHomeDirectory', :shell => 'UserShell', :uid => 'UniqueID', :comment => 'RealName', :password => 'Password', :guid => 'GeneratedUID', :en_address => 'ENetAddress', :ip_address => 'IPAddress', :members => 'GroupMembership', } @@password_hash_dir = "/var/db/shadow/hash" def self.instances # JJM Class method that provides an array of instance objects of this # type. # JJM: Properties are dependent on the Puppet::Type we're managine. type_property_array = [:name] + @resource_type.validproperties # Create a new instance of this Puppet::Type for each object present # on the system. list_all_present.collect do |name_string| self.new(single_report(name_string, *type_property_array)) end end def self.get_ds_path # JJM: 2007-07-24 This method dynamically returns the DS path we're concerned with. # For example, if we're working with an user type, this will be /Users # with a group type, this will be /Groups. # @ds_path is an attribute of the class itself. return @ds_path if defined?(@ds_path) # JJM: "Users" or "Groups" etc ... (Based on the Puppet::Type) # Remember this is a class method, so self.class is Class # Also, @resource_type seems to be the reference to the # Puppet::Type this class object is providing for. @resource_type.name.to_s.capitalize + "s" end def self.get_macosx_version_major return @macosx_version_major if defined?(@macosx_version_major) begin # Make sure we've loaded all of the facts Facter.loadfacts if Facter.value(:macosx_productversion_major) product_version_major = Facter.value(:macosx_productversion_major) else # TODO: remove this code chunk once we require Facter 1.5.5 or higher. Puppet.warning("DEPRECATION WARNING: Future versions of the directoryservice provider will require Facter 1.5.5 or newer.") product_version = Facter.value(:macosx_productversion) fail("Could not determine OS X version from Facter") if product_version.nil? product_version_major = product_version.scan(/(\d+)\.(\d+)./).join(".") end fail("#{product_version_major} is not supported by the directoryservice provider") if %w{10.0 10.1 10.2 10.3}.include?(product_version_major) @macosx_version_major = product_version_major return @macosx_version_major rescue Puppet::ExecutionFailure => detail fail("Could not determine OS X version: #{detail}") end end def self.list_all_present # JJM: List all objects of this Puppet::Type already present on the system. begin dscl_output = execute(get_exec_preamble("-list")) rescue Puppet::ExecutionFailure => detail fail("Could not get #{@resource_type.name} list from DirectoryService") end dscl_output.split("\n") end def self.parse_dscl_url_data(dscl_output) # we need to construct a Hash from the dscl -url output to match # that returned by the dscl -plist output for 10.5+ clients. # # Nasty assumptions: # a) no values *end* in a colon ':', only keys # b) if a line ends in a colon and the next line does start with # a space, then the second line is a value of the first. # c) (implied by (b)) keys don't start with spaces. dscl_plist = {} dscl_output.split("\n").inject([]) do |array, line| if line =~ /^\s+/ # it's a value array[-1] << line # add the value to the previous key else array << line end array end.compact dscl_output.each do |line| # This should be a 'normal' entry. key and value on one line. # We split on ': ' to deal with keys/values with a colon in them. split_array = line.split(/:\s+/) key = split_array.first value = CGI::unescape(split_array.last.strip.chomp) # We need to treat GroupMembership separately as it is currently # the only attribute we care about multiple values for, and # the values can never contain spaces (shortnames) # We also make every value an array to be consistent with the # output of dscl -plist under 10.5 if key == "GroupMembership" dscl_plist[key] = value.split(/\s/) else dscl_plist[key] = [value] end end dscl_plist end def self.parse_dscl_plist_data(dscl_output) Plist.parse_xml(dscl_output) end def self.generate_attribute_hash(input_hash, *type_properties) attribute_hash = {} input_hash.keys.each do |key| ds_attribute = key.sub("dsAttrTypeStandard:", "") next unless (@@ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? @@ds_to_ns_attribute_map[ds_attribute]) ds_value = input_hash[key] case @@ds_to_ns_attribute_map[ds_attribute] when :members ds_value = ds_value # only members uses arrays so far when :gid, :uid # OS X stores objects like uid/gid as strings. # Try casting to an integer for these cases to be # consistent with the other providers and the group type # validation begin ds_value = Integer(ds_value[0]) rescue ArgumentError ds_value = ds_value[0] end else ds_value = ds_value[0] end attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value end # NBK: need to read the existing password here as it's not actually # stored in the user record. It is stored at a path that involves the # UUID of the user record for non-Mobile local acccounts. # Mobile Accounts are out of scope for this provider for now attribute_hash[:password] = self.get_password(attribute_hash[:guid]) if @resource_type.validproperties.include?(:password) and Puppet.features.root? attribute_hash end def self.single_report(resource_name, *type_properties) # JJM 2007-07-24: # Given a the name of an object and a list of properties of that # object, return all property values in a hash. # # This class method returns nil if the object doesn't exist # Otherwise, it returns a hash of the object properties. all_present_str_array = list_all_present # NBK: shortcut the process if the resource is missing return nil unless all_present_str_array.include? resource_name dscl_vector = get_exec_preamble("-read", resource_name) begin dscl_output = execute(dscl_vector) rescue Puppet::ExecutionFailure => detail fail("Could not get report. command execution failed.") end # Two code paths is ugly, but until we can drop 10.4 support we don't # have a lot of choice. Ultimately this should all be done using Ruby # to access the DirectoryService APIs directly, but that's simply not # feasible for a while yet. case self.get_macosx_version_major when "10.4" dscl_plist = self.parse_dscl_url_data(dscl_output) when "10.5", "10.6" dscl_plist = self.parse_dscl_plist_data(dscl_output) end self.generate_attribute_hash(dscl_plist, *type_properties) end def self.get_exec_preamble(ds_action, resource_name = nil) # JJM 2007-07-24 # DSCL commands are often repetitive and contain the same positional # arguments over and over. See http://developer.apple.com/documentation/Porting/Conceptual/PortingUnix/additionalfeatures/chapter_10_section_9.html # for an example of what I mean. # This method spits out proper DSCL commands for us. # We EXPECT name to be @resource[:name] when called from an instance object. # 10.4 doesn't support the -plist option for dscl, and 10.5 has a # different format for the -url output with objects with spaces in # their values. *sigh*. Use -url for 10.4 in the hope this can be # deprecated one day, and use -plist for 10.5 and higher. case self.get_macosx_version_major when "10.4" command_vector = [ command(:dscl), "-url", "." ] when "10.5", "10.6" command_vector = [ command(:dscl), "-plist", "." ] end # JJM: The actual action to perform. See "man dscl" # Common actiosn: -create, -delete, -merge, -append, -passwd command_vector << ds_action # JJM: get_ds_path will spit back "Users" or "Groups", # etc... Depending on the Puppet::Type of our self. if resource_name command_vector << "/#{get_ds_path}/#{resource_name}" else command_vector << "/#{get_ds_path}" end # JJM: This returns most of the preamble of the command. # e.g. 'dscl / -create /Users/mccune' command_vector end def self.set_password(resource_name, guid, password_hash) password_hash_file = "#{@@password_hash_dir}/#{guid}" begin File.open(password_hash_file, 'w') { |f| f.write(password_hash)} rescue Errno::EACCES => detail fail("Could not write to password hash file: #{detail}") end # NBK: For shadow hashes, the user AuthenticationAuthority must contain a value of # ";ShadowHash;". The LKDC in 10.5 makes this more interesting though as it # will dynamically generate ;Kerberosv5;;username@LKDC:SHA1 attributes if # missing. Thus we make sure we only set ;ShadowHash; if it is missing, and # we can do this with the merge command. This allows people to continue to # use other custom AuthenticationAuthority attributes without stomping on them. # # There is a potential problem here in that we're only doing this when setting # the password, and the attribute could get modified at other times while the # hash doesn't change and so this doesn't get called at all... but # without switching all the other attributes to merge instead of create I can't # see a simple enough solution for this that doesn't modify the user record # every single time. This should be a rather rare edge case. (famous last words) dscl_vector = self.get_exec_preamble("-merge", resource_name) dscl_vector << "AuthenticationAuthority" << ";ShadowHash;" begin dscl_output = execute(dscl_vector) rescue Puppet::ExecutionFailure => detail fail("Could not set AuthenticationAuthority.") end end def self.get_password(guid) password_hash = nil password_hash_file = "#{@@password_hash_dir}/#{guid}" if File.exists?(password_hash_file) and File.file?(password_hash_file) fail("Could not read password hash file at #{password_hash_file}") if not File.readable?(password_hash_file) f = File.new(password_hash_file) password_hash = f.read f.close end password_hash end # Unlike most other *nixes, OS X doesn't provide built in functionality # for automatically assigning uids and gids to accounts, so we set up these # methods for consumption by functionality like --mkusers # By default we restrict to a reasonably sane range for system accounts def self.next_system_id(id_type, min_id=20) dscl_args = ['.', '-list'] if id_type == 'uid' dscl_args << '/Users' << 'uid' elsif id_type == 'gid' dscl_args << '/Groups' << 'gid' else fail("Invalid id_type #{id_type}. Only 'uid' and 'gid' supported") end dscl_out = dscl(dscl_args) # We're ok with throwing away negative uids here. ids = dscl_out.split.compact.collect { |l| l.to_i if l.match(/^\d+$/) } ids.compact!.sort! { |a,b| a.to_f <=> b.to_f } # We're just looking for an unused id in our sorted array. ids.each_index do |i| next_id = ids[i] + 1 return next_id if ids[i+1] != next_id and next_id >= min_id end end def ensure=(ensure_value) super # We need to loop over all valid properties for the type we're # managing and call the method which sets that property value # dscl can't create everything at once unfortunately. if ensure_value == :present @resource.class.validproperties.each do |name| next if name == :ensure # LAK: We use property.sync here rather than directly calling # the settor method because the properties might do some kind # of conversion. In particular, the user gid property might # have a string and need to convert it to a number if @resource.should(name) @resource.property(name).sync elsif value = autogen(name) self.send(name.to_s + "=", value) else next end end end end def password=(passphrase) exec_arg_vector = self.class.get_exec_preamble("-read", @resource.name) exec_arg_vector << @@ns_to_ds_attribute_map[:guid] begin guid_output = execute(exec_arg_vector) guid_plist = Plist.parse_xml(guid_output) # Although GeneratedUID like all DirectoryService values can be multi-valued # according to the schema, in practice user accounts cannot have multiple UUIDs # otherwise Bad Things Happen, so we just deal with the first value. guid = guid_plist["dsAttrTypeStandard:#{@@ns_to_ds_attribute_map[:guid]}"][0] self.class.set_password(@resource.name, guid, passphrase) rescue Puppet::ExecutionFailure => detail fail("Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}") end end # NBK: we override @parent.set as we need to execute a series of commands # to deal with array values, rather than the single command nameservice.rb # expects to be returned by modifycmd. Thus we don't bother defining modifycmd. def set(param, value) self.class.validate(param, value) current_members = @property_value_cache_hash[:members] if param == :members # If we are meant to be authoritative for the group membership # then remove all existing members who haven't been specified # in the manifest. remove_unwanted_members(current_members, value) if @resource[:auth_membership] and not current_members.nil? # if they're not a member, make them one. add_members(current_members, value) else exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name]) # JJM: The following line just maps the NS name to the DS name # e.g. { :uid => 'UniqueID' } exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(param)] # JJM: The following line sends the actual value to set the property to exec_arg_vector << value.to_s begin execute(exec_arg_vector) rescue Puppet::ExecutionFailure => detail fail("Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}") end end end # NBK: we override @parent.create as we need to execute a series of commands # to create objects with dscl, rather than the single command nameservice.rb # expects to be returned by addcmd. Thus we don't bother defining addcmd. def create if exists? info "already exists" return nil end # NBK: First we create the object with a known guid so we can set the contents # of the password hash if required # Shelling out sucks, but for a single use case it doesn't seem worth # requiring people install a UUID library that doesn't come with the system. # This should be revisited if Puppet starts managing UUIDs for other platform # user records. guid = %x{/usr/bin/uuidgen}.chomp exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name]) exec_arg_vector << @@ns_to_ds_attribute_map[:guid] << guid begin execute(exec_arg_vector) rescue Puppet::ExecutionFailure => detail fail("Could not set GeneratedUID for #{@resource.class.name} #{@resource.name}: #{detail}") end if value = @resource.should(:password) and value != "" self.class.set_password(@resource[:name], guid, value) end # Now we create all the standard properties Puppet::Type.type(@resource.class.name).validproperties.each do |property| next if property == :ensure value = @resource.should(property) if property == :gid and value.nil? value = self.class.next_system_id(id_type='gid') end if property == :uid and value.nil? value = self.class.next_system_id(id_type='uid') end if value != "" and not value.nil? if property == :members add_members(nil, value) else exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name]) exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)] next if property == :password # skip setting the password here exec_arg_vector << value.to_s begin execute(exec_arg_vector) rescue Puppet::ExecutionFailure => detail fail("Could not create #{@resource.class.name} #{@resource.name}: #{detail}") end end end end end def remove_unwanted_members(current_members, new_members) current_members.each do |member| if not new_members.flatten.include?(member) cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]] begin execute(cmd) rescue Puppet::ExecutionFailure => detail fail("Could not remove #{member} from group: #{@resource.name}, #{detail}") end end end end def add_members(current_members, new_members) new_members.flatten.each do |new_member| if current_members.nil? or not current_members.include?(new_member) cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", new_member, @resource[:name]] begin execute(cmd) rescue Puppet::ExecutionFailure => detail fail("Could not add #{new_member} to group: #{@resource.name}, #{detail}") end end end end def deletecmd # JJM: Like addcmd, only called when deleting the object itself # Note, this isn't used to delete properties of the object, # at least that's how I understand it... self.class.get_exec_preamble("-delete", @resource[:name]) end def getinfo(refresh = false) # JJM 2007-07-24: # Override the getinfo method, which is also defined in nameservice.rb # This method returns and sets @infohash # I'm not re-factoring the name "getinfo" because this method will be # most likely called by nameservice.rb, which I didn't write. if refresh or (! defined?(@property_value_cache_hash) or ! @property_value_cache_hash) # JJM 2007-07-24: OK, there's a bit of magic that's about to # happen... Let's see how strong my grip has become... =) # # self is a provider instance of some Puppet::Type, like # Puppet::Type::User::ProviderDirectoryservice for the case of the # user type and this provider. # # self.class looks like "user provider directoryservice", if that # helps you ... # # self.class.resource_type is a reference to the Puppet::Type class, # probably Puppet::Type::User or Puppet::Type::Group, etc... # # self.class.resource_type.validproperties is a class method, # returning an Array of the valid properties of that specific # Puppet::Type. # # So... something like [:comment, :home, :password, :shell, :uid, # :groups, :ensure, :gid] # # Ultimately, we add :name to the list, delete :ensure from the # list, then report on the remaining list. Pretty whacky, ehh? type_properties = [:name] + self.class.resource_type.validproperties type_properties.delete(:ensure) if type_properties.include? :ensure type_properties << :guid # append GeneratedUID so we just get the report here @property_value_cache_hash = self.class.single_report(@resource[:name], *type_properties) [:uid, :gid].each do |param| @property_value_cache_hash[param] = @property_value_cache_hash[param].to_i if @property_value_cache_hash and @property_value_cache_hash.include?(param) end end @property_value_cache_hash end end end diff --git a/lib/puppet/provider/network_device.rb b/lib/puppet/provider/network_device.rb new file mode 100644 index 000000000..58865fddc --- /dev/null +++ b/lib/puppet/provider/network_device.rb @@ -0,0 +1,59 @@ + +# This is the base class of all prefetched network device provider +class Puppet::Provider::NetworkDevice < Puppet::Provider + + def self.lookup(url, name) + raise "This provider doesn't implement the necessary lookup method" + end + + def self.prefetch(resources) + resources.each do |name, resource| + if result = lookup(resource[:device_url], name) + result[:ensure] = :present + resource.provider = new(result) + else + resource.provider = new(:ensure => :absent) + end + end + end + + def exists? + @property_hash[:ensure] != :absent + end + + def initialize(*args) + super + + # Make a duplicate, so that we have a copy for comparison + # at the end. + @properties = @property_hash.dup + end + + def create + @property_hash[:ensure] = :present + self.class.resource_type.validproperties.each do |property| + if val = resource.should(property) + @property_hash[property] = val + end + end + end + + def destroy + @property_hash[:ensure] = :absent + end + + def flush + @property_hash.clear + end + + def self.instances + end + + def former_properties + @properties.dup + end + + def properties + @property_hash.dup + end +end \ No newline at end of file diff --git a/lib/puppet/provider/package/darwinport.rb b/lib/puppet/provider/package/darwinport.rb deleted file mode 100755 index c5f9ba28f..000000000 --- a/lib/puppet/provider/package/darwinport.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'puppet/provider/package' - -Puppet::Type.type(:package).provide :darwinport, :parent => Puppet::Provider::Package do - desc "Package management using DarwinPorts on OS X." - - confine :operatingsystem => :darwin - commands :port => "/opt/local/bin/port" - - def self.eachpkgashash - # list out all of the packages - open("| #{command(:port)} list installed") { |process| - regex = %r{(\S+)\s+@(\S+)\s+(\S+)} - fields = [:name, :ensure, :location] - hash = {} - - # now turn each returned line into a package object - process.each { |line| - hash.clear - - if match = regex.match(line) - fields.zip(match.captures) { |field,value| - hash[field] = value - } - - hash.delete :location - hash[:provider] = self.name - yield hash.dup - else - raise Puppet::DevError, - "Failed to match dpkg line #{line}" - end - } - } - end - - def self.instances - packages = [] - - eachpkgashash do |hash| - packages << new(hash) - end - - packages - end - - def install - should = @resource.should(:ensure) - - # Seems like you can always say 'upgrade' - output = port "upgrade", @resource[:name] - if output =~ /^Error: No port/ - raise Puppet::ExecutionFailure, "Could not find package #{@resource[:name]}" - end - end - - def query - version = nil - self.class.eachpkgashash do |hash| - return hash if hash[:name] == @resource[:name] - end - - nil - end - - def latest - info = port :search, "^#{@resource[:name]}$" - - if $CHILD_STATUS != 0 or info =~ /^Error/ - return nil - end - - ary = info.split(/\s+/) - version = ary[2].sub(/^@/, '') - - version - end - - def uninstall - port :uninstall, @resource[:name] - end - - def update - install - end -end - diff --git a/lib/puppet/provider/package/macports.rb b/lib/puppet/provider/package/macports.rb new file mode 100755 index 000000000..c43eb72f3 --- /dev/null +++ b/lib/puppet/provider/package/macports.rb @@ -0,0 +1,106 @@ +require 'puppet/provider/package' + +Puppet::Type.type(:package).provide :macports, :parent => Puppet::Provider::Package do + desc "Package management using MacPorts on OS X. + + Supports MacPorts versions and revisions, but not variants. + Variant preferences may be specified using the MacPorts variants.conf file + http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf + + When specifying a version in the Puppet DSL, only specify the version, not the revision + Revisions are only used internally for ensuring the latest version/revision of a port. + " + + confine :operatingsystem => :darwin + commands :port => "/opt/local/bin/port" + + has_feature :installable + has_feature :uninstallable + has_feature :upgradeable + has_feature :versionable + + + def self.parse_installed_query_line(line) + regex = /(\S+)\s+@(\S+)_(\S+)\s+\(active\)/ + fields = [:name, :ensure, :revision] + hash_from_line(line, regex, fields) + end + + def self.parse_info_query_line(line) + regex = /(\S+)\s+(\S+)/ + fields = [:version, :revision] + hash_from_line(line, regex, fields) + end + + def self.hash_from_line(line, regex, fields) + hash = {} + if match = regex.match(line) + fields.zip(match.captures) { |field, value| + hash[field] = value + } + hash[:provider] = self.name + return hash + end + nil + end + + def self.instances + packages = [] + port("-q", :installed).each do |line| + if hash = parse_installed_query_line(line) + packages << new(hash) + end + end + packages + end + + def install + should = @resource.should(:ensure) + if [:latest, :installed, :present].include?(should) + output = port("-q", :install, @resource[:name]) + else + output = port("-q", :install, @resource[:name], "@#{should}") + end + # MacPorts now correctly exits non-zero with appropriate errors in + # situations where a port cannot be found or installed. + end + + def query + return self.class.parse_installed_query_line(port("-q", :installed, @resource[:name])) + end + + def latest + # We need both the version and the revision to be confident + # we've got the latest revision of a specific version + # Note we're still not doing anything with variants here. + info_line = port("-q", :info, "--line", "--version", "--revision", @resource[:name]) + return nil if info_line == "" + + if newest = self.class.parse_info_query_line(info_line) + current = query + # We're doing some fiddling behind the scenes here to cope with updated revisions. + # If we're already at the latest version/revision, then just return the version + # so the current and desired values match. Otherwise return version and revision + # to trigger an upgrade to the latest revision. + if newest[:version] == current[:ensure] and newest[:revision] == current[:revision] + return current[:ensure] + else + return "#{newest[:version]}_#{newest[:revision]}" + end + end + nil + end + + def uninstall + port("-q", :uninstall, @resource[:name]) + end + + def update + if query[:name] == @resource[:name] # 'port upgrade' cannot install new ports + port("-q", :upgrade, @resource[:name]) + else + install + end + end +end + diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb index 39e377d66..662d05c8f 100644 --- a/lib/puppet/provider/package/pkgdmg.rb +++ b/lib/puppet/provider/package/pkgdmg.rb @@ -1,144 +1,127 @@ # -# pkgdmg.rb -# -# Install Installer.app packages wrapped up inside a DMG image file. -# -# Copyright (C) 2007 Jeff McCune Jeff McCune -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation (version 2 of the License) -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA -# # Motivation: DMG files provide a true HFS file system # and are easier to manage and .pkg bundles. # # Note: the 'apple' Provider checks for the package name # in /L/Receipts. Since we install multiple pkg's from a single # source, we treat the source .pkg.dmg file as the package name. # As a result, we store installed .pkg.dmg file names # in /var/db/.puppet_pkgdmg_installed_ require 'puppet/provider/package' require 'facter/util/plist' Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Package do desc "Package management based on Apple's Installer.app and DiskUtility.app. This package works by checking the contents of a DMG image for Apple pkg or mpkg files. Any number of pkg or mpkg files may exist in the root directory of the DMG file system. Sub directories are not checked for packages. See `the wiki docs ` for more detail." confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin commands :installer => "/usr/sbin/installer" commands :hdiutil => "/usr/bin/hdiutil" commands :curl => "/usr/bin/curl" # JJM We store a cookie for each installed .pkg.dmg in /var/db def self.instance_by_name Dir.entries("/var/db").find_all { |f| f =~ /^\.puppet_pkgdmg_installed_/ }.collect do |f| name = f.sub(/^\.puppet_pkgdmg_installed_/, '') yield name if block_given? name end end def self.instances instance_by_name.collect do |name| new( :name => name, :provider => :pkgdmg, :ensure => :installed ) end end def self.installpkg(source, name, orig_source) installer "-pkg", source, "-target", "/" # Non-zero exit status will throw an exception. File.open("/var/db/.puppet_pkgdmg_installed_#{name}", "w") do |t| t.print "name: '#{name}'\n" t.print "source: '#{orig_source}'\n" end end def self.installpkgdmg(source, name) unless source =~ /\.dmg$/i || source =~ /\.pkg$/i raise Puppet::Error.new("Mac OS X PKG DMG's must specificy a source string ending in .dmg or flat .pkg file") end require 'open-uri' cached_source = source if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source cached_source = "/tmp/#{name}" begin curl "-o", cached_source, "-C", "-", "-k", "-s", "--url", source Puppet.debug "Success: curl transfered [#{name}]" rescue Puppet::ExecutionFailure Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods." cached_source = source end end begin if source =~ /\.dmg$/i File.open(cached_source) do |dmg| xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", dmg.path hdiutil_info = Plist::parse_xml(xml_str) raise Puppet::Error.new("No disk entities returned by mount at #{dmg.path}") unless hdiutil_info.has_key?("system-entities") mounts = hdiutil_info["system-entities"].collect { |entity| entity["mount-point"] }.compact begin mounts.each do |mountpoint| Dir.entries(mountpoint).select { |f| f =~ /\.m{0,1}pkg$/i }.each do |pkg| installpkg("#{mountpoint}/#{pkg}", name, source) end end ensure mounts.each do |mountpoint| hdiutil "eject", mountpoint end end end elsif source =~ /\.pkg$/i installpkg(cached_source, name, source) else raise Puppet::Error.new("Mac OS X PKG DMG's must specificy a source string ending in .dmg or flat .pkg file") end ensure # JJM Remove the file if open-uri didn't already do so. File.unlink(cached_source) if File.exist?(cached_source) end end def query if FileTest.exists?("/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}") Puppet.debug "/var/db/.puppet_pkgdmg_installed_#{@resource[:name]} found" return {:name => @resource[:name], :ensure => :present} else return nil end end def install source = nil unless source = @resource[:source] raise Puppet::Error.new("Mac OS X PKG DMG's must specify a package source.") end unless name = @resource[:name] raise Puppet::Error.new("Mac OS X PKG DMG's must specify a package name.") end self.class.installpkgdmg(source,name) end end diff --git a/lib/puppet/provider/user/directoryservice.rb b/lib/puppet/provider/user/directoryservice.rb index 4b62a6ae7..a2c561039 100644 --- a/lib/puppet/provider/user/directoryservice.rb +++ b/lib/puppet/provider/user/directoryservice.rb @@ -1,100 +1,86 @@ -# Created by Jeff McCune on 2007-07-22 -# Copyright (c) 2007. All rights reserved. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation (version 2 of the License) -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA - require 'puppet/provider/nameservice/directoryservice' Puppet::Type.type(:user).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do desc "User management using DirectoryService on OS X." commands :dscl => "/usr/bin/dscl" confine :operatingsystem => :darwin defaultfor :operatingsystem => :darwin # JJM: DirectoryService can manage passwords. # This needs to be a special option to dscl though (-passwd) has_feature :manages_passwords # JJM: comment matches up with the /etc/passwd concept of an user options :comment, :key => "realname" options :password, :key => "passwd" autogen_defaults :home => "/var/empty", :shell => "/usr/bin/false" verify :gid, "GID must be an integer" do |value| value.is_a? Integer end verify :uid, "UID must be an integer" do |value| value.is_a? Integer end def autogen_comment @resource[:name].capitalize end # The list of all groups the user is a member of. # JJM: FIXME: Override this method... def groups groups = [] groups.join(",") end # This is really lame. We have to iterate over each # of the groups and add us to them. def groups=(groups) # case groups # when Fixnum # groups = [groups.to_s] # when String # groups = groups.split(/\s*,\s*/) # else # raise Puppet::DevError, "got invalid groups value #{groups.class} of type #{groups}" # end # # Get just the groups we need to modify # diff = groups - (@is || []) # # data = {} # open("| #{command(:nireport)} / /groups name users") do |file| # file.each do |line| # name, members = line.split(/\s+/) # # if members.nil? or members =~ /NoValue/ # data[name] = [] # else # # Add each diff group's current members # data[name] = members.split(/,/) # end # end # end # # user = @resource[:name] # data.each do |name, members| # if members.include? user and groups.include? name # # I'm in the group and should be # next # elsif members.include? user # # I'm in the group and shouldn't be # setuserlist(name, members - [user]) # elsif groups.include? name # # I'm not in the group and should be # setuserlist(name, members + [user]) # else # # I'm not in the group and shouldn't be # next # end # end end end diff --git a/lib/puppet/provider/vlan/cisco.rb b/lib/puppet/provider/vlan/cisco.rb new file mode 100644 index 000000000..46e172c73 --- /dev/null +++ b/lib/puppet/provider/vlan/cisco.rb @@ -0,0 +1,34 @@ +require 'puppet/util/network_device/cisco/device' +require 'puppet/provider/network_device' + +Puppet::Type.type(:vlan).provide :cisco, :parent => Puppet::Provider::NetworkDevice do + + desc "Cisco switch/router provider for vlans." + + mk_resource_methods + + def self.lookup(url, id) + vlans = {} + device = Puppet::Util::NetworkDevice::Cisco::Device.new(url) + device.command do |d| + vlans = d.parse_vlans || {} + end + vlans[id] + end + + def initialize(*args) + super + end + + # Clear out the cached values. + def flush + device.command do |device| + device.update_vlan(resource[:name], former_properties, properties) + end + super + end + + def device + @device ||= Puppet::Util::NetworkDevice::Cisco::Device.new(resource[:device_url]) + end +end diff --git a/lib/puppet/rails/fact_name.rb b/lib/puppet/rails/fact_name.rb index 4273399e5..08301d9f0 100644 --- a/lib/puppet/rails/fact_name.rb +++ b/lib/puppet/rails/fact_name.rb @@ -1,5 +1,6 @@ +require 'puppet/rails' require 'puppet/rails/fact_value' class Puppet::Rails::FactName < ActiveRecord::Base has_many :fact_values, :dependent => :destroy end diff --git a/lib/puppet/rails/inventory_node.rb b/lib/puppet/rails/inventory_node.rb index 52f8621a4..da7e61040 100644 --- a/lib/puppet/rails/inventory_node.rb +++ b/lib/puppet/rails/inventory_node.rb @@ -1,25 +1,30 @@ require 'puppet/rails/inventory_fact' class Puppet::Rails::InventoryNode < ::ActiveRecord::Base has_many :facts, :class_name => "Puppet::Rails::InventoryFact", :foreign_key => :node_id, :dependent => :delete_all + if Puppet::Util.activerecord_version >= 3.0 + # Prevents "DEPRECATION WARNING: Base.named_scope has been deprecated, please use Base.scope instead" + ActiveRecord::NamedScope::ClassMethods.module_eval { alias :named_scope :scope } + end + named_scope :has_fact_with_value, lambda { |name,value| { :conditions => ["inventory_facts.name = ? AND inventory_facts.value = ?", name, value], :joins => :facts } } named_scope :has_fact_without_value, lambda { |name,value| { :conditions => ["inventory_facts.name = ? AND inventory_facts.value != ?", name, value], :joins => :facts } } def facts_to_hash facts.inject({}) do |fact_hash,fact| fact_hash.merge(fact.name => fact.value) end end end diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index 48d8c1f48..f8d820b77 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -1,349 +1,342 @@ require 'puppet/parser/parser' require 'puppet/util/warnings' require 'puppet/util/errors' require 'puppet/util/inline_docs' require 'puppet/parser/ast/leaf' require 'puppet/dsl' class Puppet::Resource::Type Puppet::ResourceType = self include Puppet::Util::InlineDocs include Puppet::Util::Warnings include Puppet::Util::Errors RESOURCE_SUPERTYPES = [:hostclass, :node, :definition] attr_accessor :file, :line, :doc, :code, :ruby_code, :parent, :resource_type_collection attr_reader :type, :namespace, :arguments, :behaves_like, :module_name RESOURCE_SUPERTYPES.each do |t| define_method("#{t}?") { self.type == t } end require 'puppet/indirector' extend Puppet::Indirector indirects :resource_type, :terminus_class => :parser def self.from_pson(data) name = data.delete('name') or raise ArgumentError, "Resource Type names must be specified" type = data.delete('type') || "definition" data = data.inject({}) { |result, ary| result[ary[0].intern] = ary[1]; result } new(type, name, data) end def to_pson_data_hash data = [:doc, :line, :file, :parent].inject({}) do |hash, param| next hash unless (value = self.send(param)) and (value != "") hash[param.to_s] = value hash end data['arguments'] = arguments.dup unless arguments.empty? data['name'] = name data['type'] = type data end def to_pson(*args) to_pson_data_hash.to_pson(*args) end # Are we a child of the passed class? Do a recursive search up our # parentage tree to figure it out. def child_of?(klass) return false unless parent return(klass == parent_type ? true : parent_type.child_of?(klass)) end # Now evaluate the code associated with this class or definition. def evaluate_code(resource) - scope = resource.scope - if tmp = evaluate_parent_type(resource) - scope = tmp - end + static_parent = evaluate_parent_type(resource) + scope = static_parent || resource.scope - scope = subscope(scope, resource) unless resource.title == :main + scope = scope.newscope(:namespace => namespace, :source => self, :resource => resource, :dynamic => !static_parent) unless resource.title == :main scope.compiler.add_class(name) unless definition? set_resource_parameters(resource, scope) code.safeevaluate(scope) if code evaluate_ruby_code(resource, scope) if ruby_code end def initialize(type, name, options = {}) @type = type.to_s.downcase.to_sym raise ArgumentError, "Invalid resource supertype '#{type}'" unless RESOURCE_SUPERTYPES.include?(@type) name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName) set_name_and_namespace(name) [:code, :doc, :line, :file, :parent].each do |param| next unless value = options[param] send(param.to_s + "=", value) end set_arguments(options[:arguments]) @module_name = options[:module_name] end # This is only used for node names, and really only when the node name # is a regexp. def match(string) return string.to_s.downcase == name unless name_is_regex? @name =~ string end # Add code from a new instance to our code. def merge(other) fail "#{name} is not a class; cannot add code to it" unless type == :hostclass fail "#{other.name} is not a class; cannot add code from it" unless other.type == :hostclass fail "Cannot have code outside of a class/node/define because 'freeze_main' is enabled" if name == "" and Puppet.settings[:freeze_main] if parent and other.parent and parent != other.parent fail "Cannot merge classes with different parent classes (#{name} => #{parent} vs. #{other.name} => #{other.parent})" end # We know they're either equal or only one is set, so keep whichever parent is specified. self.parent ||= other.parent if other.doc self.doc ||= "" self.doc += other.doc end # This might just be an empty, stub class. return unless other.code unless self.code self.code = other.code return end array_class = Puppet::Parser::AST::ASTArray self.code = array_class.new(:children => [self.code]) unless self.code.is_a?(array_class) if other.code.is_a?(array_class) code.children += other.code.children else code.children << other.code end end # Make an instance of the resource type, and place it in the catalog # if it isn't in the catalog already. This is only possible for # classes and nodes. No parameters are be supplied--if this is a # parameterized class, then all parameters take on their default # values. def ensure_in_catalog(scope, parameters=nil) type == :definition and raise ArgumentError, "Cannot create resources for defined resource types" resource_type = type == :hostclass ? :class : :node # Do nothing if the resource already exists; this makes sure we don't # get multiple copies of the class resource, which helps provide the # singleton nature of classes. # we should not do this for classes with parameters # if parameters are passed, we should still try to create the resource # even if it exists so that we can fail # this prevents us from being able to combine param classes with include if resource = scope.catalog.resource(resource_type, name) and !parameters return resource end resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self) if parameters parameters.each do |k,v| resource.set_parameter(k,v) end end instantiate_resource(scope, resource) scope.compiler.add_resource(scope, resource) resource end def instantiate_resource(scope, resource) # Make sure our parent class has been evaluated, if we have one. if parent && !scope.catalog.resource(resource.type, parent) parent_type(scope).ensure_in_catalog(scope) end if ['Class', 'Node'].include? resource.type scope.catalog.tag(*resource.tags) end end def name return @name unless @name.is_a?(Regexp) @name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'') end def name_is_regex? @name.is_a?(Regexp) end # MQR TODO: # # The change(s) introduced by the fix for #4270 are mostly silly & should be # removed, though we didn't realize it at the time. If it can be established/ # ensured that nodes never call parent_type and that resource_types are always # (as they should be) members of exactly one resource_type_collection the # following method could / should be replaced with: # # def parent_type # @parent_type ||= parent && ( # resource_type_collection.find_or_load([name],parent,type.to_sym) || # fail Puppet::ParseError, "Could not find parent resource type '#{parent}' of type #{type} in #{resource_type_collection.environment}" # ) # end # # ...and then the rest of the changes around passing in scope reverted. # def parent_type(scope = nil) return nil unless parent unless @parent_type raise "Must pass scope to parent_type when called first time" unless scope unless @parent_type = scope.environment.known_resource_types.send("find_#{type}", [name], parent) fail Puppet::ParseError, "Could not find parent resource type '#{parent}' of type #{type} in #{scope.environment}" end end @parent_type end # Set any arguments passed by the resource as variables in the scope. def set_resource_parameters(resource, scope) set = {} resource.to_hash.each do |param, value| param = param.to_sym fail Puppet::ParseError, "#{resource.ref} does not accept attribute #{param}" unless valid_parameter?(param) exceptwrap { scope.setvar(param.to_s, value) } set[param] = true end if @type == :hostclass scope.setvar("title", resource.title.to_s.downcase) unless set.include? :title scope.setvar("name", resource.name.to_s.downcase ) unless set.include? :name else scope.setvar("title", resource.title ) unless set.include? :title scope.setvar("name", resource.name ) unless set.include? :name end scope.setvar("module_name", module_name) if module_name and ! set.include? :module_name if caller_name = scope.parent_module_name and ! set.include?(:caller_module_name) scope.setvar("caller_module_name", caller_name) end scope.class_set(self.name,scope) if hostclass? or node? # Verify that all required arguments are either present or # have been provided with defaults. arguments.each do |param, default| param = param.to_sym next if set.include?(param) # Even if 'default' is a false value, it's an AST value, so this works fine fail Puppet::ParseError, "Must pass #{param} to #{resource.ref}" unless default value = default.safeevaluate(scope) scope.setvar(param.to_s, value) # Set it in the resource, too, so the value makes it to the client. resource[param] = value end end - # Create a new subscope in which to evaluate our code. - def subscope(scope, resource) - scope.newscope :resource => resource, :namespace => self.namespace, :source => self - end - # Check whether a given argument is valid. def valid_parameter?(param) param = param.to_s return true if param == "name" return true if Puppet::Type.metaparam?(param) return false unless defined?(@arguments) return(arguments.include?(param) ? true : false) end def set_arguments(arguments) @arguments = {} return if arguments.nil? arguments.each do |arg, default| arg = arg.to_s warn_if_metaparam(arg, default) @arguments[arg] = default end end private def convert_from_ast(name) value = name.value if value.is_a?(Puppet::Parser::AST::Regex) name = value.value else name = value end end def evaluate_parent_type(resource) return unless klass = parent_type(resource.scope) and parent_resource = resource.scope.compiler.catalog.resource(:class, klass.name) || resource.scope.compiler.catalog.resource(:node, klass.name) parent_resource.evaluate unless parent_resource.evaluated? parent_scope(resource.scope, klass) end def evaluate_ruby_code(resource, scope) Puppet::DSL::ResourceAPI.new(resource, scope, ruby_code).evaluate end # Split an fq name into a namespace and name def namesplit(fullname) ary = fullname.split("::") n = ary.pop || "" ns = ary.join("::") return ns, n end def parent_scope(scope, klass) scope.class_scope(klass) || raise(Puppet::DevError, "Could not find scope for #{klass.name}") end def set_name_and_namespace(name) if name.is_a?(Regexp) @name = name @namespace = "" else @name = name.to_s.downcase # Note we're doing something somewhat weird here -- we're setting # the class's namespace to its fully qualified name. This means # anything inside that class starts looking in that namespace first. @namespace, ignored_shortname = @type == :hostclass ? [@name, ''] : namesplit(@name) end end def warn_if_metaparam(param, default) return unless Puppet::Type.metaparamclass(param) if default warnonce "#{param} is a metaparam; this value will inherit to all contained resources" else raise Puppet::ParseError, "#{param} is a metaparameter; please choose another parameter name in the #{self.name} definition" end end end diff --git a/lib/puppet/resource/type_collection.rb b/lib/puppet/resource/type_collection.rb index 9fe7cdd06..89b0a16ed 100644 --- a/lib/puppet/resource/type_collection.rb +++ b/lib/puppet/resource/type_collection.rb @@ -1,213 +1,214 @@ class Puppet::Resource::TypeCollection attr_reader :environment attr_accessor :parse_failed def clear @hostclasses.clear @definitions.clear @nodes.clear + @watched_files.clear end def initialize(env) @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env @hostclasses = {} @definitions = {} @nodes = {} # So we can keep a list and match the first-defined regex @node_list = [] @watched_files = {} end def import_ast(ast, modname) ast.instantiate(modname).each do |instance| add(instance) end end def inspect "TypeCollection" + { :hostclasses => @hostclasses.keys, :definitions => @definitions.keys, :nodes => @nodes.keys }.inspect end def <<(thing) add(thing) self end def add(instance) if instance.type == :hostclass and other = @hostclasses[instance.name] and other.type == :hostclass other.merge(instance) return other end method = "add_#{instance.type}" send(method, instance) instance.resource_type_collection = self instance end def add_hostclass(instance) dupe_check(instance, @hostclasses) { |dupe| "Class '#{instance.name}' is already defined#{dupe.error_context}; cannot redefine" } dupe_check(instance, @definitions) { |dupe| "Definition '#{instance.name}' is already defined#{dupe.error_context}; cannot be redefined as a class" } @hostclasses[instance.name] = instance instance end def hostclass(name) @hostclasses[munge_name(name)] end def add_node(instance) dupe_check(instance, @nodes) { |dupe| "Node '#{instance.name}' is already defined#{dupe.error_context}; cannot redefine" } @node_list << instance @nodes[instance.name] = instance instance end def loader require 'puppet/parser/type_loader' @loader ||= Puppet::Parser::TypeLoader.new(environment) end def node(name) name = munge_name(name) if node = @nodes[name] return node end @node_list.each do |node| next unless node.name_is_regex? return node if node.match(name) end nil end def node_exists?(name) @nodes[munge_name(name)] end def nodes? @nodes.length > 0 end def add_definition(instance) dupe_check(instance, @hostclasses) { |dupe| "'#{instance.name}' is already defined#{dupe.error_context} as a class; cannot redefine as a definition" } dupe_check(instance, @definitions) { |dupe| "Definition '#{instance.name}' is already defined#{dupe.error_context}; cannot be redefined" } @definitions[instance.name] = instance end def definition(name) @definitions[munge_name(name)] end def find_node(namespaces, name) @nodes[munge_name(name)] end def find_hostclass(namespaces, name) find_or_load(namespaces, name, :hostclass) end def find_definition(namespaces, name) find_or_load(namespaces, name, :definition) end [:hostclasses, :nodes, :definitions].each do |m| define_method(m) do instance_variable_get("@#{m}").dup end end def require_reparse? @parse_failed || stale? end def stale? @watched_files.values.detect { |file| file.changed? } end def version return @version if defined?(@version) if environment[:config_version] == "" @version = Time.now.to_i return @version end @version = Puppet::Util.execute([environment[:config_version]]).strip rescue Puppet::ExecutionFailure => e raise Puppet::ParseError, "Unable to set config_version: #{e.message}" end def watch_file(file) @watched_files[file] = Puppet::Util::LoadedFile.new(file) end def watching_file?(file) @watched_files.include?(file) end private # Return a list of all possible fully-qualified names that might be # meant by the given name, in the context of namespaces. def resolve_namespaces(namespaces, name) name = name.downcase if name =~ /^::/ # name is explicitly fully qualified, so just return it, sans # initial "::". return [name.sub(/^::/, '')] end if name == "" # The name "" has special meaning--it always refers to a "main" # hostclass which contains all toplevel resources. return [""] end namespaces = [namespaces] unless namespaces.is_a?(Array) namespaces = namespaces.collect { |ns| ns.downcase } result = [] namespaces.each do |namespace| ary = namespace.split("::") # Search each namespace nesting in innermost-to-outermost order. while ary.length > 0 result << "#{ary.join("::")}::#{name}" ary.pop end # Finally, search the toplevel namespace. result << name end return result.uniq end # Resolve namespaces and find the given object. Autoload it if # necessary. def find_or_load(namespaces, name, type) resolve_namespaces(namespaces, name).each do |fqname| if result = send(type, fqname) || loader.try_load_fqname(type, fqname) return result end end # Nothing found. return nil end def munge_name(name) name.to_s.downcase end def dupe_check(instance, hash) return unless dupe = hash[instance.name] message = yield dupe instance.fail Puppet::ParseError, message end end diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index 0533273d9..d7845fbc9 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -1,372 +1,372 @@ # the class that actually walks our resource/property tree, collects the changes, # and performs them require 'puppet' require 'puppet/util/tagging' require 'puppet/application' -require 'sha1' +require 'digest/sha1' class Puppet::Transaction require 'puppet/transaction/event' require 'puppet/transaction/event_manager' require 'puppet/transaction/resource_harness' require 'puppet/resource/status' attr_accessor :component, :catalog, :ignoreschedules attr_accessor :configurator # The report, once generated. attr_accessor :report # Routes and stores any events and subscriptions. attr_reader :event_manager # Handles most of the actual interacting with resources attr_reader :resource_harness include Puppet::Util include Puppet::Util::Tagging # Wraps application run state check to flag need to interrupt processing def stop_processing? Puppet::Application.stop_requested? end # Add some additional times for reporting def add_times(hash) hash.each do |name, num| report.add_times(name, num) end end # Are there any failed resources in this transaction? def any_failed? report.resource_statuses.values.detect { |status| status.failed? } end # Apply all changes for a resource def apply(resource, ancestor = nil) status = resource_harness.evaluate(resource) add_resource_status(status) event_manager.queue_events(ancestor || resource, status.events) unless status.failed? rescue => detail resource.err "Could not evaluate: #{detail}" end # Find all of the changed resources. def changed? report.resource_statuses.values.find_all { |status| status.changed }.collect { |status| catalog.resource(status.resource) } end # Find all of the applied resources (including failed attempts). def applied_resources report.resource_statuses.values.collect { |status| catalog.resource(status.resource) } end # Copy an important relationships from the parent to the newly-generated # child resource. def add_conditional_directed_dependency(parent, child, label=nil) relationship_graph.add_vertex(child) edge = parent.depthfirst? ? [child, parent] : [parent, child] if relationship_graph.edge?(*edge.reverse) parent.debug "Skipping automatic relationship to #{child}" else relationship_graph.add_edge(edge[0],edge[1],label) end end # Evaluate a single resource. def eval_resource(resource, ancestor = nil) if skip?(resource) resource_status(resource).skipped = true else resource_status(resource).scheduled = true apply(resource, ancestor) end # Check to see if there are any events queued for this resource event_manager.process_events(resource) end # This method does all the actual work of running a transaction. It # collects all of the changes, executes them, and responds to any # necessary events. def evaluate # Start logging. Puppet::Util::Log.newdestination(@report) prepare Puppet.info "Applying configuration version '#{catalog.version}'" if catalog.version begin relationship_graph.traverse do |resource| if resource.is_a?(Puppet::Type::Component) Puppet.warning "Somehow left a component in the relationship graph" else seconds = thinmark { eval_resource(resource) } resource.info "Evaluated in %0.2f seconds" % seconds if Puppet[:evaltrace] and @catalog.host_config? end end ensure # And then close the transaction log. Puppet::Util::Log.close(@report) end Puppet.debug "Finishing transaction #{object_id}" end def events event_manager.events end def failed?(resource) s = resource_status(resource) and s.failed? end # Does this resource have any failed dependencies? def failed_dependencies?(resource) # First make sure there are no failed dependencies. To do this, # we check for failures in any of the vertexes above us. It's not # enough to check the immediate dependencies, which is why we use # a tree from the reversed graph. found_failed = false relationship_graph.dependencies(resource).each do |dep| next unless failed?(dep) resource.notice "Dependency #{dep} has failures: #{resource_status(dep).failed}" found_failed = true end found_failed end def eval_generate(resource) raise Puppet::DevError,"Depthfirst resources are not supported by eval_generate" if resource.depthfirst? begin made = resource.eval_generate.uniq.reverse rescue => detail puts detail.backtrace if Puppet[:trace] resource.err "Failed to generate additional resources using 'eval_generate: #{detail}" return end made.each do |res| begin res.tag(*resource.tags) @catalog.add_resource(res) res.finish rescue Puppet::Resource::Catalog::DuplicateResourceError res.info "Duplicate generated resource; skipping" end end sentinal = Puppet::Type::Whit.new(:name => "completed_#{resource.title}", :catalog => resource.catalog) relationship_graph.adjacent(resource,:direction => :out,:type => :edges).each { |e| add_conditional_directed_dependency(sentinal, e.target, e.label) relationship_graph.remove_edge! e } default_label = Puppet::Resource::Catalog::Default_label made.each do |res| add_conditional_directed_dependency(made.find { |r| r != res && r.name == res.name[0,r.name.length]} || resource, res) add_conditional_directed_dependency(res, sentinal, default_label) end add_conditional_directed_dependency(resource, sentinal, default_label) end # A general method for recursively generating new resources from a # resource. def generate_additional_resources(resource) return unless resource.respond_to?(:generate) begin made = resource.generate rescue => detail puts detail.backtrace if Puppet[:trace] resource.err "Failed to generate additional resources using 'generate': #{detail}" end return unless made made = [made] unless made.is_a?(Array) made.uniq.each do |res| begin res.tag(*resource.tags) @catalog.add_resource(res) res.finish add_conditional_directed_dependency(resource, res) generate_additional_resources(res) rescue Puppet::Resource::Catalog::DuplicateResourceError res.info "Duplicate generated resource; skipping" end end end # Collect any dynamically generated resources. This method is called # before the transaction starts. def xgenerate @catalog.vertices.each { |resource| generate_additional_resources(resource) } end # Should we ignore tags? def ignore_tags? ! (@catalog.host_config? or Puppet[:name] == "puppet") end # this should only be called by a Puppet::Type::Component resource now # and it should only receive an array def initialize(catalog) @catalog = catalog @report = Puppet::Transaction::Report.new("apply") @event_manager = Puppet::Transaction::EventManager.new(self) @resource_harness = Puppet::Transaction::ResourceHarness.new(self) end # Prefetch any providers that support it. We don't support prefetching # types, just providers. def prefetch prefetchers = {} @catalog.vertices.each do |resource| if provider = resource.provider and provider.class.respond_to?(:prefetch) prefetchers[provider.class] ||= {} prefetchers[provider.class][resource.name] = resource end end # Now call prefetch, passing in the resources so that the provider instances can be replaced. prefetchers.each do |provider, resources| Puppet.debug "Prefetching #{provider.name} resources for #{provider.resource_type.name}" begin provider.prefetch(resources) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not prefetch #{provider.resource_type.name} provider '#{provider.name}': #{detail}" end end end # Prepare to evaluate the resources in a transaction. def prepare # Now add any dynamically generated resources xgenerate # Then prefetch. It's important that we generate and then prefetch, # so that any generated resources also get prefetched. prefetch end # We want to monitor changes in the relationship graph of our # catalog but this is complicated by the fact that the catalog - # both is_a graph and has_a graph, by the fact that changes to + # both is_a graph and has_a graph, by the fact that changes to # the structure of the object can have adverse serialization # effects, by threading issues, by order-of-initialization issues, - # etc. + # etc. # # Since the proper lifetime/scope of the monitoring is a transaction - # and the transaction is already commiting a mild law-of-demeter + # and the transaction is already commiting a mild law-of-demeter # transgression, we cut the Gordian knot here by simply wrapping the # transaction's view of the resource graph to capture and maintain # the information we need. Nothing outside the transaction needs # this information, and nothing outside the transaction can see it # except via the Transaction#relationship_graph class Relationship_graph_wrapper attr_reader :real_graph,:transaction,:ready,:generated,:done,:unguessable_deterministic_key def initialize(real_graph,transaction) @real_graph = real_graph @transaction = transaction @ready = {} @generated = {} @done = {} @unguessable_deterministic_key = Hash.new { |h,k| h[k] = Digest::SHA1.hexdigest("NaCl, MgSO4 (salts) and then #{k.title}") } vertices.each { |v| check_if_now_ready(v) } end def method_missing(*args,&block) real_graph.send(*args,&block) end def add_vertex(v) real_graph.add_vertex(v) check_if_now_ready(v) # ????????????????????????????????????????? end def add_edge(f,t,label=nil) ready.delete(t) real_graph.add_edge(f,t,label) end def check_if_now_ready(r) ready[r] = true if direct_dependencies_of(r).all? { |r2| done[r2] } end def next_resource ready.keys.sort_by { |r0| unguessable_deterministic_key[r0] }.first end def traverse(&block) real_graph.report_cycles_in_graph while (r = next_resource) && !transaction.stop_processing? if !generated[r] && r.respond_to?(:eval_generate) transaction.eval_generate(r) generated[r] = true else ready.delete(r) yield r done[r] = true direct_dependents_of(r).each { |v| check_if_now_ready(v) } end end end end def relationship_graph @relationship_graph ||= Relationship_graph_wrapper.new(catalog.relationship_graph,self) end def add_resource_status(status) report.add_resource_status status end def resource_status(resource) report.resource_statuses[resource.to_s] || add_resource_status(Puppet::Resource::Status.new(resource)) end # Is the resource currently scheduled? def scheduled?(resource) self.ignoreschedules or resource_harness.scheduled?(resource_status(resource), resource) end # Should this resource be skipped? def skip?(resource) if missing_tags?(resource) resource.debug "Not tagged with #{tags.join(", ")}" elsif ! scheduled?(resource) resource.debug "Not scheduled" elsif failed_dependencies?(resource) resource.warning "Skipping because of failed dependencies" elsif resource.virtual? resource.debug "Skipping because virtual" else return false end true end # The tags we should be checking. def tags self.tags = Puppet[:tags] unless defined?(@tags) super end def handle_qualified_tags( qualified ) # The default behavior of Puppet::Util::Tagging is # to split qualified tags into parts. That would cause # qualified tags to match too broadly here. return end # Is this resource tagged appropriately? def missing_tags?(resource) return false if ignore_tags? return false if tags.empty? not resource.tagged?(*tags) end end require 'puppet/transaction/report' diff --git a/lib/puppet/transaction/event_manager.rb b/lib/puppet/transaction/event_manager.rb index a21bbf892..f5da870ed 100644 --- a/lib/puppet/transaction/event_manager.rb +++ b/lib/puppet/transaction/event_manager.rb @@ -1,102 +1,102 @@ require 'puppet/transaction' class Puppet::Transaction::EventManager attr_reader :transaction, :events def initialize(transaction) @transaction = transaction @event_queues = {} @events = [] end def relationship_graph transaction.relationship_graph end # Respond to any queued events for this resource. def process_events(resource) restarted = false queued_events(resource) do |callback, events| r = process_callback(resource, callback, events) restarted ||= r end if restarted queue_events(resource, [resource.event(:name => :restarted, :status => "success")]) transaction.resource_status(resource).restarted = true end end # Queue events for other resources to respond to. All of these events have # to be from the same resource. def queue_events(resource, events) #@events += events # Do some basic normalization so we're not doing so many # graph queries for large sets of events. events.inject({}) do |collection, event| collection[event.name] ||= [] collection[event.name] << event collection end.collect do |name, list| # It doesn't matter which event we use - they all have the same source # and name here. event = list[0] # Collect the targets of any subscriptions to those events. We pass # the parent resource in so it will override the source in the events, # since eval_generated children can't have direct relationships. received = (event.name != :restarted) relationship_graph.matching_edges(event, resource).each do |edge| - received ||= true unless edge.target.is_a?(Puppet::Type::Whit) + received ||= true unless edge.target.is_a?(Puppet::Type.type(:whit)) next unless method = edge.callback next unless edge.target.respond_to?(method) queue_events_for_resource(resource, edge.target, method, list) end @events << event if received queue_events_for_resource(resource, resource, :refresh, [event]) if resource.self_refresh? and ! resource.deleting? end end def queue_events_for_resource(source, target, callback, events) source.info "Scheduling #{callback} of #{target}" @event_queues[target] ||= {} @event_queues[target][callback] ||= [] @event_queues[target][callback] += events end def queued_events(resource) return unless callbacks = @event_queues[resource] callbacks.each do |callback, events| yield callback, events end end private def process_callback(resource, callback, events) process_noop_events(resource, callback, events) and return false unless events.detect { |e| e.status != "noop" } resource.send(callback) resource.notice "Triggered '#{callback}' from #{events.length} events" return true rescue => detail resource.err "Failed to call #{callback}: #{detail}" transaction.resource_status(resource).failed_to_restart = true puts detail.backtrace if Puppet[:trace] return false end def process_noop_events(resource, callback, events) resource.notice "Would have triggered '#{callback}' from #{events.length} events" # And then add an event for it. queue_events(resource, [resource.event(:status => "noop", :name => :noop_restart)]) true # so the 'and if' works end end diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 16fee42ae..652b3874a 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -1,187 +1,193 @@ require 'puppet' require 'puppet/indirector' # A class for reporting what happens on each client. Reports consist of # two types of data: Logs and Metrics. Logs are the output that each # change produces, and Metrics are all of the numerical data involved # in the transaction. class Puppet::Transaction::Report extend Puppet::Indirector indirects :report, :terminus_class => :processor attr_accessor :configuration_version attr_reader :resource_statuses, :logs, :metrics, :host, :time, :kind, :status # This is necessary since Marshall doesn't know how to # dump hash with default proc (see below @records) def self.default_format :yaml end def <<(msg) @logs << msg self end def add_times(name, value) @external_times[name] = value end def add_metric(name, hash) metric = Puppet::Util::Metric.new(name) hash.each do |name, value| metric.newvalue(name, value) end @metrics[metric.name] = metric metric end def add_resource_status(status) @resource_statuses[status.resource] = status end def compute_status(resource_metrics, change_metric) if (resource_metrics["failed"] || 0) > 0 'failed' elsif change_metric > 0 'changed' else 'unchanged' end end + def prune_internal_data + resource_statuses.delete_if {|name,res| res.resource_type == 'Whit'} + end + def finalize_report + prune_internal_data + resource_metrics = add_metric(:resources, calculate_resource_metrics) add_metric(:time, calculate_time_metrics) change_metric = calculate_change_metric add_metric(:changes, {"total" => change_metric}) add_metric(:events, calculate_event_metrics) @status = compute_status(resource_metrics, change_metric) end def initialize(kind, configuration_version=nil) @metrics = {} @logs = [] @resource_statuses = {} @external_times ||= {} @host = Puppet[:certname] @time = Time.now @kind = kind @report_format = 2 @puppet_version = Puppet.version @configuration_version = configuration_version @status = 'failed' # assume failed until the report is finalized end def name host end # Provide a human readable textual summary of this report. def summary report = raw_summary ret = "" report.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |key| ret += "#{Puppet::Util::Metric.labelize(key)}:\n" report[key].keys.sort { |a,b| # sort by label if a == :total 1 elsif b == :total -1 else report[key][a].to_s <=> report[key][b].to_s end }.each do |label| value = report[key][label] next if value == 0 value = "%0.2f" % value if value.is_a?(Float) ret += " %15s %s\n" % [Puppet::Util::Metric.labelize(label) + ":", value] end end ret end # Provide a raw hash summary of this report. def raw_summary report = {} @metrics.each do |name, metric| key = metric.name.to_s report[key] = {} metric.values.each do |name, label, value| report[key][name.to_s] = value end report[key]["total"] = 0 unless key == "time" or report[key].include?("total") end (report["time"] ||= {})["last_run"] = Time.now.tv_sec report end # Based on the contents of this report's metrics, compute a single number # that represents the report. The resulting number is a bitmask where # individual bits represent the presence of different metrics. def exit_status status = 0 status |= 2 if @metrics["changes"]["total"] > 0 status |= 4 if @metrics["resources"]["failed"] > 0 status end def to_yaml_properties (instance_variables - ["@external_times"]).sort end private def calculate_change_metric resource_statuses.map { |name, status| status.change_count || 0 }.inject(0) { |a,b| a+b } end def calculate_event_metrics metrics = Hash.new(0) metrics["total"] = 0 resource_statuses.each do |name, status| metrics["total"] += status.events.length status.events.each do |event| metrics[event.status] += 1 end end metrics end def calculate_resource_metrics metrics = Hash.new(0) metrics["total"] = resource_statuses.length resource_statuses.each do |name, status| Puppet::Resource::Status::STATES.each do |state| metrics[state.to_s] += 1 if status.send(state) end end metrics end def calculate_time_metrics metrics = Hash.new(0) resource_statuses.each do |name, status| type = Puppet::Resource.new(name).type metrics[type.to_s.downcase] += status.evaluation_time if status.evaluation_time end @external_times.each do |name, value| metrics[name.to_s.downcase] = value end metrics["total"] = metrics.values.inject(0) { |a,b| a+b } metrics end end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 5ecc430d4..c0e5d390b 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -1,1904 +1,1904 @@ require 'puppet' require 'puppet/util/log' 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/util/cacher' require 'puppet/file_collection/lookup' require 'puppet/util/tagging' # 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 include Puppet::Util::Cacher include Puppet::FileCollection::Lookup include Puppet::Util::Tagging ############################### # Code related to resource type attributes. class << self include Puppet::Util::ClassGen include Puppet::Util::Warnings attr_reader :properties end def self.states warnonce "The states method is deprecated; use properties" properties end # All parameters, in the appropriate order. The key_attributes come first, then # the provider, then the properties, and finally the params and metaparams # in the order they were specified in the files. def self.allattrs key_attributes | (parameters & [:provider]) | properties.collect { |property| property.name } | parameters | metaparams end # Retrieve an attribute alias, if there is one. def self.attr_alias(param) @attr_aliases[symbolize(param)] end # Create an alias to an existing attribute. This will cause the aliased # attribute to be valid when setting and retrieving values on the instance. def self.set_attr_alias(hash) hash.each do |new, old| @attr_aliases[symbolize(new)] = symbolize(old) end end # Find the class associated with any given attribute. def self.attrclass(name) @attrclasses ||= {} # We cache the value, since this method gets called such a huge number # of times (as in, hundreds of thousands in a given run). unless @attrclasses.include?(name) @attrclasses[name] = case self.attrtype(name) when :property; @validproperties[name] when :meta; @@metaparamhash[name] when :param; @paramhash[name] end end @attrclasses[name] end # What type of parameter are we dealing with? Cache the results, because # this method gets called so many times. def self.attrtype(attr) @attrtypes ||= {} unless @attrtypes.include?(attr) @attrtypes[attr] = case when @validproperties.include?(attr); :property when @paramhash.include?(attr); :param when @@metaparamhash.include?(attr); :meta end end @attrtypes[attr] end def self.eachmetaparam @@metaparams.each { |p| yield p.name } end # Create the 'ensure' class. This is a separate method so other types # can easily call it and create their own 'ensure' values. def self.ensurable(&block) if block_given? self.newproperty(:ensure, :parent => Puppet::Property::Ensure, &block) else self.newproperty(:ensure, :parent => Puppet::Property::Ensure) do self.defaultvalues end end end # Should we add the 'ensure' property to this class? def self.ensurable? # If the class has all three of these methods defined, then it's # ensurable. ens = [:exists?, :create, :destroy].inject { |set, method| set &&= self.public_method_defined?(method) } ens end # Deal with any options passed into parameters. def self.handle_param_options(name, options) # If it's a boolean parameter, create a method to test the value easily if options[:boolean] define_method(name.to_s + "?") do val = self[name] if val == :true or val == true return true end end end end # Is the parameter in question a meta-parameter? def self.metaparam?(param) @@metaparamhash.include?(symbolize(param)) end # Find the metaparameter class associated with a given metaparameter name. def self.metaparamclass(name) @@metaparamhash[symbolize(name)] end def self.metaparams @@metaparams.collect { |param| param.name } end def self.metaparamdoc(metaparam) @@metaparamhash[metaparam].doc end # Create a new metaparam. Requires a block and a name, stores it in the # @parameters array, and does some basic checking on it. def self.newmetaparam(name, options = {}, &block) @@metaparams ||= [] @@metaparamhash ||= {} name = symbolize(name) param = genclass( name, :parent => options[:parent] || Puppet::Parameter, :prefix => "MetaParam", :hash => @@metaparamhash, :array => @@metaparams, :attributes => options[:attributes], &block ) # Grr. param.required_features = options[:required_features] if options[:required_features] handle_param_options(name, options) param.metaparam = true param end def self.key_attribute_parameters @key_attribute_parameters ||= ( params = @parameters.find_all { |param| param.isnamevar? or param.name == :name } ) end def self.key_attributes key_attribute_parameters.collect { |p| p.name } end def self.title_patterns case key_attributes.length when 0; [] when 1; identity = lambda {|x| x} [ [ /(.*)/m, [ [key_attributes.first, identity ] ] ] ] else raise Puppet::DevError,"you must specify title patterns when there are two or more key attributes" end end def uniqueness_key self.class.key_attributes.sort_by { |attribute_name| attribute_name.to_s }.map{ |attribute_name| self[attribute_name] } end # Create a new parameter. Requires a block and a name, stores it in the # @parameters array, and does some basic checking on it. def self.newparam(name, options = {}, &block) options[:attributes] ||= {} param = genclass( name, :parent => options[:parent] || Puppet::Parameter, :attributes => options[:attributes], :block => block, :prefix => "Parameter", :array => @parameters, :hash => @paramhash ) handle_param_options(name, options) # Grr. param.required_features = options[:required_features] if options[:required_features] param.isnamevar if options[:namevar] param end def self.newstate(name, options = {}, &block) Puppet.warning "newstate() has been deprecrated; use newproperty(#{name})" newproperty(name, options, &block) end # Create a new property. The first parameter must be the name of the property; # this is how users will refer to the property when creating new instances. # The second parameter is a hash of options; the options are: # * :parent: The parent class for the property. Defaults to Puppet::Property. # * :retrieve: The method to call on the provider or @parent object (if # the provider is not set) to retrieve the current value. def self.newproperty(name, options = {}, &block) name = symbolize(name) # This is here for types that might still have the old method of defining # a parent class. unless options.is_a? Hash raise Puppet::DevError, "Options must be a hash, not #{options.inspect}" end raise Puppet::DevError, "Class #{self.name} already has a property named #{name}" if @validproperties.include?(name) if parent = options[:parent] options.delete(:parent) else parent = Puppet::Property end # We have to create our own, new block here because we want to define # an initial :retrieve method, if told to, and then eval the passed # block if available. prop = genclass(name, :parent => parent, :hash => @validproperties, :attributes => options) do # If they've passed a retrieve method, then override the retrieve # method on the class. if options[:retrieve] define_method(:retrieve) do provider.send(options[:retrieve]) end end class_eval(&block) if block end # If it's the 'ensure' property, always put it first. if name == :ensure @properties.unshift prop else @properties << prop end prop end def self.paramdoc(param) @paramhash[param].doc end # Return the parameter names def self.parameters return [] unless defined?(@parameters) @parameters.collect { |klass| klass.name } end # Find the parameter class associated with a given parameter name. def self.paramclass(name) @paramhash[name] end # Return the property class associated with a name def self.propertybyname(name) @validproperties[name] end def self.validattr?(name) name = symbolize(name) return true if name == :name @validattrs ||= {} unless @validattrs.include?(name) @validattrs[name] = !!(self.validproperty?(name) or self.validparameter?(name) or self.metaparam?(name)) end @validattrs[name] end # does the name reflect a valid property? def self.validproperty?(name) name = symbolize(name) @validproperties.include?(name) && @validproperties[name] end # Return the list of validproperties def self.validproperties return {} unless defined?(@parameters) @validproperties.keys end # does the name reflect a valid parameter? def self.validparameter?(name) raise Puppet::DevError, "Class #{self} has not defined parameters" unless defined?(@parameters) !!(@paramhash.include?(name) or @@metaparamhash.include?(name)) end # This is a forward-compatibility method - it's the validity interface we'll use in Puppet::Resource. def self.valid_parameter?(name) validattr?(name) end # Return either the attribute alias or the attribute. def attr_alias(name) name = symbolize(name) if synonym = self.class.attr_alias(name) return synonym else return name end end # Are we deleting this resource? def deleting? obj = @parameters[:ensure] and obj.should == :absent end # Create a new property if it is valid but doesn't exist # Returns: true if a new parameter was added, false otherwise def add_property_parameter(prop_name) if self.class.validproperty?(prop_name) && !@parameters[prop_name] self.newattr(prop_name) return true end false end # # The name_var is the key_attribute in the case that there is only one. # def name_var key_attributes = self.class.key_attributes (key_attributes.length == 1) && key_attributes.first end # abstract accessing parameters and properties, and normalize # access to always be symbols, not strings # This returns a value, not an object. It returns the 'is' # value, but you can also specifically return 'is' and 'should' # values using 'object.is(:property)' or 'object.should(:property)'. def [](name) name = attr_alias(name) fail("Invalid parameter #{name}(#{name.inspect})") unless self.class.validattr?(name) if name == :name && nv = name_var name = nv end if obj = @parameters[name] # Note that if this is a property, then the value is the "should" value, # not the current value. obj.value else return nil end end # Abstract setting parameters and properties, and normalize # access to always be symbols, not strings. This sets the 'should' # value on properties, and otherwise just sets the appropriate parameter. def []=(name,value) name = attr_alias(name) fail("Invalid parameter #{name}") unless self.class.validattr?(name) if name == :name && nv = name_var name = nv end raise Puppet::Error.new("Got nil value for #{name}") if value.nil? property = self.newattr(name) if property begin # make sure the parameter doesn't have any errors property.value = value rescue => detail error = Puppet::Error.new("Parameter #{name} failed: #{detail}") error.set_backtrace(detail.backtrace) raise error end end nil end # remove a property from the object; useful in testing or in cleanup # when an error has been encountered def delete(attr) attr = symbolize(attr) if @parameters.has_key?(attr) @parameters.delete(attr) else raise Puppet::DevError.new("Undefined attribute '#{attr}' in #{self}") end end # iterate across the existing properties def eachproperty # properties is a private method properties.each { |property| yield property } end # Create a transaction event. Called by Transaction or by # a property. def event(options = {}) Puppet::Transaction::Event.new({:resource => self, :file => file, :line => line, :tags => tags}.merge(options)) end # Let the catalog determine whether a given cached value is # still valid or has expired. def expirer catalog end # retrieve the 'should' value for a specified property def should(name) name = attr_alias(name) (prop = @parameters[name] and prop.is_a?(Puppet::Property)) ? prop.should : nil end # Create the actual attribute instance. Requires either the attribute # name or class as the first argument, then an optional hash of # attributes to set during initialization. def newattr(name) if name.is_a?(Class) klass = name name = klass.name end unless klass = self.class.attrclass(name) raise Puppet::Error, "Resource type #{self.class.name} does not support parameter #{name}" end if provider and ! provider.class.supports_parameter?(klass) missing = klass.required_features.find_all { |f| ! provider.class.feature?(f) } info "Provider %s does not support features %s; not managing attribute %s" % [provider.class.name, missing.join(", "), name] return nil end return @parameters[name] if @parameters.include?(name) @parameters[name] = klass.new(:resource => self) end # return the value of a parameter def parameter(name) @parameters[name.to_sym] end def parameters @parameters.dup end # Is the named property defined? def propertydefined?(name) name = name.intern unless name.is_a? Symbol @parameters.include?(name) end # Return an actual property instance by name; to return the value, use 'resource[param]' # LAK:NOTE(20081028) Since the 'parameter' method is now a superset of this method, # this one should probably go away at some point. def property(name) (obj = @parameters[symbolize(name)] and obj.is_a?(Puppet::Property)) ? obj : nil end # For any parameters or properties that have defaults and have not yet been # set, set them now. This method can be handed a list of attributes, # and if so it will only set defaults for those attributes. def set_default(attr) return unless klass = self.class.attrclass(attr) return unless klass.method_defined?(:default) return if @parameters.include?(klass.name) return unless parameter = newattr(klass.name) if value = parameter.default and ! value.nil? parameter.value = value else @parameters.delete(parameter.name) end end # Convert our object to a hash. This just includes properties. def to_hash rethash = {} @parameters.each do |name, obj| rethash[name] = obj.value end rethash end def type self.class.name end # Return a specific value for an attribute. def value(name) name = attr_alias(name) (obj = @parameters[name] and obj.respond_to?(:value)) ? obj.value : nil end def version return 0 unless catalog catalog.version end # Return all of the property objects, in the order specified in the # class. def properties self.class.properties.collect { |prop| @parameters[prop.name] }.compact end # Is this type's name isomorphic with the object? That is, if the # name conflicts, does it necessarily mean that the objects conflict? # Defaults to true. def self.isomorphic? if defined?(@isomorphic) return @isomorphic else return true end end def isomorphic? self.class.isomorphic? end # is the instance a managed instance? A 'yes' here means that # the instance was created from the language, vs. being created # in order resolve other questions, such as finding a package # in a list def managed? # Once an object is managed, it always stays managed; but an object # that is listed as unmanaged might become managed later in the process, # so we have to check that every time if @managed return @managed else @managed = false properties.each { |property| s = property.should if s and ! property.class.unmanaged @managed = true break end } return @managed end end ############################### # Code related to the container behaviour. def depthfirst? false end # Remove an object. The argument determines whether the object's # subscriptions get eliminated, too. def remove(rmdeps = true) # This is hackish (mmm, cut and paste), but it works for now, and it's # better than warnings. @parameters.each do |name, obj| obj.remove end @parameters.clear @parent = nil # Remove the reference to the provider. if self.provider @provider.clear @provider = nil end end ############################### # Code related to evaluating the resources. # Flush the provider, if it supports it. This is called by the # transaction. def flush self.provider.flush if self.provider and self.provider.respond_to?(:flush) end # if all contained objects are in sync, then we're in sync # FIXME I don't think this is used on the type instances any more, # it's really only used for testing def insync?(is) insync = true if property = @parameters[:ensure] unless is.include? property raise Puppet::DevError, "The is value is not in the is array for '#{property.name}'" end ensureis = is[property] if property.safe_insync?(ensureis) and property.should == :absent return true end end properties.each { |property| unless is.include? property raise Puppet::DevError, "The is value is not in the is array for '#{property.name}'" end propis = is[property] unless property.safe_insync?(propis) property.debug("Not in sync: #{propis.inspect} vs #{property.should.inspect}") insync = false #else # property.debug("In sync") end } #self.debug("#{self} sync status is #{insync}") insync end # retrieve the current value of all contained properties def retrieve fail "Provider #{provider.class.name} is not functional on this host" if self.provider.is_a?(Puppet::Provider) and ! provider.class.suitable? result = Puppet::Resource.new(type, title) # Provide the name, so we know we'll always refer to a real thing result[:name] = self[:name] unless self[:name] == title if ensure_prop = property(:ensure) or (self.class.validattr?(:ensure) and ensure_prop = newattr(:ensure)) result[:ensure] = ensure_state = ensure_prop.retrieve else ensure_state = nil end properties.each do |property| next if property.name == :ensure if ensure_state == :absent result[property] = :absent else result[property] = property.retrieve end end result end def retrieve_resource resource = retrieve resource = Resource.new(type, title, :parameters => resource) if resource.is_a? Hash resource end # Get a hash of the current properties. Returns a hash with # the actual property instance as the key and the current value # as the, um, value. def currentpropvalues # It's important to use the 'properties' method here, as it follows the order # in which they're defined in the class. It also guarantees that 'ensure' # is the first property, which is important for skipping 'retrieve' on # all the properties if the resource is absent. ensure_state = false return properties.inject({}) do | prophash, property| if property.name == :ensure ensure_state = property.retrieve prophash[property] = ensure_state else if ensure_state == :absent prophash[property] = :absent else prophash[property] = property.retrieve end end prophash end end # Are we running in noop mode? def noop? # If we're not a host_config, we're almost certainly part of # Settings, and we want to ignore 'noop' return false if catalog and ! catalog.host_config? if defined?(@noop) @noop else Puppet[:noop] end end def noop noop? end ############################### # Code related to managing resource instances. require 'puppet/transportable' # retrieve a named instance of the current type def self.[](name) raise "Global resource access is deprecated" @objects[name] || @aliases[name] end # add an instance by name to the class list of instances def self.[]=(name,object) raise "Global resource storage is deprecated" newobj = nil if object.is_a?(Puppet::Type) newobj = object else raise Puppet::DevError, "must pass a Puppet::Type object" end if exobj = @objects[name] and self.isomorphic? msg = "Object '#{newobj.class.name}[#{name}]' already exists" msg += ("in file #{object.file} at line #{object.line}") if exobj.file and exobj.line msg += ("and cannot be redefined in file #{object.file} at line #{object.line}") if object.file and object.line error = Puppet::Error.new(msg) raise error else #Puppet.info("adding %s of type %s to class list" % # [name,object.class]) @objects[name] = newobj end end # Create an alias. We keep these in a separate hash so that we don't encounter # the objects multiple times when iterating over them. def self.alias(name, obj) raise "Global resource aliasing is deprecated" if @objects.include?(name) unless @objects[name] == obj raise Puppet::Error.new( "Cannot create alias #{name}: object already exists" ) end end if @aliases.include?(name) unless @aliases[name] == obj raise Puppet::Error.new( "Object #{@aliases[name].name} already has alias #{name}" ) end end @aliases[name] = obj end # remove all of the instances of a single type def self.clear raise "Global resource removal is deprecated" if defined?(@objects) @objects.each do |name, obj| obj.remove(true) end @objects.clear end @aliases.clear if defined?(@aliases) end # Force users to call this, so that we can merge objects if # necessary. def self.create(args) # LAK:DEP Deprecation notice added 12/17/2008 Puppet.warning "Puppet::Type.create is deprecated; use Puppet::Type.new" new(args) end # remove a specified object def self.delete(resource) raise "Global resource removal is deprecated" return unless defined?(@objects) @objects.delete(resource.title) if @objects.include?(resource.title) @aliases.delete(resource.title) if @aliases.include?(resource.title) if @aliases.has_value?(resource) names = [] @aliases.each do |name, otherres| if otherres == resource names << name end end names.each { |name| @aliases.delete(name) } end end # iterate across each of the type's instances def self.each raise "Global resource iteration is deprecated" return unless defined?(@objects) @objects.each { |name,instance| yield instance } end # does the type have an object with the given name? def self.has_key?(name) raise "Global resource access is deprecated" @objects.has_key?(name) end # Retrieve all known instances. Either requires providers or must be overridden. def self.instances raise Puppet::DevError, "#{self.name} has no providers and has not overridden 'instances'" if provider_hash.empty? # Put the default provider first, then the rest of the suitable providers. provider_instances = {} providers_by_source.collect do |provider| provider.instances.collect do |instance| # We always want to use the "first" provider instance we find, unless the resource # is already managed and has a different provider set if other = provider_instances[instance.name] Puppet.warning "%s %s found in both %s and %s; skipping the %s version" % [self.name.to_s.capitalize, instance.name, other.class.name, instance.class.name, instance.class.name] next end provider_instances[instance.name] = instance new(:name => instance.name, :provider => instance, :audit => :all) end end.flatten.compact end # Return a list of one suitable provider per source, with the default provider first. def self.providers_by_source # Put the default provider first, then the rest of the suitable providers. sources = [] [defaultprovider, suitableprovider].flatten.uniq.collect do |provider| next if sources.include?(provider.source) sources << provider.source provider end.compact end # Convert a simple hash into a Resource instance. def self.hash2resource(hash) hash = hash.inject({}) { |result, ary| result[ary[0].to_sym] = ary[1]; result } title = hash.delete(:title) title ||= hash[:name] title ||= hash[key_attributes.first] if key_attributes.length == 1 raise Puppet::Error, "Title or name must be provided" unless title # Now create our resource. resource = Puppet::Resource.new(self.name, title) [:catalog].each do |attribute| if value = hash[attribute] hash.delete(attribute) resource.send(attribute.to_s + "=", value) end end hash.each do |param, value| resource[param] = value end resource end # Create the path for logging and such. def pathbuilder if p = parent [p.pathbuilder, self.ref].flatten else [self.ref] end end ############################### # Add all of the meta parameters. newmetaparam(:noop) do desc "Boolean flag indicating whether work should actually be done." newvalues(:true, :false) munge do |value| case value when true, :true, "true"; @resource.noop = true when false, :false, "false"; @resource.noop = false end end end newmetaparam(:schedule) do desc "On what schedule the object should be managed. You must create a schedule object, and then reference the name of that object to use that for your schedule: schedule { daily: period => daily, range => \"2-4\" } exec { \"/usr/bin/apt-get update\": schedule => daily } The creation of the schedule object does not need to appear in the configuration before objects that use it." end newmetaparam(:audit) do desc "Marks a subset of this resource's unmanaged attributes for auditing. Accepts an attribute name or a list of attribute names. Auditing a resource attribute has two effects: First, whenever a catalog is applied with puppet apply or puppet agent, Puppet will check whether that attribute of the resource has been modified, comparing its current value to the previous run; any change will be logged alongside any actions performed by Puppet while applying the catalog. Secondly, marking a resource attribute for auditing will include that attribute in inspection reports generated by puppet inspect; see the puppet inspect documentation for more details. Managed attributes for a resource can also be audited, but note that changes made by Puppet will be logged as additional modifications. (I.e. if a user manually edits a file whose contents are audited and managed, puppet agent's next two runs will both log an audit notice: the first run will log the user's edit and then revert the file to the desired state, and the second run will log the edit made by Puppet.)" validate do |list| list = Array(list).collect {|p| p.to_sym} unless list == [:all] list.each do |param| next if @resource.class.validattr?(param) fail "Cannot audit #{param}: not a valid attribute for #{resource}" end end end munge do |args| properties_to_audit(args).each do |param| next unless resource.class.validproperty?(param) resource.newattr(param) end end def all_properties resource.class.properties.find_all do |property| resource.provider.nil? or resource.provider.class.supports_parameter?(property) end.collect do |property| property.name end end def properties_to_audit(list) if !list.kind_of?(Array) && list.to_sym == :all list = all_properties else list = Array(list).collect { |p| p.to_sym } end end end newmetaparam(:check) do desc "Audit specified attributes of resources over time, and report if any have changed. This parameter has been deprecated in favor of 'audit'." munge do |args| resource.warning "'check' attribute is deprecated; use 'audit' instead" resource[:audit] = args end end newmetaparam(:loglevel) do desc "Sets the level that information will be logged. The log levels have the biggest impact when logs are sent to syslog (which is currently the default)." defaultto :notice newvalues(*Puppet::Util::Log.levels) newvalues(:verbose) munge do |loglevel| val = super(loglevel) if val == :verbose val = :info end val end end newmetaparam(:alias) do desc "Creates an alias for the object. Puppet uses this internally when you provide a symbolic name: file { sshdconfig: path => $operatingsystem ? { solaris => \"/usr/local/etc/ssh/sshd_config\", default => \"/etc/ssh/sshd_config\" }, source => \"...\" } service { sshd: subscribe => File[sshdconfig] } When you use this feature, the parser sets `sshdconfig` as the name, and the library sets that as an alias for the file so the dependency lookup for `sshd` works. You can use this parameter yourself, but note that only the library can use these aliases; for instance, the following code will not work: file { \"/etc/ssh/sshd_config\": owner => root, group => root, alias => sshdconfig } file { sshdconfig: mode => 644 } There's no way here for the Puppet parser to know that these two stanzas should be affecting the same file. See the [Language Tutorial](http://docs.puppetlabs.com/guides/language_tutorial.html) for more information. " munge do |aliases| aliases = [aliases] unless aliases.is_a?(Array) raise(ArgumentError, "Cannot add aliases without a catalog") unless @resource.catalog aliases.each do |other| if obj = @resource.catalog.resource(@resource.class.name, other) unless obj.object_id == @resource.object_id self.fail("#{@resource.title} can not create alias #{other}: object already exists") end next end # Newschool, add it to the catalog. @resource.catalog.alias(@resource, other) end end end newmetaparam(:tag) do desc "Add the specified tags to the associated resource. While all resources are automatically tagged with as much information as possible (e.g., each class and definition containing the resource), it can be useful to add your own tags to a given resource. Tags are currently useful for things like applying a subset of a host's configuration: puppet agent --test --tags mytag This way, when you're testing a configuration you can run just the portion you're testing." munge do |tags| tags = [tags] unless tags.is_a? Array tags.each do |tag| @resource.tag(tag) end end end class RelationshipMetaparam < Puppet::Parameter class << self attr_accessor :direction, :events, :callback, :subclasses end @subclasses = [] def self.inherited(sub) @subclasses << sub end def munge(references) references = [references] unless references.is_a?(Array) references.collect do |ref| if ref.is_a?(Puppet::Resource) ref else Puppet::Resource.new(ref) end end end def validate_relationship @value.each do |ref| unless @resource.catalog.resource(ref.to_s) description = self.class.direction == :in ? "dependency" : "dependent" fail "Could not find #{description} #{ref} for #{resource.ref}" end end end # Create edges from each of our relationships. :in # relationships are specified by the event-receivers, and :out # relationships are specified by the event generator. This # way 'source' and 'target' are consistent terms in both edges # and events -- that is, an event targets edges whose source matches # the event's source. The direction of the relationship determines # which resource is applied first and which resource is considered # to be the event generator. def to_edges @value.collect do |reference| reference.catalog = resource.catalog # Either of the two retrieval attempts could have returned # nil. unless related_resource = reference.resolve self.fail "Could not retrieve dependency '#{reference}' of #{@resource.ref}" end # Are we requiring them, or vice versa? See the method docs # for futher info on this. if self.class.direction == :in source = related_resource target = @resource else source = @resource target = related_resource end if method = self.class.callback subargs = { :event => self.class.events, :callback => method } self.debug("subscribes to #{related_resource.ref}") else # If there's no callback, there's no point in even adding # a label. subargs = nil self.debug("requires #{related_resource.ref}") end rel = Puppet::Relationship.new(source, target, subargs) end end end def self.relationship_params RelationshipMetaparam.subclasses end # Note that the order in which the relationships params is defined # matters. The labelled params (notify and subcribe) must be later, # so that if both params are used, those ones win. It's a hackish # solution, but it works. newmetaparam(:require, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :NONE}) do desc "One or more objects that this object depends on. This is used purely for guaranteeing that changes to required objects happen before the dependent object. For instance: # Create the destination directory before you copy things down file { \"/usr/local/scripts\": ensure => directory } file { \"/usr/local/scripts/myscript\": source => \"puppet://server/module/myscript\", mode => 755, require => File[\"/usr/local/scripts\"] } Multiple dependencies can be specified by providing a comma-seperated list of resources, enclosed in square brackets: require => [ File[\"/usr/local\"], File[\"/usr/local/scripts\"] ] Note that Puppet will autorequire everything that it can, and there are hooks in place so that it's easy for resources to add new ways to autorequire objects, so if you think Puppet could be smarter here, let us know. In fact, the above code was redundant -- Puppet will autorequire any parent directories that are being managed; it will automatically realize that the parent directory should be created before the script is pulled down. Currently, exec resources will autorequire their CWD (if it is specified) plus any fully qualified paths that appear in the command. For instance, if you had an `exec` command that ran the `myscript` mentioned above, the above code that pulls the file down would be automatically listed as a requirement to the `exec` code, so that you would always be running againts the most recent version. " end newmetaparam(:subscribe, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :ALL_EVENTS, :callback => :refresh}) do desc "One or more objects that this object depends on. Changes in the subscribed to objects result in the dependent objects being refreshed (e.g., a service will get restarted). For instance: class nagios { file { \"/etc/nagios/nagios.conf\": source => \"puppet://server/module/nagios.conf\", alias => nagconf # just to make things easier for me } service { nagios: ensure => running, subscribe => File[nagconf] } } Currently the `exec`, `mount` and `service` type support refreshing. " end newmetaparam(:before, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :NONE}) do desc %{This parameter is the opposite of **require** -- it guarantees that the specified object is applied later than the specifying object: file { "/var/nagios/configuration": source => "...", recurse => true, before => Exec["nagios-rebuid"] } exec { "nagios-rebuild": command => "/usr/bin/make", cwd => "/var/nagios/configuration" } This will make sure all of the files are up to date before the make command is run.} end newmetaparam(:notify, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :ALL_EVENTS, :callback => :refresh}) do desc %{This parameter is the opposite of **subscribe** -- it sends events to the specified object: file { "/etc/sshd_config": source => "....", notify => Service[sshd] } service { sshd: ensure => running } This will restart the sshd service if the sshd config file changes.} end newmetaparam(:stage) do desc %{Which run stage a given resource should reside in. This just creates a dependency on or from the named milestone. For instance, saying that this is in the 'bootstrap' stage creates a dependency on the 'bootstrap' milestone. By default, all classes get directly added to the 'main' stage. You can create new stages as resources: stage { [pre, post]: } To order stages, use standard relationships: stage { pre: before => Stage[main] } Or use the new relationship syntax: Stage[pre] -> Stage[main] -> Stage[post] Then use the new class parameters to specify a stage: class { foo: stage => pre } Stages can only be set on classes, not individual resources. This will fail: file { '/foo': stage => pre, ensure => file } } end ############################### # All of the provider plumbing for the resource types. require 'puppet/provider' require 'puppet/util/provider_features' # Add the feature handling module. extend Puppet::Util::ProviderFeatures attr_reader :provider # the Type class attribute accessors class << self attr_accessor :providerloader attr_writer :defaultprovider end # Find the default provider. def self.defaultprovider unless @defaultprovider suitable = suitableprovider # Find which providers are a default for this system. defaults = suitable.find_all { |provider| provider.default? } # If we don't have any default we use suitable providers defaults = suitable if defaults.empty? max = defaults.collect { |provider| provider.specificity }.max defaults = defaults.find_all { |provider| provider.specificity == max } retval = nil if defaults.length > 1 Puppet.warning( "Found multiple default providers for #{self.name}: #{defaults.collect { |i| i.name.to_s }.join(", ")}; using #{defaults[0].name}" ) retval = defaults.shift elsif defaults.length == 1 retval = defaults.shift else raise Puppet::DevError, "Could not find a default provider for #{self.name}" end @defaultprovider = retval end @defaultprovider end def self.provider_hash_by_type(type) @provider_hashes ||= {} @provider_hashes[type] ||= {} end def self.provider_hash Puppet::Type.provider_hash_by_type(self.name) end # Retrieve a provider by name. def self.provider(name) name = Puppet::Util.symbolize(name) # If we don't have it yet, try loading it. @providerloader.load(name) unless provider_hash.has_key?(name) provider_hash[name] end # Just list all of the providers. def self.providers provider_hash.keys end def self.validprovider?(name) name = Puppet::Util.symbolize(name) (provider_hash.has_key?(name) && provider_hash[name].suitable?) end # Create a new provider of a type. This method must be called # directly on the type that it's implementing. def self.provide(name, options = {}, &block) name = Puppet::Util.symbolize(name) if obj = provider_hash[name] Puppet.debug "Reloading #{name} #{self.name} provider" unprovide(name) end parent = if pname = options[:parent] options.delete(:parent) if pname.is_a? Class pname else if provider = self.provider(pname) provider else raise Puppet::DevError, "Could not find parent provider #{pname} of #{name}" end end else Puppet::Provider end options[:resource_type] ||= self self.providify provider = genclass( name, :parent => parent, :hash => provider_hash, :prefix => "Provider", :block => block, :include => feature_module, :extend => feature_module, :attributes => options ) provider end # Make sure we have a :provider parameter defined. Only gets called if there # are providers. def self.providify return if @paramhash.has_key? :provider newparam(:provider) do desc "The specific backend for #{self.name.to_s} to use. You will seldom need to specify this -- Puppet will usually discover the appropriate provider for your platform." # This is so we can refer back to the type to get a list of # providers for documentation. class << self attr_accessor :parenttype end # We need to add documentation for each provider. def self.doc @doc + " Available providers are:\n\n" + parenttype.providers.sort { |a,b| a.to_s <=> b.to_s }.collect { |i| "* **#{i}**: #{parenttype().provider(i).doc}" }.join("\n") end defaultto { @resource.class.defaultprovider.name } validate do |provider_class| provider_class = provider_class[0] if provider_class.is_a? Array provider_class = provider_class.class.name if provider_class.is_a?(Puppet::Provider) unless provider = @resource.class.provider(provider_class) raise ArgumentError, "Invalid #{@resource.class.name} provider '#{provider_class}'" end end munge do |provider| provider = provider[0] if provider.is_a? Array provider = provider.intern if provider.is_a? String @resource.provider = provider if provider.is_a?(Puppet::Provider) provider.class.name else provider end end end.parenttype = self end def self.unprovide(name) if provider_hash.has_key? name rmclass( name, :hash => provider_hash, :prefix => "Provider" ) if @defaultprovider and @defaultprovider.name == name @defaultprovider = nil end end end # Return an array of all of the suitable providers. def self.suitableprovider providerloader.loadall if provider_hash.empty? provider_hash.find_all { |name, provider| provider.suitable? }.collect { |name, provider| provider }.reject { |p| p.name == :fake } # For testing end def provider=(name) if name.is_a?(Puppet::Provider) @provider = name @provider.resource = self elsif klass = self.class.provider(name) @provider = klass.new(self) else raise ArgumentError, "Could not find #{name} provider of #{self.class.name}" end end ############################### # All of the relationship code. # Specify a block for generating a list of objects to autorequire. This # makes it so that you don't have to manually specify things that you clearly # require. def self.autorequire(name, &block) @autorequires ||= {} @autorequires[name] = block end # Yield each of those autorequires in turn, yo. def self.eachautorequire @autorequires ||= {} @autorequires.each { |type, block| yield(type, block) } end # Figure out of there are any objects we can automatically add as # dependencies. def autorequire(rel_catalog = nil) rel_catalog ||= catalog raise(Puppet::DevError, "You cannot add relationships without a catalog") unless rel_catalog reqs = [] self.class.eachautorequire { |type, block| # Ignore any types we can't find, although that would be a bit odd. next unless typeobj = Puppet::Type.type(type) # Retrieve the list of names from the block. next unless list = self.instance_eval(&block) list = [list] unless list.is_a?(Array) # Collect the current prereqs list.each { |dep| obj = nil # Support them passing objects directly, to save some effort. unless dep.is_a? Puppet::Type # Skip autorequires that we aren't managing unless dep = rel_catalog.resource(type, dep) next end end reqs << Puppet::Relationship.new(dep, self) } } reqs end # Build the dependencies associated with an individual object. def builddepends # Handle the requires self.class.relationship_params.collect do |klass| if param = @parameters[klass.name] param.to_edges end end.flatten.reject { |r| r.nil? } end # Define the initial list of tags. def tags=(list) tag(self.class.name) tag(*list) end # 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_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 @defaults = {} @parameters ||= [] @validproperties = {} @properties = [] @parameters = [] @paramhash = {} @attr_aliases = {} @paramdoc = Hash.new { |hash,key| key = key.intern if key.is_a?(String) if hash.include?(key) hash[key] else "Param Documentation for #{key} not found" end } @doc ||= "" 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 # is the resource exported attr_accessor :exported # is the resource virtual (it should not :-)) attr_accessor :virtual # 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 attr_reader :original_parameters # initialize the type instance def initialize(resource) raise Puppet::DevError, "Got TransObject instead of Resource or hash" if resource.is_a?(Puppet::TransObject) resource = self.class.hash2resource(resource) unless resource.is_a?(Puppet::Resource) # The list of parameter/property instances. @parameters = {} # Set the title first, so any failures print correctly. if resource.type.to_s.downcase.to_sym == self.class.name self.title = resource.title else # This should only ever happen for components self.title = resource.ref end [:file, :line, :catalog, :exported, :virtual].each do |getter| setter = getter.to_s + "=" if val = resource.send(getter) self.send(setter, val) end end @tags = resource.tags @original_parameters = resource.to_hash set_name(@original_parameters) set_default(:provider) set_parameters(@original_parameters) self.validate if self.respond_to?(:validate) end private # Set our resource's name. def set_name(hash) self[name_var] = hash.delete(name_var) if name_var end # Set all of the parameters from a hash, in the appropriate order. def set_parameters(hash) # Use the order provided by allattrs, but add in any # extra attributes from the resource so we get failures # on invalid attributes. no_values = [] (self.class.allattrs + hash.keys).uniq.each do |attr| begin # Set any defaults immediately. This is mostly done so # that the default provider is available for any other # property validation. if hash.has_key?(attr) self[attr] = hash[attr] else no_values << attr end rescue ArgumentError, Puppet::Error, TypeError raise rescue => detail error = Puppet::DevError.new( "Could not set #{attr} on #{self.class.name}: #{detail}") error.set_backtrace(detail.backtrace) raise error end end no_values.each do |attr| set_default(attr) end end public # Set up all of our autorequires. def finish # 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 # For now, leave the 'name' method functioning like it used to. Once 'title' # works everywhere, I'll switch it. def name self[:name] end # Look up our parent in the catalog, if we have one. def parent return nil unless catalog unless defined?(@parent) if parents = catalog.adjacent(self, :direction => :in) # 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 "#{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 @title if self.class.validparameter?(name_var) @title = self[:name] elsif self.class.validproperty?(name_var) @title = self.should(name_var) else self.devfail "Could not find namevar #{name_var} for #{self.class.name}" end end @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_resource values.each do |name, value| name = name.name if name.respond_to? :name trans[name] = value end @parameters.each do |name, param| # Avoid adding each instance name twice 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' trans end def to_resource # this 'type instance' versus 'resource' distinction seems artificial # I'd like to see it collapsed someday ~JW self.to_trans.to_resource end def virtual?; !!@virtual; end def exported?; !!@exported; end end end require 'puppet/provider' # Always load these types. -require 'puppet/type/component' +Puppet::Type.type(:component) diff --git a/lib/puppet/type/augeas.rb b/lib/puppet/type/augeas.rb index a8fb1f15f..f4d3c43e1 100644 --- a/lib/puppet/type/augeas.rb +++ b/lib/puppet/type/augeas.rb @@ -1,182 +1,178 @@ -#-- -# Copyright (C) 2008 Red Hat Inc. # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. +# Copyright 2011 Bryan Kearney # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# You should have received a copy of the GNU General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# http://www.apache.org/licenses/LICENSE-2.0 # -# Author: Bryan Kearney +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. Puppet::Type.newtype(:augeas) do include Puppet::Util feature :parse_commands, "Parse the command string" feature :need_to_run?, "If the command should run" feature :execute_changes, "Actually make the changes" @doc = "Apply the changes (single or array of changes) to the filesystem via the augeas tool. Requires: - augeas to be installed (http://www.augeas.net) - ruby-augeas bindings Sample usage with a string: augeas{\"test1\" : context => \"/files/etc/sysconfig/firstboot\", changes => \"set RUN_FIRSTBOOT YES\", onlyif => \"match other_value size > 0\", } Sample usage with an array and custom lenses: augeas{\"jboss_conf\": context => \"/files\", changes => [ \"set /etc/jbossas/jbossas.conf/JBOSS_IP $ipaddress\", \"set /etc/jbossas/jbossas.conf/JAVA_HOME /usr\" ], load_path => \"$/usr/share/jbossas/lenses\", } " newparam (:name) do desc "The name of this task. Used for uniqueness" isnamevar end newparam (:context) do desc "Optional context path. This value is prepended to the paths of all changes if the path is relative. If INCL is set, defaults to '/files' + INCL, otherwise the empty string" defaultto "" munge do |value| if value.empty? and resource[:incl] "/files" + resource[:incl] else value end end end newparam (:onlyif) do desc "Optional augeas command and comparisons to control the execution of this type. Supported onlyif syntax: get [AUGEAS_PATH] [COMPARATOR] [STRING] match [MATCH_PATH] size [COMPARATOR] [INT] match [MATCH_PATH] include [STRING] match [MATCH_PATH] not_include [STRING] match [MATCH_PATH] == [AN_ARRAY] match [MATCH_PATH] != [AN_ARRAY] where: AUGEAS_PATH is a valid path scoped by the context MATCH_PATH is a valid match synatx scoped by the context COMPARATOR is in the set [> >= != == <= <] STRING is a string INT is a number AN_ARRAY is in the form ['a string', 'another']" defaultto "" end newparam(:changes) do desc "The changes which should be applied to the filesystem. This can be either a string which contains a command or an array of commands. Commands supported are: set [PATH] [VALUE] Sets the value VALUE at loction PATH rm [PATH] Removes the node at location PATH remove [PATH] Synonym for rm clear [PATH] Keeps the node at PATH, but removes the value. ins [LABEL] [WHERE] [PATH] Inserts an empty node LABEL either [WHERE={before|after}] PATH. insert [LABEL] [WHERE] [PATH] Synonym for ins If the parameter 'context' is set that value is prepended to PATH" end newparam(:root) do desc "A file system path; all files loaded by Augeas are loaded underneath ROOT" defaultto "/" end newparam(:load_path) do desc "Optional colon separated list of directories; these directories are searched for schema definitions" defaultto "" end newparam(:force) do desc "Optional command to force the augeas type to execute even if it thinks changes will not be made. This does not overide the only setting. If onlyif is set, then the foce setting will not override that result" defaultto false end newparam(:type_check) do desc "Set to true if augeas should perform typechecking. Optional, defaults to false" newvalues(:true, :false) defaultto :false end newparam(:lens) do desc "Use a specific lens, e.g. `Hosts.lns`. When this parameter is set, you must also set the incl parameter to indicate which file to load. Only that file will be loaded, which greatly speeds up execution of the type" end newparam(:incl) do desc "Load only a specific file, e.g. `/etc/hosts`. When this parameter is set, you must also set the lens parameter to indicate which lens to use." end validate do has_lens = !self[:lens].nil? has_incl = !self[:incl].nil? self.fail "You must specify both the lens and incl parameters, or neither" if has_lens != has_incl end # This is the acutal meat of the code. It forces # augeas to be run and fails or not based on the augeas return # code. newproperty(:returns) do |property| include Puppet::Util desc "The expected return code from the augeas command. Should not be set" defaultto 0 # Make output a bit prettier def change_to_s(currentvalue, newvalue) "executed successfully" end # if the onlyif resource is provided, then the value is parsed. # a return value of 0 will stop exection because it matches the # default value. def retrieve if @resource.provider.need_to_run?() :need_to_run else 0 end end # Actually execute the command. def sync @resource.provider.execute_changes end end end diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index 4f6ea733c..5083ca556 100755 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -1,412 +1,412 @@ require 'etc' require 'facter' require 'puppet/util/filetype' Puppet::Type.newtype(:cron) do @doc = "Installs and manages cron jobs. All fields except the command and the user are optional, although specifying no periodic fields would result in the command being executed every minute. While the name of the cron job is not part of the actual job, it is used by Puppet to store and retrieve it. If you specify a cron job that matches an existing job in every way except name, then the jobs will be considered equivalent and the new name will be permanently associated with that job. Once this association is made and synced to disk, you can then manage the job normally (e.g., change the schedule of the job). Example: cron { logrotate: command => \"/usr/sbin/logrotate\", user => root, hour => 2, minute => 0 } Note that all cron values can be specified as an array of values: cron { logrotate: command => \"/usr/sbin/logrotate\", user => root, hour => [2, 4] } Or using ranges, or the step syntax `*/2` (although there's no guarantee that your `cron` daemon supports it): cron { logrotate: command => \"/usr/sbin/logrotate\", user => root, hour => ['2-4'], minute => '*/10' } " ensurable # A base class for all of the Cron parameters, since they all have # similar argument checking going on. class CronParam < Puppet::Property class << self attr_accessor :boundaries, :default end # We have to override the parent method, because we consume the entire # "should" array def insync?(is) self.is_to_s(is) == self.should_to_s end # A method used to do parameter input handling. Converts integers # in string form to actual integers, and returns the value if it's # an integer or false if it's just a normal string. def numfix(num) if num =~ /^\d+$/ return num.to_i elsif num.is_a?(Integer) return num else return false end end # Verify that a number is within the specified limits. Return the # number if it is, or false if it is not. def limitcheck(num, lower, upper) (num >= lower and num <= upper) && num end # Verify that a value falls within the specified array. Does case # insensitive matching, and supports matching either the entire word # or the first three letters of the word. def alphacheck(value, ary) tmp = value.downcase # If they specified a shortened version of the name, then see # if we can lengthen it (e.g., mon => monday). if tmp.length == 3 ary.each_with_index { |name, index| if name =~ /#{tmp}/i return index end } else return ary.index(tmp) if ary.include?(tmp) end false end def should_to_s(newvalue = @should) if newvalue newvalue = [newvalue] unless newvalue.is_a?(Array) if self.name == :command or newvalue[0].is_a? Symbol newvalue[0] else newvalue.join(",") end else nil end end def is_to_s(currentvalue = @is) if currentvalue return currentvalue unless currentvalue.is_a?(Array) if self.name == :command or currentvalue[0].is_a? Symbol currentvalue[0] else currentvalue.join(",") end else nil end end def should if @should and @should[0] == :absent :absent else @should end end def should=(ary) super @should.flatten! end # The method that does all of the actual parameter value # checking; called by all of the +param=+ methods. # Requires the value, type, and bounds, and optionally supports # a boolean of whether to do alpha checking, and if so requires # the ary against which to do the checking. munge do |value| # Support 'absent' as a value, so that they can remove # a value if value == "absent" or value == :absent return :absent end # Allow the */2 syntax if value =~ /^\*\/[0-9]+$/ return value end # Allow ranges if value =~ /^[0-9]+-[0-9]+$/ return value end # Allow ranges + */2 if value =~ /^[0-9]+-[0-9]+\/[0-9]+$/ return value end if value == "*" return :absent end return value unless self.class.boundaries lower, upper = self.class.boundaries retval = nil if num = numfix(value) retval = limitcheck(num, lower, upper) elsif respond_to?(:alpha) # If it has an alpha method defined, then we check # to see if our value is in that list and if so we turn # it into a number retval = alphacheck(value, alpha) end if retval return retval.to_s else self.fail "#{value} is not a valid #{self.class.name}" end end end # Somewhat uniquely, this property does not actually change anything -- it # just calls +@resource.sync+, which writes out the whole cron tab for # the user in question. There is no real way to change individual cron # jobs without rewriting the entire cron file. # # Note that this means that managing many cron jobs for a given user # could currently result in multiple write sessions for that user. newproperty(:command, :parent => CronParam) do desc "The command to execute in the cron job. The environment provided to the command varies by local system rules, and it is best to always provide a fully qualified command. The user's profile is not sourced when the command is run, so if the user's environment is desired it should be sourced manually. All cron parameters support `absent` as a value; this will remove any existing values for that field." def retrieve return_value = super return_value = return_value[0] if return_value && return_value.is_a?(Array) return_value end def should if @should if @should.is_a? Array @should[0] else devfail "command is not an array" end else nil end end end newproperty(:special) do desc "A special value such as 'reboot' or 'annually'. Only available on supported systems such as Vixie Cron. Overrides more specific time of day/week settings." def specials %w{reboot yearly annually monthly weekly daily midnight hourly} end validate do |value| raise ArgumentError, "Invalid special schedule #{value.inspect}" unless specials.include?(value) end end newproperty(:minute, :parent => CronParam) do self.boundaries = [0, 59] desc "The minute at which to run the cron job. Optional; if specified, must be between 0 and 59, inclusive." end newproperty(:hour, :parent => CronParam) do self.boundaries = [0, 23] desc "The hour at which to run the cron job. Optional; if specified, must be between 0 and 23, inclusive." end newproperty(:weekday, :parent => CronParam) do def alpha %w{sunday monday tuesday wednesday thursday friday saturday} end self.boundaries = [0, 7] desc "The weekday on which to run the command. Optional; if specified, must be between 0 and 7, inclusive, with 0 (or 7) being Sunday, or must be the name of the day (e.g., Tuesday)." end newproperty(:month, :parent => CronParam) do def alpha %w{january february march april may june july august september october november december} end self.boundaries = [1, 12] desc "The month of the year. Optional; if specified must be between 1 and 12 or the month name (e.g., December)." end newproperty(:monthday, :parent => CronParam) do self.boundaries = [1, 31] desc "The day of the month on which to run the command. Optional; if specified, must be between 1 and 31." end newproperty(:environment) do desc "Any environment settings associated with this cron job. They will be stored between the header and the job in the crontab. There can be no guarantees that other, earlier settings will not also affect a given cron job. Also, Puppet cannot automatically determine whether an existing, unmanaged environment setting is associated with a given cron job. If you already have cron jobs with environment settings, then Puppet will keep those settings in the same place in the file, but will not associate them with a specific job. Settings should be specified exactly as they should appear in the crontab, e.g., `PATH=/bin:/usr/bin:/usr/sbin`." validate do |value| unless value =~ /^\s*(\w+)\s*=\s*(.*)\s*$/ or value == :absent or value == "absent" raise ArgumentError, "Invalid environment setting #{value.inspect}" end end def insync?(is) if is.is_a? Array return is.sort == @should.sort else return is == @should end end def is_to_s(newvalue) if newvalue if newvalue.is_a?(Array) newvalue.join(",") else newvalue end else nil end end def should @should end def should_to_s(newvalue = @should) if newvalue newvalue.join(",") else nil end end end newparam(:name) do desc "The symbolic name of the cron job. This name is used for human reference only and is generated automatically for cron jobs found on the system. This generally won't matter, as Puppet will do its best to match existing cron jobs against specified jobs (and Puppet adds a comment to cron jobs it adds), but it is at least possible that converting from unmanaged jobs to managed jobs might require manual intervention." isnamevar end newproperty(:user) do desc "The user to run the command as. This user must be allowed to run cron jobs, which is not currently checked by Puppet. The user defaults to whomever Puppet is running as." defaultto { Etc.getpwuid(Process.uid).name || "root" } end newproperty(:target) do desc "Where the cron job should be stored. For crontab-style entries this is the same as the user and defaults that way. Other providers default accordingly." defaultto { if provider.is_a?(@resource.class.provider(:crontab)) if val = @resource.should(:user) val else raise ArgumentError, "You must provide a user with crontab entries" end elsif provider.class.ancestors.include?(Puppet::Provider::ParsedFile) provider.class.default_target else nil end } end # We have to reorder things so that :provide is before :target attr_accessor :uid def value(name) name = symbolize(name) ret = nil if obj = @parameters[name] ret = obj.should ret ||= obj.retrieve if ret == :absent ret = nil end end unless ret case name when :command - devfail "No command, somehow" + devfail "No command, somehow" unless @parameters[:ensure].value == :absent when :special # nothing else #ret = (self.class.validproperty?(name).default || "*").to_s ret = "*" end end ret end end diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index d3b3a48eb..76c646baf 100755 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -1,182 +1,183 @@ require 'puppet/file_serving/content' require 'puppet/file_serving/metadata' module Puppet # Copy files from a local or remote source. This state *only* does any work # when the remote file is an actual file; in that case, this state copies # the file down. If the remote file is a dir or a link or whatever, then # this state, during retrieval, modifies the appropriate other states # so that things get taken care of appropriately. Puppet::Type.type(:file).newparam(:source) do include Puppet::Util::Diff attr_accessor :source, :local desc "Copy a file over the current file. Uses `checksum` to determine when a file should be copied. Valid values are either fully qualified paths to files, or URIs. Currently supported URI types are *puppet* and *file*. This is one of the primary mechanisms for getting content into applications that Puppet does not directly support and is very useful for those configuration files that don't change much across sytems. For instance: class sendmail { file { \"/etc/mail/sendmail.cf\": source => \"puppet://server/modules/module_name/sendmail.cf\" } } You can also leave out the server name, in which case `puppet agent` will fill in the name of its configuration server and `puppet apply` will use the local filesystem. This makes it easy to use the same configuration in both local and centralized forms. Currently, only the `puppet` scheme is supported for source URL's. Puppet will connect to the file server running on `server` to retrieve the contents of the file. If the `server` part is empty, the behavior of the command-line interpreter (`puppet apply`) and the client demon (`puppet agent`) differs slightly: `apply` will look such a file up on the module path on the local host, whereas `agent` will connect to the puppet server that it received the manifest from. See the [fileserver configuration documentation](http://projects.puppetlabs.com/projects/puppet/wiki/File_Serving_Configuration) for information on how to configure and use file services within Puppet. If you specify multiple file sources for a file, then the first source that exists will be used. This allows you to specify what amount to search paths for files: file { \"/path/to/my/file\": source => [ \"/modules/nfs/files/file.$host\", \"/modules/nfs/files/file.$operatingsystem\", \"/modules/nfs/files/file\" ] } This will use the first found file as the source. You cannot currently copy links using this mechanism; set `links` to `follow` if any remote sources are links. " validate do |sources| sources = [sources] unless sources.is_a?(Array) sources.each do |source| begin uri = URI.parse(URI.escape(source)) rescue => detail self.fail "Could not understand source #{source}: #{detail}" end self.fail "Cannot use URLs of type '#{uri.scheme}' as source for fileserving" unless uri.scheme.nil? or %w{file puppet}.include?(uri.scheme) end end munge do |sources| sources = [sources] unless sources.is_a?(Array) sources.collect { |source| source.sub(/\/$/, '') } end def change_to_s(currentvalue, newvalue) # newvalue = "{md5}#{@metadata.checksum}" if @resource.property(:ensure).retrieve == :absent return "creating from source #{metadata.source} with contents #{metadata.checksum}" else return "replacing from source #{metadata.source} with contents #{metadata.checksum}" end end def checksum metadata && metadata.checksum end # Look up (if necessary) and return remote content. cached_attr(:content) do raise Puppet::DevError, "No source for content was stored with the metadata" unless metadata.source unless tmp = Puppet::FileServing::Content.indirection.find(metadata.source) fail "Could not find any content at %s" % metadata.source end tmp.content end # Copy the values from the source to the resource. Yay. def copy_source_values devfail "Somehow got asked to copy source values without any metadata" unless metadata # Take each of the stats and set them as states on the local file # if a value has not already been provided. [:owner, :mode, :group, :checksum].each do |metadata_method| param_name = (metadata_method == :checksum) ? :content : metadata_method next if metadata_method == :owner and !Puppet.features.root? next if metadata_method == :checksum and metadata.ftype == "directory" + next if metadata_method == :checksum and metadata.ftype == "link" and metadata.links == :manage if resource[param_name].nil? or resource[param_name] == :absent resource[param_name] = metadata.send(metadata_method) end end if resource[:ensure] == :absent # We know all we need to elsif metadata.ftype != "link" resource[:ensure] = metadata.ftype elsif @resource[:links] == :follow resource[:ensure] = :present else resource[:ensure] = "link" resource[:target] = metadata.destination end end def found? ! (metadata.nil? or metadata.ftype.nil?) end # Provide, and retrieve if necessary, the metadata for this file. Fail # if we can't find data about this host, and fail if there are any # problems in our query. cached_attr(:metadata) do return nil unless value result = nil value.each do |source| begin if data = Puppet::FileServing::Metadata.indirection.find(source) result = data result.source = source break end rescue => detail fail detail, "Could not retrieve file metadata for #{source}: #{detail}" end end fail "Could not retrieve information from source(s) #{value.join(", ")}" unless result result end def local? found? and uri and (uri.scheme || "file") == "file" end def full_path URI.unescape(uri.path) if found? and uri end def server (uri and uri.host) or Puppet.settings[:server] end def port (uri and uri.port) or Puppet.settings[:masterport] end private def uri @uri ||= URI.parse(URI.escape(metadata.source)) end end end diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb index 2573633f9..b534ef275 100755 --- a/lib/puppet/type/group.rb +++ b/lib/puppet/type/group.rb @@ -1,136 +1,145 @@ - require 'etc' require 'facter' require 'puppet/property/keyvalue' module Puppet newtype(:group) do @doc = "Manage groups. On most platforms this can only create groups. Group membership must be managed on individual users. On some platforms such as OS X, group membership is managed as an attribute of the group, not the user record. Providers must have the feature 'manages_members' to manage the 'members' property of a group record." feature :manages_members, "For directories where membership is an attribute of groups not users." feature :manages_aix_lam, "The provider can manage AIX Loadable Authentication Module (LAM) system." + feature :system_groups, + "The provider allows you to create system groups with lower GIDs." + ensurable do desc "Create or remove the group." newvalue(:present) do provider.create end newvalue(:absent) do provider.delete end end newproperty(:gid) do desc "The group ID. Must be specified numerically. If not specified, a number will be picked, which can result in ID differences across systems and thus is not recommended. The GID is picked according to local system standards." def retrieve provider.gid end def sync if self.should == :absent raise Puppet::DevError, "GID cannot be deleted" else provider.gid = self.should end end munge do |gid| case gid when String if gid =~ /^[-0-9]+$/ gid = Integer(gid) else self.fail "Invalid GID #{gid}" end when Symbol unless gid == :absent self.devfail "Invalid GID #{gid}" end end return gid end end newproperty(:members, :array_matching => :all, :required_features => :manages_members) do desc "The members of the group. For directory services where group membership is stored in the group objects, not the users." def change_to_s(currentvalue, newvalue) currentvalue = currentvalue.join(",") if currentvalue != :absent newvalue = newvalue.join(",") super(currentvalue, newvalue) end end newparam(:auth_membership) do desc "whether the provider is authoritative for group membership." defaultto true end newparam(:name) do desc "The group name. While naming limitations vary by system, it is advisable to keep the name to the degenerate limitations, which is a maximum of 8 characters beginning with a letter." isnamevar end newparam(:allowdupe, :boolean => true) do desc "Whether to allow duplicate GIDs. This option does not work on FreeBSD (contract to the `pw` man page)." newvalues(:true, :false) defaultto false end newparam(:ia_load_module, :required_features => :manages_aix_lam) do desc "The name of the I&A module to use to manage this user" defaultto "compat" end newproperty(:attributes, :parent => Puppet::Property::KeyValue, :required_features => :manages_aix_lam) do desc "Specify group AIX attributes in an array of keyvalue pairs" def membership :attribute_membership end - + def delimiter " " end validate do |value| raise ArgumentError, "Attributes value pairs must be seperated by an =" unless value.include?("=") end end newparam(:attribute_membership) do desc "Whether specified attribute value pairs should be treated as the only attributes of the user or whether they should merely be treated as the minimum list." newvalues(:inclusive, :minimum) defaultto :minimum end + newparam(:system, :boolean => true) do + desc "Whether the group is a system group with lower GID." + + newvalues(:true, :false) + + defaultto false + end end end diff --git a/lib/puppet/type/interface.rb b/lib/puppet/type/interface.rb new file mode 100644 index 000000000..7560a0552 --- /dev/null +++ b/lib/puppet/type/interface.rb @@ -0,0 +1,107 @@ +# +# Manages an interface on a given router or switch +# + +require 'puppet/util/network_device/ipcalc' + +Puppet::Type.newtype(:interface) do + + @doc = "This represents a router or switch interface. It is possible to manage + interface mode (access or trunking, native vlan and encapsulation), + switchport characteristics (speed, duplex)." + + ensurable do + defaultvalues + + aliasvalue :shutdown, :absent + aliasvalue :no_shutdown, :present + + defaultto { :no_shutdown } + end + + newparam(:name) do + desc "Interface name" + end + + newparam(:device_url) do + desc "Url to connect to a router or switch." + end + + newproperty(:description) do + desc "Interface description." + + defaultto { @resource[:name] } + end + + newproperty(:speed) do + desc "Interface speed." + newvalues(:auto, /^\d+/) + end + + newproperty(:duplex) do + desc "Interface duplex." + newvalues(:auto, :full, :half) + end + + newproperty(:native_vlan) do + desc "Interface native vlan (for access mode only)." + newvalues(/^\d+/) + end + + newproperty(:encapsulation) do + desc "Interface switchport encapsulation." + newvalues(:none, :dot1q, :isl ) + end + + newproperty(:mode) do + desc "Interface switchport mode." + newvalues(:access, :trunk) + end + + newproperty(:allowed_trunk_vlans) do + desc "Allowed list of Vlans that this trunk can forward." + newvalues(:all, /./) + end + + newproperty(:etherchannel) do + desc "Channel group this interface is part of." + newvalues(/^\d+/) + end + + newproperty(:ipaddress, :array_matching => :all) do + include Puppet::Util::NetworkDevice::IPCalc + + desc "IP Address of this interface (it might not be possible to set an interface IP address + it depends on the interface type and device type). + Valid format of ip addresses are: + * IPV4, like 127.0.0.1 + * IPV4/prefixlength like 127.0.1.1/24 + * IPV6/prefixlength like FE80::21A:2FFF:FE30:ECF0/128 + * an optional suffix for IPV6 addresses from this list: eui-64, link-local + It is also possible to use an array of values. + " + + validate do |values| + values = [values] unless values.is_a?(Array) + values.each do |value| + self.fail "Invalid interface ip address" unless parse(value.gsub(/\s*(eui-64|link-local)\s*$/,'')) + end + end + + munge do |value| + option = value =~ /eui-64|link-local/i ? value.gsub(/^.*?\s*(eui-64|link-local)\s*$/,'\1') : nil + [parse(value.gsub(/\s*(eui-64|link-local)\s*$/,'')), option].flatten + end + + def value_to_s(value) + value = [value] unless value.is_a?(Array) + value.map{ |v| "#{v[1].to_s}/#{v[0]} #{v[2]}"}.join(",") + end + + def change_to_s(currentvalue, newvalue) + currentvalue = value_to_s(currentvalue) if currentvalue != :absent + newvalue = value_to_s(newvalue) + super(currentvalue, newvalue) + end + end +end diff --git a/lib/puppet/type/mcx.rb b/lib/puppet/type/mcx.rb index 07c9348dd..d0306ca46 100644 --- a/lib/puppet/type/mcx.rb +++ b/lib/puppet/type/mcx.rb @@ -1,118 +1,99 @@ -#-- -# Copyright (C) 2008 Jeffrey J McCune. - -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Author: Jeff McCune - Puppet::Type.newtype(:mcx) do @doc = "MCX object management using DirectoryService on OS X. The default provider of this type merely manages the XML plist as reported by the dscl -mcxexport command. This is similar to the content property of the file type in Puppet. The recommended method of using this type is to use Work Group Manager to manage users and groups on the local computer, record the resulting puppet manifest using the command `puppet resource mcx`, then deploy it to other machines. **Autorequires:** If Puppet is managing the user, group, or computer that these MCX settings refer to, the MCX resource will autorequire that user, group, or computer. " feature :manages_content, \ "The provider can manage MCXSettings as a string.", :methods => [:content, :content=] ensurable do desc "Create or remove the MCX setting." newvalue(:present) do provider.create end newvalue(:absent) do provider.destroy end end newparam(:name) do desc "The name of the resource being managed. The default naming convention follows Directory Service paths: /Computers/localhost /Groups/admin /Users/localadmin The `ds_type` and `ds_name` type parameters are not necessary if the default naming convention is followed." isnamevar end newparam(:ds_type) do desc "The DirectoryService type this MCX setting attaches to." newvalues(:user, :group, :computer, :computerlist) end newparam(:ds_name) do desc "The name to attach the MCX Setting to. e.g. 'localhost' when ds_type => computer. This setting is not required, as it may be parsed so long as the resource name is parseable. e.g. /Groups/admin where 'group' is the dstype." end newproperty(:content, :required_features => :manages_content) do desc "The XML Plist. The value of MCXSettings in DirectoryService. This is the standard output from the system command: - + dscl localhost -mcxexport /Local/Default//ds_name - + Note that `ds_type` is capitalized and plural in the dscl command." end # JJM Yes, this is not DRY at all. Because of the code blocks # autorequire must be done this way. I think. def setup_autorequire(type) # value returns a Symbol name = value(:name) ds_type = value(:ds_type) ds_name = value(:ds_name) if ds_type == type rval = [ ds_name.to_s ] else rval = [ ] end rval end autorequire(:user) do setup_autorequire(:user) end autorequire(:group) do setup_autorequire(:group) end autorequire(:computer) do setup_autorequire(:computer) end end diff --git a/lib/puppet/type/router.rb b/lib/puppet/type/router.rb new file mode 100644 index 000000000..648389d39 --- /dev/null +++ b/lib/puppet/type/router.rb @@ -0,0 +1,14 @@ +# +# Manage a router abstraction +# + +module Puppet + newtype(:router) do + @doc = "Manages connected router." + + newparam(:url) do + desc "An URL to access the router of the form (ssh|telnet)://user:pass:enable@host/." + isnamevar + end + end +end diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb index 3ef044932..5a2c69b87 100644 --- a/lib/puppet/type/service.rb +++ b/lib/puppet/type/service.rb @@ -1,193 +1,201 @@ # This is our main way of managing processes right now. # # a service is distinct from a process in that services # can only be managed through the interface of an init script # which is why they have a search path for initscripts and such module Puppet newtype(:service) do @doc = "Manage running services. Service support unfortunately varies - widely by platform -- some platforms have very little if any + widely by platform --- some platforms have very little if any concept of a running service, and some have a very codified and powerful concept. Puppet's service support will generally be able - to make up for any inherent shortcomings (e.g., if there is no + to do the right thing regardless (e.g., if there is no 'status' command, then Puppet will look in the process table for a command matching the service name), but the more information you - can provide the better behaviour you will get. Or, you can just - use a platform that has very good service support. + can provide, the better behaviour you will get. In particular, any + virtual services that don't have a predictable entry in the process table + (for example, `network` on Red Hat/CentOS systems) will manifest odd + behavior on restarts if you don't specify `hasstatus` or a `status` + command. Note that if a `service` receives an event from another resource, the service will get restarted. The actual command to restart the - service depends on the platform. You can provide a special command - for restarting with the `restart` attribute." + service depends on the platform. You can provide an explicit command + for restarting with the `restart` attribute, or use the init script's + restart command with the `hasrestart` attribute; if you do neither, + the service's stop and start commands will be used." feature :refreshable, "The provider can restart the service.", :methods => [:restart] feature :enableable, "The provider can enable and disable the service", :methods => [:disable, :enable, :enabled?] feature :controllable, "The provider uses a control variable." newproperty(:enable, :required_features => :enableable) do desc "Whether a service should be enabled to start at boot. This property behaves quite differently depending on the platform; wherever possible, it relies on local tools to enable or disable a given service." newvalue(:true, :event => :service_enabled) do provider.enable end newvalue(:false, :event => :service_disabled) do provider.disable end def retrieve provider.enabled? end end # Handle whether the service should actually be running right now. newproperty(:ensure) do desc "Whether a service should be running." newvalue(:stopped, :event => :service_stopped) do provider.stop end newvalue(:running, :event => :service_started) do provider.start end aliasvalue(:false, :stopped) aliasvalue(:true, :running) def retrieve provider.status end def sync event = super() if property = @resource.property(:enable) val = property.retrieve property.sync unless property.safe_insync?(val) end event end end newparam(:binary) do desc "The path to the daemon. This is only used for systems that do not support init scripts. This binary will be used to start the service if no `start` parameter is provided." end newparam(:hasstatus) do desc "Declare the the service's init script has a functional status command. Based on testing, it was found that a large number of init scripts on different platforms do not support any kind of status command; thus, you must specify manually whether the service you are running has such a - command (or you can specify a specific command using the - `status` parameter). - - If you do not specify anything, then the service name will be - looked for in the process table." + command. Alternately, you can provide a specific command using the + `status` attribute. + + If you specify neither of these, then Puppet will look for the + service name in the process table. Be aware that 'virtual' init + scripts such as networking will respond poorly to refresh events + (via notify and subscribe relationships) if you don't override + this default behavior." newvalues(:true, :false) defaultto :true end newparam(:name) do desc "The name of the service to run. This name is used to find the service in whatever service subsystem it is in." isnamevar end newparam(:path) do desc "The search path for finding init scripts. Multiple values should be separated by colons or provided as an array." munge do |value| value = [value] unless value.is_a?(Array) # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] # It affects stand-alone blocks, too. paths = value.flatten.collect { |p| x = p.split(":") }.flatten end defaultto { provider.class.defpath if provider.class.respond_to?(:defpath) } end newparam(:pattern) do desc "The pattern to search for in the process table. This is used for stopping services on platforms that do not support init scripts, and is also used for determining service status on those service whose init scripts do not include a status command. If this is left unspecified and is needed to check the status of a service, then the service name will be used instead. The pattern can be a simple string or any legal Ruby pattern." defaultto { @resource[:binary] || @resource[:name] } end newparam(:restart) do desc "Specify a *restart* command manually. If left unspecified, the service will be stopped and then started." end newparam(:start) do desc "Specify a *start* command manually. Most service subsystems support a `start` command, so this will not need to be specified." end newparam(:status) do desc "Specify a *status* command manually. This command must return 0 if the service is running and a nonzero value otherwise. Ideally, these return codes should conform to [the LSB's specification for init script status actions](http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html), but puppet only considers the difference between 0 and nonzero to be relevant. If left unspecified, the status method will be determined automatically, usually by looking for the service in the process table." end newparam(:stop) do desc "Specify a *stop* command manually." end newparam(:control) do desc "The control variable used to manage services (originally for HP-UX). Defaults to the upcased service name plus `START` replacing dots with underscores, for those providers that support the `controllable` feature." defaultto { resource.name.gsub(".","_").upcase + "_START" if resource.provider.controllable? } end newparam :hasrestart do desc "Specify that an init script has a `restart` option. Otherwise, the init script's `stop` and `start` methods are used." newvalues(:true, :false) end newparam(:manifest) do desc "Specify a command to config a service, or a path to a manifest to do so." end # Basically just a synonym for restarting. Used to respond # to events. def refresh # Only restart if we're actually running if (@parameters[:ensure] || newattr(:ensure)).retrieve == :running provider.restart else debug "Skipping restart; service is not running" end end end end diff --git a/lib/puppet/type/vlan.rb b/lib/puppet/type/vlan.rb new file mode 100644 index 000000000..6708ea4f5 --- /dev/null +++ b/lib/puppet/type/vlan.rb @@ -0,0 +1,24 @@ +# +# Manages a Vlan on a given router or switch +# + +Puppet::Type.newtype(:vlan) do + @doc = "This represents a router or switch vlan." + + ensurable + + newparam(:name) do + desc "Vlan id. It must be a number" + isnamevar + + newvalues(/^\d+/) + end + + newproperty(:description) do + desc "Vlan name" + end + + newparam(:device_url) do + desc "Url to connect to a router or switch." + end +end \ No newline at end of file diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index 52b5f81ef..a884b8658 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -1,102 +1,112 @@ require "puppet/util/plugins" module Puppet module Util class CommandLine LegacyName = Hash.new{|h,k| k}.update( 'agent' => 'puppetd', 'cert' => 'puppetca', 'doc' => 'puppetdoc', 'filebucket' => 'filebucket', 'apply' => 'puppet', 'describe' => 'pi', 'queue' => 'puppetqd', 'resource' => 'ralsh', 'kick' => 'puppetrun', 'master' => 'puppetmasterd' ) - def initialize( zero = $0, argv = ARGV, stdin = STDIN ) + def initialize(zero = $0, argv = ARGV, stdin = STDIN) @zero = zero @argv = argv.dup @stdin = stdin - @subcommand_name, @args = subcommand_and_args( @zero, @argv, @stdin ) + @subcommand_name, @args = subcommand_and_args(@zero, @argv, @stdin) Puppet::Plugins.on_commandline_initialization(:command_line_object => self) end attr :subcommand_name attr :args def appdir File.join('puppet', 'application') end - def available_subcommands - absolute_appdirs = $LOAD_PATH.collect do |x| + def self.available_subcommands + absolute_appdirs = $LOAD_PATH.collect do |x| File.join(x,'puppet','application') end.select{ |x| File.directory?(x) } absolute_appdirs.inject([]) do |commands, dir| commands + Dir[File.join(dir, '*.rb')].map{|fn| File.basename(fn, '.rb')} end.uniq end - - def usage_message - usage = "Usage: puppet command " - available = "Available commands are: #{available_subcommands.sort.join(', ')}" - [usage, available].join("\n") + # available_subcommands was previously an instance method, not a class + # method, and we have an unknown number of user-implemented applications + # that depend on that behaviour. Forwarding allows us to preserve a + # backward compatible API. --daniel 2011-04-11 + def available_subcommands + self.class.available_subcommands end def require_application(application) require File.join(appdir, application) end def execute - if subcommand_name.nil? - puts usage_message - elsif available_subcommands.include?(subcommand_name) #subcommand + if subcommand_name and available_subcommands.include?(subcommand_name) then require_application subcommand_name app = Puppet::Application.find(subcommand_name).new(self) Puppet::Plugins.on_application_initialization(:appliation_object => self) app.run + elsif execute_external_subcommand then + # Logically, we shouldn't get here, but we do, so whatever. We just + # return to the caller. How strange we are. --daniel 2011-04-11 else - abort "Error: Unknown command #{subcommand_name}.\n#{usage_message}" unless execute_external_subcommand + unless subcommand_name.nil? then + puts "Error: Unknown Puppet subcommand #{subcommand_name}.\n" + end + + # Doing this at the top of the file is natural, but causes puppet.rb + # to load too early, which causes things to break. This is a nasty + # thing, found in #7065. --daniel 2011-04-11 + require 'puppet/face' + puts Puppet::Face[:help, :current].help end end def execute_external_subcommand external_command = "puppet-#{subcommand_name}" require 'puppet/util' - path_to_subcommand = Puppet::Util.which( external_command ) + path_to_subcommand = Puppet::Util.which(external_command) return false unless path_to_subcommand - system( path_to_subcommand, *args ) + system(path_to_subcommand, *args) true end def legacy_executable_name LegacyName[ subcommand_name ] end private - def subcommand_and_args( zero, argv, stdin ) + def subcommand_and_args(zero, argv, stdin) zero = File.basename(zero, '.rb') if zero == 'puppet' case argv.first when nil; [ stdin.tty? ? nil : "apply", argv] # ttys get usage info when "--help", "-h"; [nil, argv] # help should give you usage, not the help for `puppet apply` when /^-|\.pp$|\.rb$/; ["apply", argv] else [ argv.first, argv[1..-1] ] end else [ zero, argv ] end end end end end diff --git a/lib/puppet/util/logging.rb b/lib/puppet/util/logging.rb index bc52b17f0..4e76ae414 100644 --- a/lib/puppet/util/logging.rb +++ b/lib/puppet/util/logging.rb @@ -1,40 +1,51 @@ # A module to make logging a bit easier. require 'puppet/util/log' module Puppet::Util::Logging def send_log(level, message) Puppet::Util::Log.create({:level => level, :source => log_source, :message => message}.merge(log_metadata)) end # Create a method for each log level. Puppet::Util::Log.eachlevel do |level| define_method(level) do |args| args = args.join(" ") if args.is_a?(Array) send_log(level, args) end end + def deprecation_warning(message) + $deprecation_warnings ||= Hash.new(0) + if $deprecation_warnings.length < 100 and ($deprecation_warnings[message] += 1) == 1 + warning message + end + end + + def clear_deprecation_warnings + $deprecation_warnings.clear if $deprecation_warnings + end + private def is_resource? defined?(Puppet::Type) && is_a?(Puppet::Type) end def is_resource_parameter? defined?(Puppet::Parameter) && is_a?(Puppet::Parameter) end def log_metadata [:file, :line, :tags].inject({}) do |result, attr| result[attr] = send(attr) if respond_to?(attr) result end end def log_source # We need to guard the existence of the constants, since this module is used by the base Puppet module. (is_resource? or is_resource_parameter?) and respond_to?(:path) and return path.to_s to_s end end diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb index a93c66b07..10a268409 100644 --- a/lib/puppet/util/monkey_patches.rb +++ b/lib/puppet/util/monkey_patches.rb @@ -1,92 +1,106 @@ unless defined? JRUBY_VERSION Process.maxgroups = 1024 end module RDoc def self.caller(skip=nil) in_gem_wrapper = false Kernel.caller.reject { |call| in_gem_wrapper ||= call =~ /#{Regexp.escape $0}:\d+:in `load'/ } end end require "yaml" require "puppet/util/zaml.rb" class Symbol def to_zaml(z) z.emit("!ruby/sym ") to_s.to_zaml(z) end def <=> (other) self.to_s <=> other.to_s end end [Object, Exception, Integer, Struct, Date, Time, Range, Regexp, Hash, Array, Float, String, FalseClass, TrueClass, Symbol, NilClass, Class].each { |cls| cls.class_eval do def to_yaml(ignored=nil) ZAML.dump(self) end end } def YAML.dump(*args) ZAML.dump(*args) end # # Workaround for bug in MRI 1.8.7, see # http://redmine.ruby-lang.org/issues/show/2708 # for details # if RUBY_VERSION == '1.8.7' class NilClass def closed? true end end end class Object # ActiveSupport 2.3.x mixes in a dangerous method # that can cause rspec to fork bomb # and other strange things like that. def daemonize raise NotImplementedError, "Kernel.daemonize is too dangerous, please don't try to use it." end # The following code allows callers to make assertions that are only # checked when the environment variable PUPPET_ENABLE_ASSERTIONS is # set to a non-empty string. For example: # # assert_that { condition } # assert_that(message) { condition } if ENV["PUPPET_ENABLE_ASSERTIONS"].to_s != '' def assert_that(message = nil) unless yield raise Exception.new("Assertion failure: #{message}") end end else def assert_that(message = nil) end end end # Workaround for yaml_initialize, which isn't supported before Ruby # 1.8.3. if RUBY_VERSION == '1.8.1' || RUBY_VERSION == '1.8.2' YAML.add_ruby_type( /^object/ ) { |tag, val| type, obj_class = YAML.read_type_class( tag, Object ) r = YAML.object_maker( obj_class, val ) if r.respond_to? :yaml_initialize r.instance_eval { instance_variables.each { |name| remove_instance_variable name } } r.yaml_initialize(tag, val) end r } end + +class Array + # Ruby < 1.8.7 doesn't have this method but we use it in tests + def combination(num) + return [] if num < 0 || num > size + return [[]] if num == 0 + return map{|e| [e] } if num == 1 + tmp = self.dup + self[0, size - (num - 1)].inject([]) do |ret, e| + tmp.shift + ret += tmp.combination(num - 1).map{|a| a.unshift(e) } + end + end unless method_defined? :combination +end diff --git a/lib/puppet/util/network_device.rb b/lib/puppet/util/network_device.rb new file mode 100644 index 000000000..bca66016b --- /dev/null +++ b/lib/puppet/util/network_device.rb @@ -0,0 +1,2 @@ +module Puppet::Util::NetworkDevice +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/base.rb b/lib/puppet/util/network_device/base.rb new file mode 100644 index 000000000..ff96c8693 --- /dev/null +++ b/lib/puppet/util/network_device/base.rb @@ -0,0 +1,29 @@ +require 'puppet/util/autoload' +require 'uri' +require 'puppet/util/network_device/transport' +require 'puppet/util/network_device/transport/base' + +module Puppet::Util::NetworkDevice + class Base + + attr_accessor :url, :transport + + def initialize(url) + @url = URI.parse(url) + + @autoloader = Puppet::Util::Autoload.new( + self, + "puppet/util/network_device/transport", + :wrap => false + ) + + if @autoloader.load(@url.scheme) + @transport = Puppet::Util::NetworkDevice::Transport.const_get(@url.scheme.capitalize).new + @transport.host = @url.host + @transport.port = @url.port || case @url.scheme ; when "ssh" ; 22 ; when "telnet" ; 23 ; end + @transport.user = @url.user + @transport.password = @url.password + end + end + end +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/cisco.rb b/lib/puppet/util/network_device/cisco.rb new file mode 100644 index 000000000..c03a00104 --- /dev/null +++ b/lib/puppet/util/network_device/cisco.rb @@ -0,0 +1,4 @@ + +module Puppet::Util::NetworkDevice::Cisco + +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/cisco/device.rb b/lib/puppet/util/network_device/cisco/device.rb new file mode 100644 index 000000000..1f350991a --- /dev/null +++ b/lib/puppet/util/network_device/cisco/device.rb @@ -0,0 +1,246 @@ +require 'puppet' +require 'puppet/util' +require 'puppet/util/network_device/base' +require 'puppet/util/network_device/ipcalc' +require 'puppet/util/network_device/cisco/interface' +require 'ipaddr' + +class Puppet::Util::NetworkDevice::Cisco::Device < Puppet::Util::NetworkDevice::Base + + include Puppet::Util::NetworkDevice::IPCalc + + attr_accessor :enable_password + + def initialize(url, options = {}) + super(url) + @enable_password = options[:enable_password] || parse_enable(@url.query) + transport.default_prompt = /[#>]\s?\z/n + end + + def parse_enable(query) + return $1 if query =~ /enable=(.*)/ + end + + def command(cmd=nil) + Puppet.debug("command #{cmd}") + transport.connect + login + transport.command("terminal length 0") do |out| + enable if out =~ />\s?\z/n + end + find_capabilities + out = execute(cmd) if cmd + yield self if block_given? + transport.close + out + end + + def execute(cmd) + transport.command(cmd) + end + + def login + return if transport.handles_login? + if @url.user != '' + transport.command(@url.user, :prompt => /^Password:/) + else + transport.expect(/^Password:/) + end + transport.command(@url.password) + end + + def enable + raise "Can't issue \"enable\" to enter privileged, no enable password set" unless enable_password + transport.command("enable", :prompt => /^Password:/) + transport.command(enable_password) + end + + def support_vlan_brief? + !! @support_vlan_brief + end + + def find_capabilities + out = transport.command("sh vlan brief") + lines = out.split("\n") + lines.shift; lines.pop + + @support_vlan_brief = ! (lines.first =~ /^%/) + end + + IF={ + :FastEthernet => %w{FastEthernet FastEth Fast FE Fa F}, + :GigEthernet => %w{GigabitEthernet GigEthernet GigEth GE Gi G}, + :Ethernet => %w{Ethernet Eth E}, + :Serial => %w{Serial Se S}, + :PortChannel => %w{PortChannel Port-Channel Po}, + :POS => %w{POS P}, + :VLAN => %w{VLAN VL V}, + :Loopback => %w{Loopback Loop Lo}, + :ATM => %w{ATM AT A}, + :Dialer => %w{Dialer Dial Di D}, + :VirtualAccess => %w{Virtual-Access Virtual-A Virtual Virt} + } + + def canonalize_ifname(interface) + IF.each do |k,ifnames| + if found = ifnames.find { |ifname| interface =~ /^#{ifname}\s*\d/i } + interface =~ /^#{found}(.+)\b/i + return "#{k.to_s}#{$1}".gsub(/\s+/,'') + end + end + interface + end + + def interface(name) + ifname = canonalize_ifname(name) + interface = parse_interface(ifname) + return { :ensure => :absent } if interface.empty? + interface.merge!(parse_trunking(ifname)) + interface.merge!(parse_interface_config(ifname)) + end + + def new_interface(name) + Puppet::Util::NetworkDevice::Cisco::Interface.new(canonalize_ifname(name), transport) + end + + def parse_interface(name) + resource = {} + out = transport.command("sh interface #{name}") + lines = out.split("\n") + lines.shift; lines.pop + lines.each do |l| + if l =~ /#{name} is (.+), line protocol is / + resource[:ensure] = ($1 == 'up' ? :present : :absent); + end + if l =~ /Auto Speed \(.+\),/ or l =~ /Auto Speed ,/ or l =~ /Auto-speed/ + resource[:speed] = :auto + end + if l =~ /, (.+)Mb\/s/ + resource[:speed] = $1 + end + if l =~ /\s+Auto-duplex \((.{4})\),/ + resource[:duplex] = :auto + end + if l =~ /\s+(.+)-duplex/ + resource[:duplex] = $1 == "Auto" ? :auto : $1.downcase.to_sym + end + if l =~ /Description: (.+)/ + resource[:description] = $1 + end + end + resource + end + + def parse_interface_config(name) + resource = Hash.new { |hash, key| hash[key] = Array.new ; } + out = transport.command("sh running-config interface #{name} | begin interface") + lines = out.split("\n") + lines.shift; lines.pop + lines.each do |l| + if l =~ /ip address (#{IP}) (#{IP})\s+secondary\s*$/ + resource[:ipaddress] << [prefix_length(IPAddr.new($2)), IPAddr.new($1), 'secondary'] + end + if l =~ /ip address (#{IP}) (#{IP})\s*$/ + resource[:ipaddress] << [prefix_length(IPAddr.new($2)), IPAddr.new($1), nil] + end + if l =~ /ipv6 address (#{IP})\/(\d+) (eui-64|link-local)/ + resource[:ipaddress] << [$2.to_i, IPAddr.new($1), $3] + end + if l =~ /channel-group\s+(\d+)/ + resource[:etherchannel] = $1 + end + end + resource + end + + def parse_vlans + vlans = {} + out = transport.command(support_vlan_brief? ? "sh vlan brief" : "sh vlan-switch brief") + lines = out.split("\n") + lines.shift; lines.shift; lines.shift; lines.pop + vlan = nil + lines.each do |l| + case l + # vlan name status + when /^(\d+)\s+(\w+)\s+(\w+)\s+([a-zA-Z0-9,\/. ]+)\s*$/ + vlan = { :name => $1, :description => $2, :status => $3, :interfaces => [] } + if $4.strip.length > 0 + vlan[:interfaces] = $4.strip.split(/\s*,\s*/).map{ |ifn| canonalize_ifname(ifn) } + end + vlans[vlan[:name]] = vlan + when /^\s+([a-zA-Z0-9,\/. ]+)\s*$/ + raise "invalid sh vlan summary output" unless vlan + if $1.strip.length > 0 + vlan[:interfaces] += $1.strip.split(/\s*,\s*/).map{ |ifn| canonalize_ifname(ifn) } + end + else + end + end + vlans + end + + def update_vlan(id, is = {}, should = {}) + if should[:ensure] == :absent + Puppet.info "Removing #{id} from device vlan" + transport.command("conf t") + transport.command("no vlan #{id}") + transport.command("exit") + return + end + + # We're creating or updating an entry + transport.command("conf t") + transport.command("vlan #{id}") + [is.keys, should.keys].flatten.uniq.each do |property| + Puppet.debug("trying property: #{property}: #{should[property]}") + next if property != :description + transport.command("name #{should[property]}") + end + transport.command("exit") + transport.command("exit") + end + + def parse_trunking(interface) + trunking = {} + out = transport.command("sh interface #{interface} switchport") + lines = out.split("\n") + lines.shift; lines.pop + lines.each do |l| + case l + when /^Administrative mode:\s+(.*)$/i + case $1 + when "trunk" + trunking[:mode] = :trunk + when "static access" + trunking[:mode] = :access + else + raise "Unknown switchport mode: #{$1} for #{interface}" + end + when /^Administrative Trunking Encapsulation:\s+(.*)$/ + case $1 + when "dot1q","isl" + trunking[:encapsulation] = $1.to_sym if trunking[:mode] == :trunk + else + raise "Unknown switchport encapsulation: #{$1} for #{interface}" + end + when /^Access Mode VLAN:\s+(.*) \(\(Inactive\)\)$/ + # nothing + when /^Access Mode VLAN:\s+(.*) \(.*\)$/ + trunking[:native_vlan] = $1 if trunking[:mode] == :access + when /^Trunking VLANs Enabled:\s+(.*)$/ + next if trunking[:mode] == :access + vlans = $1 + trunking[:allowed_trunk_vlans] = case vlans + when /all/i + :all + when /none/i + :none + else + vlans + end + end + end + trunking + end + +end diff --git a/lib/puppet/util/network_device/cisco/interface.rb b/lib/puppet/util/network_device/cisco/interface.rb new file mode 100644 index 000000000..63d5492a7 --- /dev/null +++ b/lib/puppet/util/network_device/cisco/interface.rb @@ -0,0 +1,82 @@ +require 'puppet/util/network_device/cisco' +require 'puppet/util/network_device/ipcalc' + +# this manages setting properties to an interface in a cisco switch or router +class Puppet::Util::NetworkDevice::Cisco::Interface + + include Puppet::Util::NetworkDevice::IPCalc + extend Puppet::Util::NetworkDevice::IPCalc + + attr_reader :transport, :name + + def initialize(name, transport) + @name = name + @transport = transport + end + + COMMANDS = { + # property => order, ios command/block/array + :description => [1, "description %s"], + :speed => [2, "speed %s"], + :duplex => [3, "duplex %s"], + :native_vlan => [4, "switchport access vlan %s"], + :encapsulation => [5, "switchport trunk encapsulation %s"], + :mode => [6, "switchport mode %s"], + :allowed_trunk_vlans => [7, "switchport trunk allowed vlan %s"], + :etherchannel => [8, ["channel-group %s", "port group %s"]], + :ipaddress => [9, + lambda do |prefix,ip,option| + ip.ipv6? ? "ipv6 address #{ip.to_s}/#{prefix} #{option}" : + "ip address #{ip.to_s} #{netmask(Socket::AF_INET,prefix)}" + end], + :ensure => [10, lambda { |value| value == :present ? "no shutdown" : "shutdown" } ] + } + + def update(is={}, should={}) + Puppet.debug("Updating interface #{name}") + command("conf t") + command("interface #{name}") + + # apply changes in a defined orders for cisco IOS devices + [is.keys, should.keys].flatten.uniq.sort {|a,b| COMMANDS[a][0] <=> COMMANDS[b][0] }.each do |property| + # They're equal, so do nothing. + next if is[property] == should[property] + + # We're deleting it + if should[property] == :absent or should[property].nil? + execute(property, is[property], "no ") + next + end + + # We're replacing an existing value or creating a new one + execute(property, should[property]) + end + + command("exit") + command("exit") + end + + def execute(property, value, prefix='') + case COMMANDS[property][1] + when Array + COMMANDS[property][1].each do |command| + transport.command(prefix + command % value) do |out| + break unless out =~ /^%/ + end + end + when String + command(prefix + COMMANDS[property][1] % value) + when Proc + value = [value] unless value.is_a?(Array) + value.each do |value| + command(prefix + COMMANDS[property][1].call(*value)) + end + end + end + + def command(command) + transport.command(command) do |out| + Puppet.err "Error while executing #{command}, device returned #{out}" if out =~ /^%/mo + end + end +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/ipcalc.rb b/lib/puppet/util/network_device/ipcalc.rb new file mode 100644 index 000000000..2b4f360b7 --- /dev/null +++ b/lib/puppet/util/network_device/ipcalc.rb @@ -0,0 +1,68 @@ + +require 'puppet/util/network_device' +module Puppet::Util::NetworkDevice::IPCalc + + # This is a rip-off of authstore + Octet = '(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])' + IPv4 = "#{Octet}\.#{Octet}\.#{Octet}\.#{Octet}" + IPv6_full = "_:_:_:_:_:_:_:_|_:_:_:_:_:_::_?|_:_:_:_:_::((_:)?_)?|_:_:_:_::((_:){0,2}_)?|_:_:_::((_:){0,3}_)?|_:_::((_:){0,4}_)?|_::((_:){0,5}_)?|::((_:){0,6}_)?" + IPv6_partial = "_:_:_:_:_:_:|_:_:_:_::(_:)?|_:_::(_:){0,2}|_::(_:){0,3}" + IP = "#{IPv4}|#{IPv6_full}".gsub(/_/,'([0-9a-fA-F]{1,4})').gsub(/\(/,'(?:') + + def parse(value) + case value + when /^(#{IP})\/(\d+)$/ # 12.34.56.78/24, a001:b002::efff/120, c444:1000:2000::9:192.168.0.1/112 + [$2.to_i,IPAddr.new($1)] + when /^(#{IP})$/ # 10.20.30.40, + value = IPAddr.new(value) + [bits(value.family),value] + end + end + + def bits(family) + family == Socket::AF_INET6 ? 128 : 32 + end + + def fullmask(family) + (1 << bits(family)) - 1 + end + + def mask(family, length) + (1 << (bits(family) - length)) - 1 + end + + # returns ip address netmask from prefix length + def netmask(family, length) + IPAddr.new(fullmask(family) & ~mask(family, length) , family) + end + + # returns an IOS wildmask + def wildmask(family, length) + IPAddr.new(mask(family, length) , family) + end + + # returns ip address prefix length from netmask + def prefix_length(netmask) + mask_addr = netmask.to_i + return 0 if mask_addr == 0 + length=32 + if (netmask.ipv6?) + length=128 + end + + mask = mask_addr < 2**length ? length : 128 + + mask.times do + if ((mask_addr & 1) == 1) + break + end + mask_addr = mask_addr >> 1 + mask = mask - 1 + end + mask + end + + def linklocal?(ip) + end + +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/transport.rb b/lib/puppet/util/network_device/transport.rb new file mode 100644 index 000000000..e64fe9b69 --- /dev/null +++ b/lib/puppet/util/network_device/transport.rb @@ -0,0 +1,5 @@ +# stub +module Puppet::Util::NetworkDevice + module Transport + end +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/transport/base.rb b/lib/puppet/util/network_device/transport/base.rb new file mode 100644 index 000000000..1d62209cb --- /dev/null +++ b/lib/puppet/util/network_device/transport/base.rb @@ -0,0 +1,26 @@ + +require 'puppet/util/network_device' +require 'puppet/util/network_device/transport' + +class Puppet::Util::NetworkDevice::Transport::Base + attr_accessor :user, :password, :host, :port + attr_accessor :default_prompt, :timeout + + def initialize + @timeout = 10 + end + + def send(cmd) + end + + def expect(prompt) + end + + def command(cmd, options = {}) + send(cmd) + expect(options[:prompt] || default_prompt) do |output| + yield output if block_given? + end + end + +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/transport/ssh.rb b/lib/puppet/util/network_device/transport/ssh.rb new file mode 100644 index 000000000..b3cf51b8a --- /dev/null +++ b/lib/puppet/util/network_device/transport/ssh.rb @@ -0,0 +1,115 @@ + +require 'puppet/util/network_device' +require 'puppet/util/network_device/transport' +require 'puppet/util/network_device/transport/base' +require 'net/ssh' + +# This is an adaptation/simplification of gem net-ssh-telnet, which aims to have +# a sane interface to Net::SSH. Credits goes to net-ssh-telnet authors +class Puppet::Util::NetworkDevice::Transport::Ssh < Puppet::Util::NetworkDevice::Transport::Base + + attr_accessor :buf, :ssh, :channel, :verbose + + def initialize + super + end + + def handles_login? + true + end + + def eof? + !! @eof + end + + def connect(&block) + @output = [] + @channel_data = "" + + begin + Puppet.debug("connecting to #{host} as #{user}") + @ssh = Net::SSH.start(host, user, :port => port, :password => password, :timeout => timeout) + rescue TimeoutError + raise TimeoutError, "timed out while opening an ssh connection to the host" + end + + @buf = "" + @eof = false + @channel = nil + @ssh.open_channel do |channel| + channel.request_pty { |ch,success| raise "failed to open pty" unless success } + + channel.send_channel_request("shell") do |ch, success| + raise "failed to open ssh shell channel" unless success + + ch.on_data { |ch,data| @buf << data } + ch.on_extended_data { |ch,type,data| @buf << data if type == 1 } + ch.on_close { @eof = true } + + @channel = ch + expect(default_prompt, &block) + # this is a little bit unorthodox, we're trying to escape + # the ssh loop there while still having the ssh connection up + # otherwise we wouldn't be able to return ssh stdout/stderr + # for a given call of command. + return + end + + end + @ssh.loop + + end + + def close + @channel.close if @channel + @channel = nil + @ssh.close if @ssh + end + + def expect(prompt) + line = '' + sock = @ssh.transport.socket + + while not @eof + break if line =~ prompt and @buf == '' + break if sock.closed? + + IO::select([sock], [sock], nil, nil) + + process_ssh + + # at this point we have accumulated some data in @buf + # or the channel has been closed + if @buf != "" + line += @buf.gsub(/\r\n/no, "\n") + @buf = '' + yield line if block_given? + elsif @eof + # channel has been closed + break if line =~ prompt + if line == '' + line = nil + yield nil if block_given? + end + break + end + end + Puppet.debug("ssh: expected #{line}") if @verbose + line + end + + def send(line) + Puppet.debug("ssh: send #{line}") if @verbose + @channel.send_data(line + "\n") + end + + def process_ssh + while @buf == "" and not eof? + begin + @channel.connection.process(0.1) + rescue IOError + @eof = true + end + end + end +end \ No newline at end of file diff --git a/lib/puppet/util/network_device/transport/telnet.rb b/lib/puppet/util/network_device/transport/telnet.rb new file mode 100644 index 000000000..e55079e06 --- /dev/null +++ b/lib/puppet/util/network_device/transport/telnet.rb @@ -0,0 +1,42 @@ +require 'puppet/util/network_device' +require 'puppet/util/network_device/transport' +require 'puppet/util/network_device/transport/base' +require 'net/telnet' + +class Puppet::Util::NetworkDevice::Transport::Telnet < Puppet::Util::NetworkDevice::Transport::Base + def initialize + super + end + + def handles_login? + false + end + + def connect + @telnet = Net::Telnet::new("Host" => host, "Port" => port || 23, + "Timeout" => 10, + "Prompt" => default_prompt, "Output_log" => "/tmp/out.log") + end + + def close + @telnet.close if @telnet + @telnet = nil + end + + def expect(prompt) + @telnet.waitfor(prompt) do |out| + yield out if block_given? + end + end + + def command(cmd, options = {}) + send(cmd) + expect(options[:prompt] || default_prompt) do |output| + yield output if block_given? + end + end + + def send(line) + @telnet.puts(line) + end +end \ No newline at end of file diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb index 9d0e0a715..cec8a57d9 100644 --- a/lib/puppet/util/selinux.rb +++ b/lib/puppet/util/selinux.rb @@ -1,216 +1,216 @@ -# Provides utility functions to help interfaces Puppet to SELinux. +# Provides utility functions to help interface Puppet to SELinux. # # This requires the very new SELinux Ruby bindings. These bindings closely # mirror the SELinux C library interface. # # Support for the command line tools is not provided because the performance # was abysmal. At this time (2008-11-02) the only distribution providing # these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby). Puppet.features.selinux? # check, but continue even if it's not require 'pathname' module Puppet::Util::SELinux def selinux_support? return false unless defined?(Selinux) if Selinux.is_selinux_enabled == 1 return true end false end # Retrieve and return the full context of the file. If we don't have # SELinux support or if the SELinux call fails then return nil. def get_selinux_current_context(file) return nil unless selinux_support? retval = Selinux.lgetfilecon(file) if retval == -1 return nil end retval[1] end # Retrieve and return the default context of the file. If we don't have # SELinux support or if the SELinux call fails to file a default then return nil. def get_selinux_default_context(file) return nil unless selinux_support? # If the filesystem has no support for SELinux labels, return a default of nil # instead of what matchpathcon would return return nil unless selinux_label_support?(file) # If the file exists we should pass the mode to matchpathcon for the most specific # matching. If not, we can pass a mode of 0. begin filestat = File.lstat(file) mode = filestat.mode rescue Errno::ENOENT mode = 0 end retval = Selinux.matchpathcon(file, mode) if retval == -1 return nil end retval[1] end # Take the full SELinux context returned from the tools and parse it # out to the three (or four) component parts. Supports :seluser, :selrole, # :seltype, and on systems with range support, :selrange. def parse_selinux_context(component, context) if context.nil? or context == "unlabeled" return nil end unless context =~ /^([a-z0-9_]+):([a-z0-9_]+):([a-zA-Z0-9_]+)(?::([a-zA-Z0-9:,._-]+))?/ raise Puppet::Error, "Invalid context to parse: #{context}" end ret = { :seluser => $1, :selrole => $2, :seltype => $3, :selrange => $4, } ret[component] end # This updates the actual SELinux label on the file. You can update # only a single component or update the entire context. # The caveat is that since setting a partial context makes no sense the # file has to already exist. Puppet (via the File resource) will always # just try to set components, even if all values are specified by the manifest. # I believe that the OS should always provide at least a fall-through context # though on any well-running system. def set_selinux_context(file, value, component = false) return nil unless selinux_support? && selinux_label_support?(file) if component # Must first get existing context to replace a single component context = Selinux.lgetfilecon(file)[1] if context == -1 # We can't set partial context components when no context exists # unless/until we can find a way to make Puppet call this method # once for all selinux file label attributes. Puppet.warning "Can't set SELinux context on file unless the file already has some kind of context" return nil end context = context.split(':') case component when :seluser context[0] = value when :selrole context[1] = value when :seltype context[2] = value when :selrange context[3] = value else raise ArguementError, "set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange" end context = context.join(':') else context = value end retval = Selinux.lsetfilecon(file, context) if retval == 0 return true else Puppet.warning "Failed to set SELinux context #{context} on #{file}" return false end end # Since this call relies on get_selinux_default_context it also needs a # full non-relative path to the file. Fortunately, that seems to be all # Puppet uses. This will set the file's SELinux context to the policy's # default context (if any) if it differs from the context currently on # the file. def set_selinux_default_context(file) new_context = get_selinux_default_context(file) return nil unless new_context cur_context = get_selinux_current_context(file) if new_context != cur_context set_selinux_context(file, new_context) return new_context end nil end # Internal helper function to read and parse /proc/mounts def read_mounts mounts = "" begin if File.instance_methods.include? "read_nonblock" # If possible we use read_nonblock in a loop rather than read to work- # a linux kernel bug. See ticket #1963 for details. mountfh = File.open("/proc/mounts") mounts += mountfh.read_nonblock(1024) while true else # Otherwise we shell out and let cat do it for us mountfh = IO.popen("/bin/cat /proc/mounts") mounts = mountfh.read end rescue EOFError # that's expected rescue return nil ensure mountfh.close if mountfh end mntpoint = {} # Read all entries in /proc/mounts. The second column is the # mountpoint and the third column is the filesystem type. # We skip rootfs because it is always mounted at / mounts.collect do |line| params = line.split(' ') next if params[2] == 'rootfs' mntpoint[params[1]] = params[2] end mntpoint end def realpath(path) path, rest = Pathname.new(path), [] path, rest = path.dirname, [path.basename] + rest while ! path.exist? File.join( path.realpath, *rest ) end def parent_directory(path) Pathname.new(path).dirname.to_s end # Internal helper function to return which type of filesystem a # given file path resides on def find_fs(path) unless mnts = read_mounts return nil end # For a given file: # Check if the filename is in the data structure; # return the fstype if it is. # Just in case: return something if you're down to "/" or "" # Remove the last slash and everything after it, # and repeat with that as the file for the next loop through. path = realpath(path) while not path.empty? return mnts[path] if mnts.has_key?(path) path = parent_directory(path) end mnts['/'] end # Check filesystem a path resides on for SELinux support against # whitelist of known-good filesystems. # Returns true if the filesystem can support SELinux labels and # false if not. def selinux_label_support?(file) fstype = find_fs(file) return false if fstype.nil? filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs'] filesystems.include?(fstype) end end diff --git a/man/man5/puppet.conf.5 b/man/man5/puppet.conf.5 index f6c9926c3..930cec533 100644 --- a/man/man5/puppet.conf.5 +++ b/man/man5/puppet.conf.5 @@ -1,1592 +1,1608 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "PUPPETCONF" "5" "February 2011" "Puppet Labs, LLC" "Puppet manual" -\fBThis page is autogenerated; any changes will get overwritten\fR \fI(last generated on Thu Feb 17 09:56:34 \-0800 2011)\fR +.TH "PUPPETCONF" "5" "April 2011" "Puppet Labs, LLC" "Puppet manual" +\fBThis page is autogenerated; any changes will get overwritten\fR \fI(last generated on Wed Apr 13 14:24:43 \-0700 2011)\fR . .SH "Specifying Configuration Parameters" . .SS "On The Command\-Line" Every Puppet executable (with the exception of \fBpuppetdoc\fR) accepts all of the parameters below, but not all of the arguments make sense for every executable\. . .P I have tried to be as thorough as possible in the descriptions of the arguments, so it should be obvious whether an argument is appropriate or not\. . .P These parameters can be supplied to the executables either as command\-line options or in the configuration file\. For instance, the command\-line invocation below would set the configuration directory to \fB/private/puppet\fR: . .IP "" 4 . .nf $ puppet agent \-\-confdir=/private/puppet . .fi . .IP "" 0 . .P Note that boolean options are turned on and off with a slightly different syntax on the command line: . .IP "" 4 . .nf $ puppet agent \-\-storeconfigs $ puppet agent \-\-no\-storeconfigs . .fi . .IP "" 0 . .P The invocations above will enable and disable, respectively, the storage of the client configuration\. . .SS "Configuration Files" As mentioned above, the configuration parameters can also be stored in a configuration file, located in the configuration directory\. As root, the default configuration directory is \fB/etc/puppet\fR, and as a regular user, the default configuration directory is \fB~user/\.puppet\fR\. As of 0\.23\.0, all executables look for \fBpuppet\.conf\fR in their configuration directory (although they previously looked for separate files)\. For example, \fBpuppet\.conf\fR is located at \fB/etc/puppet/puppet\.conf\fR as \fBroot\fR and \fB~user/\.puppet/puppet\.conf\fR as a regular user by default\. . .P All executables will set any parameters set within the \fB[main]\fR section, and each executable will also use one of the \fB[master]\fR, \fB[agent]\fR\. . .P The file follows INI\-style formatting\. Here is an example of a very simple \fBpuppet\.conf\fR file: . .IP "" 4 . .nf [main] confdir = /private/puppet storeconfigs = true . .fi . .IP "" 0 . .P Note that boolean parameters must be explicitly specified as \fBtrue\fR or \fBfalse\fR as seen above\. . .P If you need to change file or directory parameters (e\.g\., reset the mode or owner), do so within curly braces on the same line: . .IP "" 4 . .nf [main] vardir = /new/vardir {owner = root, mode = 644} . .fi . .IP "" 0 . .P If you\'re starting out with a fresh configuration, you may wish to let the executable generate a template configuration file for you by invoking the executable in question with the \fB\-\-genconfig\fR command\. The executable will print a template configuration to standard output, which can be redirected to a file like so: . .IP "" 4 . .nf $ puppet agent \-\-genconfig > /etc/puppet/puppet\.conf . .fi . .IP "" 0 . .P Note that this invocation will replace the contents of any pre\-existing \fBpuppet\.conf\fR file, so make a backup of your present config if it contains valuable information\. . .P Like the \fB\-\-genconfig\fR argument, the executables also accept a \fB\-\-genmanifest\fR argument, which will generate a manifest that can be used to manage all of Puppet\'s directories and files and prints it to standard output\. This can likewise be redirected to a file: . .IP "" 4 . .nf $ puppet agent \-\-genmanifest > /etc/puppet/manifests/site\.pp . .fi . .IP "" 0 . .P Puppet can also create user and group accounts for itself (one \fBpuppet\fR group and one \fBpuppet\fR user) if it is invoked as \fBroot\fR with the \fB\-\-mkusers\fR argument: . .IP "" 4 . .nf -$ puppet agent \-\-mkusers +$ puppet master \-\-mkusers . .fi . .IP "" 0 . .SH "Signals" The \fBpuppet agent\fR and \fBpuppet master\fR executables catch some signals for special handling\. Both daemons catch (\fBSIGHUP\fR), which forces the server to restart tself\. Predictably, interrupt and terminate (\fBSIGINT\fR and \fBSIGTERM\fR) will shut down the server, whether it be an instance of \fBpuppet agent\fR or \fBpuppet master\fR\. . .P Sending the \fBSIGUSR1\fR signal to an instance of \fBpuppet agent\fR will cause it to immediately begin a new configuration transaction with the server\. This signal has no effect on \fBpuppet master\fR\. . .SH "Configuration Parameter Reference" Below is a list of all documented parameters\. Not all of them are valid with all Puppet executables, but the executables will ignore any inappropriate values\. . +.SS "allow_duplicate_certs" +Whether to allow a new certificate request to overwrite an existing certificate\. +. +.IP "\(bu" 4 +\fIDefault\fR: false +. +.IP "" 0 +. .SS "archive_file_server" During an inspect run, the file bucket server to archive files to if archive_files is set\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "archive_files" During an inspect run, whether to archive files whose contents are audited to a file bucket\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "async_storeconfigs" Whether to use a queueing system to provide asynchronous database integration\. Requires that \fBpuppetqd\fR be running and that \'PSON\' support for ruby be installed\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "authconfig" The configuration file that defines the rights to the different namespaces and methods\. This can be used as a coarse\-grained authorization system for both \fBpuppet agent\fR and \fBpuppet master\fR\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/namespaceauth\.conf . .IP "" 0 . .SS "autoflush" Whether log files should always flush to disk\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "autosign" Whether to enable autosign\. Valid values are true (which autosigns any key request, and is a very bad idea), false (which never autosigns any key request), and the path to a file, which uses that configuration file to determine which keys to sign\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/autosign\.conf . .IP "" 0 . .SS "bindaddress" The address a listening server should bind to\. Mongrel servers default to 127\.0\.0\.1 and WEBrick defaults to 0\.0\.0\.0\. . .SS "bucketdir" Where FileBucket files are stored\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/bucket . .IP "" 0 . .SS "ca" Wether the master should function as a certificate authority\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "ca_days" How long a certificate should be valid\. This parameter is deprecated, use ca_ttl instead . .SS "ca_md" The type of hash used in certificates\. . .IP "\(bu" 4 \fIDefault\fR: md5 . .IP "" 0 . .SS "ca_name" The name to use the Certificate Authority certificate\. . .IP "\(bu" 4 \fIDefault\fR: Puppet CA: $certname . .IP "" 0 . .SS "ca_port" The port to use for the certificate authority\. . .IP "\(bu" 4 \fIDefault\fR: $masterport . .IP "" 0 . .SS "ca_server" The server to use for certificate authority requests\. It\'s a separate server because it cannot and does not need to horizontally scale\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "ca_ttl" The default TTL for new certificates; valid values must be an integer, optionally followed by one of the units \'y\' (years of 365 days), \'d\' (days), \'h\' (hours), or \'s\' (seconds)\. The unit defaults to seconds\. If this parameter is set, ca_days is ignored\. Examples are \'3600\' (one hour) and \'1825d\', which is the same as \'5y\' (5 years) . .IP "\(bu" 4 \fIDefault\fR: 5y . .IP "" 0 . .SS "cacert" The CA certificate\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_crt\.pem . .IP "" 0 . .SS "cacrl" The certificate revocation list (CRL) for the CA\. Will be used if present but otherwise ignored\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_crl\.pem . .IP "" 0 . .SS "cadir" The root directory for the certificate authority\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/ca . .IP "" 0 . .SS "cakey" The CA private key\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_key\.pem . .IP "" 0 . .SS "capass" Where the CA stores the password for the private key . .IP "\(bu" 4 \fIDefault\fR: $caprivatedir/ca\.pass . .IP "" 0 . .SS "caprivatedir" Where the CA stores private certificate information\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/private . .IP "" 0 . .SS "capub" The CA public key\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/ca_pub\.pem . .IP "" 0 . .SS "catalog_format" (Deprecated for \'preferred_serialization_format\') What format to use to dump the catalog\. Only supports \'marshal\' and \'yaml\'\. Only matters on the client, since it asks the server for a specific format\. . .SS "catalog_terminus" Where to get node catalogs\. This is useful to change if, for instance, you\'d like to pre\-compile catalogs and store them in memcached or some other easily\-accessed store\. . .IP "\(bu" 4 \fIDefault\fR: compiler . .IP "" 0 . .SS "cert_inventory" A Complete listing of all certificates . .IP "\(bu" 4 \fIDefault\fR: $cadir/inventory\.txt . .IP "" 0 . .SS "certdir" The certificate directory\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/certs . .IP "" 0 . .SS "certdnsnames" The DNS names on the Server certificate as a colon\-separated list\. If it\'s anything other than an empty string, it will be used as an alias in the created certificate\. By default, only the server gets an alias set up, and only for \'puppet\'\. . .SS "certificate_revocation" Whether certificate revocation should be supported by downloading a Certificate Revocation List (CRL) to all clients\. If enabled, CA chaining will almost definitely not work\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "certname" The name to use when handling certificates\. Defaults to the fully qualified domain name\. . .IP "\(bu" 4 -\fIDefault\fR: magpie\.puppetlabs\.lan +\fIDefault\fR: nick\-lewiss\-macbook\-pro\.local . .IP "" 0 . .SS "classfile" The file in which puppet agent stores a list of the classes associated with the retrieved configuration\. Can be loaded in the separate \fBpuppet\fR executable using the \fB\-\-loadclasses\fR option\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/classes\.txt . .IP "" 0 . .SS "client_datadir" The directory in which serialized data is stored on the client\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/client_data . .IP "" 0 . .SS "clientbucketdir" Where FileBucket files are stored locally\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/clientbucket . .IP "" 0 . .SS "clientyamldir" The directory in which client\-side YAML data is stored\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/client_yaml . .IP "" 0 . .SS "code" Code to parse directly\. This is essentially only used by \fBpuppet\fR, and should only be set if you\'re writing your own Puppet executable . .SS "color" Whether to use colors when logging to the console\. Valid values are \fBansi\fR (equivalent to \fBtrue\fR), \fBhtml\fR (mostly used during testing with TextMate), and \fBfalse\fR, which produces no color\. . .IP "\(bu" 4 \fIDefault\fR: ansi . .IP "" 0 . .SS "confdir" The main Puppet configuration directory\. The default for this parameter is calculated based on the user\. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it\'s running as any other user, it defaults to being in the user\'s home directory\. . .IP "\(bu" 4 \fIDefault\fR: /etc/puppet . .IP "" 0 . .SS "config" The configuration file for doc\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/puppet\.conf . .IP "" 0 . .SS "config_version" How to determine the configuration version\. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined\. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server\. . .SS "configprint" Print the value of a specific configuration parameter\. If a parameter is provided for this, then the value is printed and puppet exits\. Comma\-separate multiple values\. For a list of all values, specify \'all\'\. This feature is only available in Puppet versions higher than 0\.18\.4\. . .SS "configtimeout" How long the client should wait for the configuration to be retrieved before considering it a failure\. This can help reduce flapping if too many clients contact the server at one time\. . .IP "\(bu" 4 \fIDefault\fR: 120 . .IP "" 0 . .SS "couchdb_url" The url where the puppet couchdb database will be created . .IP "\(bu" 4 \fIDefault\fR: http://127\.0\.0\.1:5984/puppet . .IP "" 0 . .SS "csrdir" Where the CA stores certificate requests . .IP "\(bu" 4 \fIDefault\fR: $cadir/requests . .IP "" 0 . .SS "daemonize" Send the process into the background\. This is the default\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "dbadapter" The type of database to use\. . .IP "\(bu" 4 \fIDefault\fR: sqlite3 . .IP "" 0 . .SS "dbconnections" The number of database connections for networked databases\. Will be ignored unless the value is a positive integer\. . .SS "dblocation" The database cache for client configurations\. Used for querying within the language\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/clientconfigs\.sqlite3 . .IP "" 0 . .SS "dbmigrate" Whether to automatically migrate the database\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "dbname" The name of the database to use\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "dbpassword" The database password for caching\. Only used when networked databases are used\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "dbport" The database password for caching\. Only used when networked databases are used\. . .SS "dbserver" The database server for caching\. Only used when networked databases are used\. . .IP "\(bu" 4 \fIDefault\fR: localhost . .IP "" 0 . .SS "dbsocket" The database socket location\. Only used when networked databases are used\. Will be ignored if the value is an empty string\. . .SS "dbuser" The database user for caching\. Only used when networked databases are used\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "diff" Which diff command to use when printing differences between files\. . .IP "\(bu" 4 \fIDefault\fR: diff . .IP "" 0 . .SS "diff_args" Which arguments to pass to the diff command when printing differences between files\. . .IP "\(bu" 4 \fIDefault\fR: \-u . .IP "" 0 . +.SS "document_all" +Document all resources +. +.IP "\(bu" 4 +\fIDefault\fR: false +. +.IP "" 0 +. .SS "downcasefacts" Whether facts should be made all lowercase when sent to the server\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "dynamicfacts" Facts that are dynamic; these facts will be ignored when deciding whether changed facts should result in a recompile\. Multiple facts should be comma\-separated\. . .IP "\(bu" 4 \fIDefault\fR: memorysize,memoryfree,swapsize,swapfree . .IP "" 0 . .SS "environment" The environment Puppet is running in\. For clients (e\.g\., \fBpuppet agent\fR) this determines the environment itself, which is used to find modules and much more\. For servers (i\.e\., \fBpuppet master\fR) this provides the default environment for nodes we know nothing about\. . .IP "\(bu" 4 \fIDefault\fR: production . .IP "" 0 . .SS "evaltrace" Whether each resource should log when it is being evaluated\. This allows you to interactively see exactly what is being done\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "external_nodes" An external command that can produce node information\. The output must be a YAML dump of a hash, and that hash must have one or both of \fBclasses\fR and \fBparameters\fR, where \fBclasses\fR is an array and \fBparameters\fR is a hash\. For unknown nodes, the commands should exit with a non\-zero exit code\. This command makes it straightforward to store your node mapping information in other data sources like databases\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "factdest" Where Puppet should store facts that it pulls down from the central server\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/facts/ . .IP "" 0 . .SS "factpath" Where Puppet should look for facts\. Multiple directories should be colon\-separated, like normal PATH variables\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/lib/facter:$vardir/facts . .IP "" 0 . .SS "facts_terminus" The node facts terminus\. . .IP "\(bu" 4 \fIDefault\fR: facter . .IP "" 0 . .SS "factsignore" What files to ignore when pulling down facts\. . .IP "\(bu" 4 \fIDefault\fR: \.svn CVS . .IP "" 0 . .SS "factsource" From where to retrieve facts\. The standard Puppet \fBfile\fR type is used for retrieval, so anything that is a valid file source can be used here\. . .IP "\(bu" 4 \fIDefault\fR: puppet://$server/facts/ . .IP "" 0 . .SS "factsync" Whether facts should be synced with the central server\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "fileserverconfig" Where the fileserver configuration is stored\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/fileserver\.conf . .IP "" 0 . .SS "filetimeout" The minimum time to wait (in seconds) between checking for updates in configuration files\. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk\. . .IP "\(bu" 4 \fIDefault\fR: 15 . .IP "" 0 . .SS "freeze_main" Freezes the \'main\' class, disallowing any code to be added to it\. This essentially means that you can\'t have any code outside of a node, class, or definition other than in the site manifest\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "genconfig" Whether to just print a configuration to stdout and exit\. Only makes sense when used interactively\. Takes into account arguments specified on the CLI\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "genmanifest" Whether to just print a manifest to stdout and exit\. Only makes sense when used interactively\. Takes into account arguments specified on the CLI\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "graph" Whether to create dot graph files for the different configuration graphs\. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick)\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "graphdir" Where to store dot\-outputted graphs\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/graphs . .IP "" 0 . .SS "group" The group puppet master should run as\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "hostcert" Where individual hosts store and look for their certificates\. . .IP "\(bu" 4 \fIDefault\fR: $certdir/$certname\.pem . .IP "" 0 . .SS "hostcrl" Where the host\'s certificate revocation list can be found\. This is distinct from the certificate authority\'s CRL\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/crl\.pem . .IP "" 0 . .SS "hostcsr" Where individual hosts store and look for their certificate requests\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/csr_$certname\.pem . .IP "" 0 . .SS "hostprivkey" Where individual hosts store and look for their private key\. . .IP "\(bu" 4 \fIDefault\fR: $privatekeydir/$certname\.pem . .IP "" 0 . .SS "hostpubkey" Where individual hosts store and look for their public key\. . .IP "\(bu" 4 \fIDefault\fR: $publickeydir/$certname\.pem . .IP "" 0 . .SS "http_compression" Allow http compression in REST communication with the master\. This setting might improve performance for agent \-> master communications over slow WANs\. Your puppetmaster needs to support compression (usually by activating some settings in a reverse\-proxy in front of the puppetmaster, which rules out webrick)\. It is harmless to activate this settings if your master doesn\'t support compression, but if it supports it, this setting might reduce performance on high\-speed LANs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "http_proxy_host" The HTTP proxy host to use for outgoing connections\. Note: You may need to use a FQDN for the server hostname when using a proxy\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "http_proxy_port" The HTTP proxy port to use for outgoing connections . .IP "\(bu" 4 \fIDefault\fR: 3128 . .IP "" 0 . .SS "httplog" Where the puppet agent web server logs\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/http\.log . .IP "" 0 . .SS "ignorecache" Ignore cache and always recompile the configuration\. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date \- if the facts change or if the server changes\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ignoreimport" A parameter that can be used in commit hooks, since it enables you to parse\-check a single file rather than requiring that all files exist\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ignoreschedules" Boolean; whether puppet agent should ignore schedules\. This is useful for initial puppet agent runs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "inventory_port" The port to communicate with the inventory_server\. . .IP "\(bu" 4 \fIDefault\fR: $masterport . .IP "" 0 . .SS "inventory_server" The server to send facts to\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "inventory_terminus" Should usually be the same as the facts terminus . .IP "\(bu" 4 \fIDefault\fR: $facts_terminus . .IP "" 0 . .SS "keylength" The bit length of keys\. . .IP "\(bu" 4 \fIDefault\fR: 1024 . .IP "" 0 . .SS "lastrunfile" Where puppet agent stores the last run report summary in yaml format\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/last_run_summary\.yaml . .IP "" 0 . .SS "lastrunreport" Where puppet agent stores the last run report in yaml format\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/last_run_report\.yaml . .IP "" 0 . .SS "ldapattrs" The LDAP attributes to include when querying LDAP for nodes\. All returned attributes are set as variables in the top\-level scope\. Multiple values should be comma\-separated\. The value \'all\' returns all attributes\. . .IP "\(bu" 4 \fIDefault\fR: all . .IP "" 0 . .SS "ldapbase" The search base for LDAP searches\. It\'s impossible to provide a meaningful default here, although the LDAP libraries might have one already set\. Generally, it should be the \'ou=Hosts\' branch under your main directory\. . .SS "ldapclassattrs" The LDAP attributes to use to define Puppet classes\. Values should be comma\-separated\. . .IP "\(bu" 4 \fIDefault\fR: puppetclass . .IP "" 0 . .SS "ldapnodes" Whether to search for node configurations in LDAP\. See http://projects\.puppetlabs\.com/projects/puppet/wiki/LDAP_Nodes for more information\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ldapparentattr" The attribute to use to define the parent node\. . .IP "\(bu" 4 \fIDefault\fR: parentnode . .IP "" 0 . .SS "ldappassword" The password to use to connect to LDAP\. . .SS "ldapport" The LDAP port\. Only used if \fBldapnodes\fR is enabled\. . .IP "\(bu" 4 \fIDefault\fR: 389 . .IP "" 0 . .SS "ldapserver" The LDAP server\. Only used if \fBldapnodes\fR is enabled\. . .IP "\(bu" 4 \fIDefault\fR: ldap . .IP "" 0 . .SS "ldapssl" Whether SSL should be used when searching for nodes\. Defaults to false because SSL usually requires certificates to be set up on the client side\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ldapstackedattrs" The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree\. Values should be comma\-separated\. . .IP "\(bu" 4 \fIDefault\fR: puppetvar . .IP "" 0 . .SS "ldapstring" The search string used to find an LDAP node\. . .IP "\(bu" 4 \fIDefault\fR: (&(objectclass=puppetClient)(cn=%s)) . .IP "" 0 . .SS "ldaptls" Whether TLS should be used when searching for nodes\. Defaults to false because TLS usually requires certificates to be set up on the client side\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "ldapuser" The user to use to connect to LDAP\. Must be specified as a full DN\. . .SS "lexical" Whether to use lexical scoping (vs\. dynamic)\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "libdir" An extra search path for Puppet\. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases\. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby\'s search path . .IP "\(bu" 4 \fIDefault\fR: $vardir/lib . .IP "" 0 . .SS "listen" Whether puppet agent should listen for connections\. If this is true, then by default only the \fBrunner\fR server is started, which allows remote authorized and authenticated nodes to connect and trigger \fBpuppet agent\fR runs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "localcacert" Where each client stores the CA certificate\. . .IP "\(bu" 4 \fIDefault\fR: $certdir/ca\.pem . .IP "" 0 . .SS "localconfig" Where puppet agent caches the local configuration\. An extension indicating the cache format is added automatically\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/localconfig . .IP "" 0 . .SS "logdir" The Puppet log directory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/log . .IP "" 0 . .SS "manage_internal_file_permissions" Whether Puppet should manage the owner, group, and mode of files it uses internally . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "manifest" The entry\-point manifest for puppet master\. . .IP "\(bu" 4 \fIDefault\fR: $manifestdir/site\.pp . .IP "" 0 . .SS "manifestdir" Where puppet master looks for its manifests\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/manifests . .IP "" 0 . .SS "masterhttplog" Where the puppet master web server logs\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/masterhttp\.log . .IP "" 0 . .SS "masterlog" Where puppet master logs\. This is generally not used, since syslog is the default log destination\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/puppetmaster\.log . .IP "" 0 . .SS "masterport" Which port puppet master listens on\. . .IP "\(bu" 4 \fIDefault\fR: 8140 . .IP "" 0 . .SS "maximum_uid" The maximum allowed UID\. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system\. This is a hackish way to fail in a slightly more useful way when that happens\. . .IP "\(bu" 4 \fIDefault\fR: 4294967290 . .IP "" 0 . .SS "mkusers" Whether to create the necessary user and group that puppet agent will run as\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "modulepath" The search path for modules as a colon\-separated list of directories\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/modules:/usr/share/puppet/modules . .IP "" 0 . .SS "name" The name of the application, if we are running as one\. The default is essentially $0 without the path or \fB\.rb\fR\. . .IP "\(bu" 4 \fIDefault\fR: doc . .IP "" 0 . .SS "node_name" How the puppetmaster determines the client\'s identity and sets the \'hostname\', \'fqdn\' and \'domain\' facts for use in the manifest, in particular for determining which \'node\' statement applies to the client\. Possible values are \'cert\' (use the subject\'s CN in the client\'s certificate) and \'facter\' (use the hostname that the client reported in its facts) . .IP "\(bu" 4 \fIDefault\fR: cert . .IP "" 0 . .SS "node_terminus" Where to find information about nodes\. . .IP "\(bu" 4 \fIDefault\fR: plain . .IP "" 0 . .SS "noop" Whether puppet agent should be run in noop mode\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "onetime" Run the configuration once, rather than as a long\-running daemon\. This is useful for interactively running puppetd\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . -.SS "parseonly" -Just check the syntax of the manifests\. -. -.IP "\(bu" 4 -\fIDefault\fR: false -. -.IP "" 0 -. .SS "passfile" Where puppet agent stores the password for its private key\. Generally unused\. . .IP "\(bu" 4 \fIDefault\fR: $privatedir/password . .IP "" 0 . .SS "path" The shell search path\. Defaults to whatever is inherited from the parent process\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "pidfile" The pid file . .IP "\(bu" 4 \fIDefault\fR: $rundir/$name\.pid . .IP "" 0 . .SS "plugindest" Where Puppet should store plugins that it pulls down from the central server\. . .IP "\(bu" 4 \fIDefault\fR: $libdir . .IP "" 0 . .SS "pluginsignore" What files to ignore when pulling down plugins\. . .IP "\(bu" 4 \fIDefault\fR: \.svn CVS \.git . .IP "" 0 . .SS "pluginsource" From where to retrieve plugins\. The standard Puppet \fBfile\fR type is used for retrieval, so anything that is a valid file source can be used here\. . .IP "\(bu" 4 \fIDefault\fR: puppet://$server/plugins . .IP "" 0 . .SS "pluginsync" Whether plugins should be synced with the central server\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "postrun_command" A command to run after every agent run\. If this command returns a non\-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run\. . .SS "preferred_serialization_format" The preferred means of serializing ruby instances for passing over the wire\. This won\'t guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it\. . .IP "\(bu" 4 \fIDefault\fR: pson . .IP "" 0 . .SS "prerun_command" A command to run before every agent run\. If this command returns a non\-zero return code, the entire Puppet run will fail\. . .SS "privatedir" Where the client stores private certificate information\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/private . .IP "" 0 . .SS "privatekeydir" The private key directory\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/private_keys . .IP "" 0 . .SS "publickeydir" The public key directory\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/public_keys . .IP "" 0 . .SS "puppetdlockfile" A lock file to temporarily stop puppet agent from doing anything\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/puppetdlock . .IP "" 0 . .SS "puppetdlog" The log file for puppet agent\. This is generally not used\. . .IP "\(bu" 4 \fIDefault\fR: $logdir/puppetd\.log . .IP "" 0 . .SS "puppetport" Which port puppet agent listens on\. . .IP "\(bu" 4 \fIDefault\fR: 8139 . .IP "" 0 . .SS "queue_source" Which type of queue to use for asynchronous processing\. If your stomp server requires authentication, you can include it in the URI as long as your stomp client library is at least 1\.1\.1 . .IP "\(bu" 4 \fIDefault\fR: stomp://localhost:61613/ . .IP "" 0 . .SS "queue_type" Which type of queue to use for asynchronous processing\. . .IP "\(bu" 4 \fIDefault\fR: stomp . .IP "" 0 . .SS "rails_loglevel" The log level for Rails connections\. The value must be a valid log level within Rails\. Production environments normally use \fBinfo\fR and other environments normally use \fBdebug\fR\. . .IP "\(bu" 4 \fIDefault\fR: info . .IP "" 0 . .SS "railslog" Where Rails\-specific logs are sent . .IP "\(bu" 4 \fIDefault\fR: $logdir/rails\.log . .IP "" 0 . .SS "report" Whether to send reports after every transaction\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "report_port" The port to communicate with the report_server\. . .IP "\(bu" 4 \fIDefault\fR: $masterport . .IP "" 0 . .SS "report_server" The server to send transaction reports to\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "reportdir" The directory in which to store reports received from the client\. Each client gets a separate subdirectory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/reports . .IP "" 0 . .SS "reportfrom" The \'from\' email address for the reports\. . .IP "\(bu" 4 -\fIDefault\fR: report@magpie\.puppetlabs\.lan +\fIDefault\fR: report@Nick\-Lewiss\-MacBook\-Pro\.local . .IP "" 0 . .SS "reports" The list of reports to generate\. All reports are looked for in \fBpuppet/reports/name\.rb\fR, and multiple report names should be comma\-separated (whitespace is okay)\. . .IP "\(bu" 4 \fIDefault\fR: store . .IP "" 0 . .SS "reportserver" (Deprecated for \'report_server\') The server to which to send transaction reports\. . .IP "\(bu" 4 \fIDefault\fR: $server . .IP "" 0 . .SS "reporturl" The URL used by the http reports processor to send reports . .IP "\(bu" 4 \fIDefault\fR: http://localhost:3000/reports . .IP "" 0 . .SS "req_bits" The bit length of the certificates\. . .IP "\(bu" 4 \fIDefault\fR: 2048 . .IP "" 0 . .SS "requestdir" Where host certificate requests are stored\. . .IP "\(bu" 4 \fIDefault\fR: $ssldir/certificate_requests . .IP "" 0 . .SS "rest_authconfig" The configuration file that defines the rights to the different rest indirections\. This can be used as a fine\-grained authorization system for \fBpuppet master\fR\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/auth\.conf . .IP "" 0 . +.SS "route_file" +The YAML file containing indirector route configuration\. +. +.IP "\(bu" 4 +\fIDefault\fR: $confdir/routes\.yaml +. +.IP "" 0 +. .SS "rrddir" The directory where RRD database files are stored\. Directories for each reporting host will be created under this directory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/rrd . .IP "" 0 . .SS "rrdinterval" How often RRD should expect data\. This should match how often the hosts report back to the server\. . .IP "\(bu" 4 \fIDefault\fR: $runinterval . .IP "" 0 . .SS "run_mode" The effective \'run mode\' of the application: master, agent, or user\. . .IP "\(bu" 4 \fIDefault\fR: master . .IP "" 0 . .SS "rundir" Where Puppet PID files are kept\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/run . .IP "" 0 . .SS "runinterval" How often puppet agent applies the client configuration; in seconds\. . .IP "\(bu" 4 \fIDefault\fR: 1800 . .IP "" 0 . .SS "sendmail" Where to find the sendmail binary with which to send email\. . .IP "\(bu" 4 \fIDefault\fR: /usr/sbin/sendmail . .IP "" 0 . .SS "serial" Where the serial number for certificates is stored\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/serial . .IP "" 0 . .SS "server" The server to which server puppet agent should connect . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "server_datadir" The directory in which serialized data is stored, usually in a subdirectory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/server_data . .IP "" 0 . .SS "servertype" The type of server to use\. Currently supported options are webrick and mongrel\. If you use mongrel, you will need a proxy in front of the process or processes, since Mongrel cannot speak SSL\. . .IP "\(bu" 4 \fIDefault\fR: webrick . .IP "" 0 . .SS "show_diff" Whether to print a contextual diff when files are being replaced\. The diff is printed on stdout, so this option is meaningless unless you are running Puppet interactively\. This feature currently requires the \fBdiff/lcs\fR Ruby library\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "signeddir" Where the CA stores signed certificates\. . .IP "\(bu" 4 \fIDefault\fR: $cadir/signed . .IP "" 0 . .SS "smtpserver" The server through which to send email reports\. . .IP "\(bu" 4 \fIDefault\fR: none . .IP "" 0 . .SS "splay" Whether to sleep for a pseudo\-random (but consistent) amount of time before a run\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "splaylimit" The maximum time to delay before runs\. Defaults to being the same as the run interval\. . .IP "\(bu" 4 \fIDefault\fR: $runinterval . .IP "" 0 . .SS "ssl_client_header" The header containing an authenticated client\'s SSL DN\. Only used with Mongrel\. This header must be set by the proxy to the authenticated client\'s SSL DN (e\.g\., \fB/CN=puppet\.puppetlabs\.com\fR)\. See http://projects\.puppetlabs\.com/projects/puppet/wiki/Using_Mongrel for more information\. . .IP "\(bu" 4 \fIDefault\fR: HTTP_X_CLIENT_DN . .IP "" 0 . .SS "ssl_client_verify_header" The header containing the status message of the client verification\. Only used with Mongrel\. This header must be set by the proxy to \'SUCCESS\' if the client successfully authenticated, and anything else otherwise\. See http://projects\.puppetlabs\.com/projects/puppet/wiki/Using_Mongrel for more information\. . .IP "\(bu" 4 \fIDefault\fR: HTTP_X_CLIENT_VERIFY . .IP "" 0 . .SS "ssldir" Where SSL certificates are kept\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/ssl . .IP "" 0 . .SS "statedir" The directory where Puppet state is stored\. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/state . .IP "" 0 . .SS "statefile" Where puppet agent and puppet master store state associated with the running configuration\. In the case of puppet master, this file reflects the state discovered through interacting with clients\. . .IP "\(bu" 4 \fIDefault\fR: $statedir/state\.yaml . .IP "" 0 . .SS "storeconfigs" Whether to store each client\'s configuration\. This requires ActiveRecord from Ruby on Rails\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "strict_hostname_checking" Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "summarize" Whether to print a transaction summary\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "syslogfacility" What syslog facility to use when logging to syslog\. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up\. . .IP "\(bu" 4 \fIDefault\fR: daemon . .IP "" 0 . .SS "tagmap" The mapping between reporting tags and email addresses\. . .IP "\(bu" 4 \fIDefault\fR: $confdir/tagmail\.conf . .IP "" 0 . .SS "tags" Tags to use to find resources\. If this is set, then only resources tagged with the specified tags will be applied\. Values must be comma\-separated\. . .SS "templatedir" Where Puppet looks for template files\. Can be a list of colon\-seperated directories\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/templates . .IP "" 0 . .SS "thin_storeconfigs" Boolean; wether storeconfigs store in the database only the facts and exported resources\. If true, then storeconfigs performance will be higher and still allow exported/collected resources, but other usage external to Puppet might not work . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "trace" Whether to print stack traces on some errors . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "use_cached_catalog" Whether to only use the cached catalog rather than compiling a new catalog on every run\. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired\. . .IP "\(bu" 4 \fIDefault\fR: false . .IP "" 0 . .SS "usecacheonfailure" Whether to use the cached configuration when the remote configuration will not compile\. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known\-good one\. . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .SS "user" The user puppet master should run as\. . .IP "\(bu" 4 \fIDefault\fR: puppet . .IP "" 0 . .SS "vardir" Where Puppet stores dynamic and growing data\. The default for this parameter is calculated specially, like \fBconfdir\fR_\. . .IP "\(bu" 4 \fIDefault\fR: /var/lib/puppet . .IP "" 0 . .SS "yamldir" The directory in which YAML data is stored, usually in a subdirectory\. . .IP "\(bu" 4 \fIDefault\fR: $vardir/yaml . .IP "" 0 . .SS "zlib" Boolean; whether to use the zlib library . .IP "\(bu" 4 \fIDefault\fR: true . .IP "" 0 . .P -\fIThis page autogenerated on Thu Feb 17 09:56:34 \-0800 2011\fR +\fIThis page autogenerated on Wed Apr 13 14:24:43 \-0700 2011\fR diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index 876f55568..84acc28b2 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -1,32 +1,29 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') - +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' - require 'puppet/application/apply' describe "apply" do include PuppetSpec::Files describe "when applying provided catalogs", :if => Puppet.features.pson? do it "should be able to apply catalogs provided in a file in pson" do file_to_create = tmpfile("pson_catalog") catalog = Puppet::Resource::Catalog.new resource = Puppet::Resource.new(:file, file_to_create, :parameters => {:content => "my stuff"}) catalog.add_resource resource manifest = tmpfile("manifest") File.open(manifest, "w") { |f| f.print catalog.to_pson } puppet = Puppet::Application[:apply] puppet.options[:catalog] = manifest puppet.apply File.should be_exist(file_to_create) File.read(file_to_create).should == "my stuff" end end end diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb old mode 100644 new mode 100755 index f0b9d7db0..df9b91608 --- a/spec/integration/application/doc_spec.rb +++ b/spec/integration/application/doc_spec.rb @@ -1,55 +1,54 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' describe Puppet::Application::Doc do include PuppetSpec::Files it "should not generate an error when module dir overlaps parent of site.pp (#4798)" do begin # Note: the directory structure below is more complex than it # needs to be, but it's representative of the directory structure # used in bug #4798. old_dir = Dir.getwd # Note: can't use chdir with a block because it will generate bogus warnings tmpdir = tmpfile('doc_spec') Dir.mkdir(tmpdir) Dir.chdir(tmpdir) site_file = 'site.pp' File.open(site_file, 'w') do |f| f.puts '# A comment' end modules_dir = 'modules' Dir.mkdir(modules_dir) rt_dir = File.join(modules_dir, 'rt') Dir.mkdir(rt_dir) manifests_dir = File.join(rt_dir, 'manifests') Dir.mkdir(manifests_dir) rt_file = File.join(manifests_dir, 'rt.pp') File.open(rt_file, 'w') do |f| f.puts '# A class' f.puts 'class foo { }' f.puts '# A definition' f.puts 'define bar { }' end puppet = Puppet::Application[:doc] Puppet[:modulepath] = modules_dir Puppet[:manifest] = site_file puppet.options[:mode] = :rdoc puppet.expects(:exit).with(0) puppet.run_command File.should be_exist('doc') ensure Dir.chdir(old_dir) end end it "should respect the -o option" do puppetdoc = Puppet::Application[:doc] puppetdoc.command_line.stubs(:args).returns(['foo', '-o', 'bar']) puppetdoc.parse_options puppetdoc.options[:outputdir].should == 'bar' end end diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index 05b3d6146..f5d8bceb2 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -1,73 +1,72 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/configurer' describe Puppet::Configurer do include PuppetSpec::Files describe "when downloading plugins" do it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do resource = Puppet::Type.type(:notify).new :name => "yay" Puppet::Type.type(:file).expects(:new).with { |args| args[:ignore] == Puppet[:pluginsignore].split(/\s+/) }.returns resource configurer = Puppet::Configurer.new configurer.stubs(:download_plugins?).returns true configurer.download_plugins end end describe "when running" do before(:each) do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing")) # Make sure we don't try to persist the local state after the transaction ran, # because it will fail during test (the state file is in an not existing directory) # and we need the transaction to be successful to be able to produce a summary report @catalog.host_config = false @configurer = Puppet::Configurer.new end it "should send a transaction report with valid data" do @configurer.stubs(:save_last_run_summary) Puppet::Transaction::Report.indirection.expects(:save).with do |report, x| report.time.class == Time and report.logs.length > 0 end Puppet[:report] = true @configurer.run :catalog => @catalog end it "should save a correct last run summary" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.indirection.stubs(:save) Puppet[:lastrunfile] = tmpfile("lastrunfile") Puppet[:report] = true # We only record integer seconds in the timestamp, and truncate # backwards, so don't use a more accurate timestamp in the test. # --daniel 2011-03-07 t1 = Time.now.tv_sec @configurer.run :catalog => @catalog, :report => report t2 = Time.now.tv_sec summary = nil File.open(Puppet[:lastrunfile], "r") do |fd| summary = YAML.load(fd.read) end summary.should be_a(Hash) %w{time changes events resources}.each do |key| summary.should be_key(key) end summary["time"].should be_key("notify") summary["time"]["last_run"].should be_between(t1, t2) end end end diff --git a/spec/integration/defaults_spec.rb b/spec/integration/defaults_spec.rb index ee0a43d07..8aa59288e 100755 --- a/spec/integration/defaults_spec.rb +++ b/spec/integration/defaults_spec.rb @@ -1,259 +1,258 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/defaults' require 'puppet/rails' describe "Puppet defaults" do include Puppet::Util::Execution after { Puppet.settings.clear } describe "when setting the :factpath" do it "should add the :factpath to Facter's search paths" do Facter.expects(:search).with("/my/fact/path") Puppet.settings[:factpath] = "/my/fact/path" end end describe "when setting the :certname" do it "should fail if the certname is not downcased" do lambda { Puppet.settings[:certname] = "Host.Domain.Com" }.should raise_error(ArgumentError) end end describe "when configuring the :crl" do it "should warn if :cacrl is set to false" do Puppet.expects(:warning) Puppet.settings[:cacrl] = 'false' end end describe "when setting the :catalog_format" do it "should log a deprecation notice" do Puppet.expects(:warning) Puppet.settings[:catalog_format] = 'marshal' end it "should copy the value to :preferred_serialization_format" do Puppet.settings[:catalog_format] = 'marshal' Puppet.settings[:preferred_serialization_format].should == 'marshal' end end it "should have a clientyamldir setting" do Puppet.settings[:clientyamldir].should_not be_nil end it "should have different values for the yamldir and clientyamldir" do Puppet.settings[:yamldir].should_not == Puppet.settings[:clientyamldir] end it "should have a client_datadir setting" do Puppet.settings[:client_datadir].should_not be_nil end it "should have different values for the server_datadir and client_datadir" do Puppet.settings[:server_datadir].should_not == Puppet.settings[:client_datadir] end # See #1232 it "should not specify a user or group for the clientyamldir" do Puppet.settings.setting(:clientyamldir).owner.should be_nil Puppet.settings.setting(:clientyamldir).group.should be_nil end it "should use the service user and group for the yamldir" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:yamldir).owner.should == Puppet.settings[:user] Puppet.settings.setting(:yamldir).group.should == Puppet.settings[:group] end # See #1232 it "should not specify a user or group for the rundir" do Puppet.settings.setting(:rundir).owner.should be_nil Puppet.settings.setting(:rundir).group.should be_nil end it "should specify that the host private key should be owned by the service user" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:hostprivkey).owner.should == Puppet.settings[:user] end it "should specify that the host certificate should be owned by the service user" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:hostcert).owner.should == Puppet.settings[:user] end it "should use a bind address of ''" do Puppet.settings.clear Puppet.settings[:bindaddress].should == "" end [:factdest].each do |setting| it "should force the :factdest to be a directory" do Puppet.settings[setting].should =~ /\/$/ end end [:modulepath, :factpath].each do |setting| it "should configure '#{setting}' not to be a file setting, so multi-directory settings are acceptable" do Puppet.settings.setting(setting).should be_instance_of(Puppet::Util::Settings::Setting) end end it "should add /usr/sbin and /sbin to the path if they're not there" do withenv("PATH" => "/usr/bin:/usr/local/bin") do Puppet.settings[:path] = "none" # this causes it to ignore the setting ENV["PATH"].split(File::PATH_SEPARATOR).should be_include("/usr/sbin") ENV["PATH"].split(File::PATH_SEPARATOR).should be_include("/sbin") end end it "should default to pson for the preferred serialization format" do Puppet.settings.value(:preferred_serialization_format).should == "pson" end describe "when enabling storeconfigs" do before do Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet.features.stubs(:rails?).returns true end it "should set the Catalog cache class to :active_record" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:active_record) Puppet.settings[:storeconfigs] = true end it "should not set the Catalog cache class to :active_record if asynchronous storeconfigs is enabled" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:active_record).never Puppet.settings.expects(:value).with(:async_storeconfigs).returns true Puppet.settings[:storeconfigs] = true end it "should set the Facts cache class to :active_record" do Puppet::Node::Facts.indirection.expects(:cache_class=).with(:active_record) Puppet.settings[:storeconfigs] = true end it "should set the Node cache class to :active_record" do Puppet::Node.indirection.expects(:cache_class=).with(:active_record) Puppet.settings[:storeconfigs] = true end it "should fail if rails is not available" do Puppet.features.stubs(:rails?).returns false lambda { Puppet.settings[:storeconfigs] = true }.should raise_error end end describe "when enabling asynchronous storeconfigs" do before do Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet.features.stubs(:rails?).returns true end it "should set storeconfigs to true" do Puppet.settings[:async_storeconfigs] = true Puppet.settings[:storeconfigs].should be_true end it "should set the Catalog cache class to :queue" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:queue) Puppet.settings[:async_storeconfigs] = true end it "should set the Facts cache class to :active_record" do Puppet::Node::Facts.indirection.expects(:cache_class=).with(:active_record) Puppet.settings[:storeconfigs] = true end it "should set the Node cache class to :active_record" do Puppet::Node.indirection.expects(:cache_class=).with(:active_record) Puppet.settings[:storeconfigs] = true end end describe "when enabling thin storeconfigs" do before do Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet.features.stubs(:rails?).returns true end it "should set storeconfigs to true" do Puppet.settings[:thin_storeconfigs] = true Puppet.settings[:storeconfigs].should be_true end end it "should have a setting for determining the configuration version and should default to an empty string" do Puppet.settings[:config_version].should == "" end describe "when enabling reports" do it "should use the default server value when report server is unspecified" do Puppet.settings[:server] = "server" Puppet.settings[:report_server].should == "server" end it "should use the default masterport value when report port is unspecified" do Puppet.settings[:masterport] = "1234" Puppet.settings[:report_port].should == "1234" end it "should set report_server when reportserver is set" do Puppet.settings[:reportserver] = "reportserver" Puppet.settings[:report_server].should == "reportserver" end it "should use report_port when set" do Puppet.settings[:masterport] = "1234" Puppet.settings[:report_port] = "5678" Puppet.settings[:report_port].should == "5678" end it "should prefer report_server over reportserver" do Puppet.settings[:reportserver] = "reportserver" Puppet.settings[:report_server] = "report_server" Puppet.settings[:report_server].should == "report_server" end end it "should have a :caname setting that defaults to the cert name" do Puppet.settings[:certname] = "foo" Puppet.settings[:ca_name].should == "Puppet CA: foo" end it "should have a 'prerun_command' that defaults to the empty string" do Puppet.settings[:prerun_command].should == "" end it "should have a 'postrun_command' that defaults to the empty string" do Puppet.settings[:postrun_command].should == "" end it "should have a 'certificate_revocation' setting that defaults to true" do Puppet.settings[:certificate_revocation].should be_true end it "should have an http_compression setting that defaults to false" do Puppet.settings[:http_compression].should be_false end describe "reportdir" do subject { Puppet.settings[:reportdir] } it { should == "#{Puppet[:vardir]}/reports" } end describe "reporturl" do subject { Puppet.settings[:reporturl] } it { should == "http://localhost:3000/reports" } end end diff --git a/spec/integration/file_serving/content_spec.rb b/spec/integration/file_serving/content_spec.rb index 207720d56..a95ddc520 100755 --- a/spec/integration/file_serving/content_spec.rb +++ b/spec/integration/file_serving/content_spec.rb @@ -1,20 +1,20 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/file_serving/content' require 'shared_behaviours/file_serving' describe Puppet::FileServing::Content, " when finding files" do it_should_behave_like "Puppet::FileServing::Files" before do @test_class = Puppet::FileServing::Content @indirection = Puppet::FileServing::Content.indirection end after { Puppet::Util::Cacher.expire } end diff --git a/spec/integration/file_serving/fileset_spec.rb b/spec/integration/file_serving/fileset_spec.rb index f607aa203..f4b869847 100755 --- a/spec/integration/file_serving/fileset_spec.rb +++ b/spec/integration/file_serving/fileset_spec.rb @@ -1,14 +1,13 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/fileset' describe Puppet::FileServing::Fileset do it "should be able to recurse on a single file" do @path = Tempfile.new("fileset_integration") fileset = Puppet::FileServing::Fileset.new(@path.path) lambda { fileset.files }.should_not raise_error end end diff --git a/spec/integration/file_serving/metadata_spec.rb b/spec/integration/file_serving/metadata_spec.rb index 627369ff8..ba7d3311f 100755 --- a/spec/integration/file_serving/metadata_spec.rb +++ b/spec/integration/file_serving/metadata_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/file_serving/metadata' require 'shared_behaviours/file_serving' require 'puppet/indirector/file_metadata/file_server' describe Puppet::FileServing::Metadata, " when finding files" do it_should_behave_like "Puppet::FileServing::Files" before do @test_class = Puppet::FileServing::Metadata @indirection = Puppet::FileServing::Metadata.indirection end after { Puppet::Util::Cacher.expire } end diff --git a/spec/integration/file_serving/terminus_helper_spec.rb b/spec/integration/file_serving/terminus_helper_spec.rb index 615b24c87..7500b1fc0 100755 --- a/spec/integration/file_serving/terminus_helper_spec.rb +++ b/spec/integration/file_serving/terminus_helper_spec.rb @@ -1,22 +1,21 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/terminus_helper' class TerminusHelperIntegrationTester include Puppet::FileServing::TerminusHelper def model Puppet::FileServing::Metadata end end describe Puppet::FileServing::TerminusHelper do it "should be able to recurse on a single file" do @path = Tempfile.new("fileset_integration") request = Puppet::Indirector::Request.new(:metadata, :find, @path.path, :recurse => true) tester = TerminusHelperIntegrationTester.new lambda { tester.path2instances(request, @path.path) }.should_not raise_error end end diff --git a/spec/integration/indirector/catalog/compiler_spec.rb b/spec/integration/indirector/catalog/compiler_spec.rb index dafa1af7c..f51a3f24e 100755 --- a/spec/integration/indirector/catalog/compiler_spec.rb +++ b/spec/integration/indirector/catalog/compiler_spec.rb @@ -1,65 +1,64 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/catalog' Puppet::Resource::Catalog.indirection.terminus(:compiler) describe Puppet::Resource::Catalog::Compiler do before do Facter.stubs(:value).returns "something" @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@one = Puppet::Resource.new(:file, "/one")) @catalog.add_resource(@two = Puppet::Resource.new(:file, "/two")) end after { Puppet.settings.clear } it "should remove virtual resources when filtering" do @one.virtual = true Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.should == [ @two.ref ] end it "should not remove exported resources when filtering" do @one.exported = true Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.sort.should == [ @one.ref, @two.ref ] end it "should remove virtual exported resources when filtering" do @one.exported = true @one.virtual = true Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.should == [ @two.ref ] end it "should filter out virtual resources when finding a catalog" do @one.virtual = true request = stub 'request', :name => "mynode" Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog) Puppet::Resource::Catalog.indirection.find(request).resource_refs.should == [ @two.ref ] end it "should not filter out exported resources when finding a catalog" do @one.exported = true request = stub 'request', :name => "mynode" Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog) Puppet::Resource::Catalog.indirection.find(request).resource_refs.sort.should == [ @one.ref, @two.ref ] end it "should filter out virtual exported resources when finding a catalog" do @one.exported = true @one.virtual = true request = stub 'request', :name => "mynode" Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog) Puppet::Resource::Catalog.indirection.find(request).resource_refs.should == [ @two.ref ] end end diff --git a/spec/integration/indirector/catalog/queue_spec.rb b/spec/integration/indirector/catalog/queue_spec.rb index 237e489f9..569f096bf 100755 --- a/spec/integration/indirector/catalog/queue_spec.rb +++ b/spec/integration/indirector/catalog/queue_spec.rb @@ -1,58 +1,57 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/catalog' describe "Puppet::Resource::Catalog::Queue", :if => Puppet.features.pson? do before do Puppet::Resource::Catalog.indirection.terminus(:queue) @catalog = Puppet::Resource::Catalog.new @one = Puppet::Resource.new(:file, "/one") @two = Puppet::Resource.new(:file, "/two") @catalog.add_resource(@one, @two) @catalog.add_edge(@one, @two) Puppet[:trace] = true end after { Puppet.settings.clear } it "should render catalogs to pson and send them via the queue client when catalogs are saved" do terminus = Puppet::Resource::Catalog.indirection.terminus(:queue) client = mock 'client' terminus.stubs(:client).returns client client.expects(:send_message).with(:catalog, @catalog.to_pson) request = Puppet::Indirector::Request.new(:catalog, :save, "foo", :instance => @catalog) terminus.save(request) end it "should intern catalog messages when they are passed via a subscription" do client = mock 'client' Puppet::Resource::Catalog::Queue.stubs(:client).returns client pson = @catalog.to_pson client.expects(:subscribe).with(:catalog).yields(pson) Puppet.expects(:err).never result = [] Puppet::Resource::Catalog::Queue.subscribe do |catalog| result << catalog end catalog = result.shift catalog.should be_instance_of(Puppet::Resource::Catalog) catalog.resource(:file, "/one").should be_instance_of(Puppet::Resource) catalog.resource(:file, "/two").should be_instance_of(Puppet::Resource) catalog.should be_edge(catalog.resource(:file, "/one"), catalog.resource(:file, "/two")) end end diff --git a/spec/integration/indirector/direct_file_server_spec.rb b/spec/integration/indirector/direct_file_server_spec.rb index 627733b09..e53b48d69 100755 --- a/spec/integration/indirector/direct_file_server_spec.rb +++ b/spec/integration/indirector/direct_file_server_spec.rb @@ -1,73 +1,73 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-19. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_content/file' describe Puppet::Indirector::DirectFileServer, " when interacting with the filesystem and the model" do before do # We just test a subclass, since it's close enough. @terminus = Puppet::Indirector::FileContent::File.new @filepath = "/path/to/my/file" end it "should return an instance of the model" do FileTest.expects(:exists?).with(@filepath).returns(true) @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}")).should be_instance_of(Puppet::FileServing::Content) end it "should return an instance capable of returning its content" do FileTest.expects(:exists?).with(@filepath).returns(true) File.stubs(:lstat).with(@filepath).returns(stub("stat", :ftype => "file")) File.expects(:read).with(@filepath).returns("my content") instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}")) instance.content.should == "my content" end end describe Puppet::Indirector::DirectFileServer, " when interacting with FileServing::Fileset and the model" do before do @terminus = Puppet::Indirector::FileContent::File.new @path = Tempfile.new("direct_file_server_testing") path = @path.path @path.close! @path = path Dir.mkdir(@path) File.open(File.join(@path, "one"), "w") { |f| f.print "one content" } File.open(File.join(@path, "two"), "w") { |f| f.print "two content" } @request = @terminus.indirection.request(:search, "file:///#{@path}", :recurse => true) end after do system("rm -rf #{@path}") end it "should return an instance for every file in the fileset" do result = @terminus.search(@request) result.should be_instance_of(Array) result.length.should == 3 result.each { |r| r.should be_instance_of(Puppet::FileServing::Content) } end it "should return instances capable of returning their content" do @terminus.search(@request).each do |instance| case instance.full_path when /one/; instance.content.should == "one content" when /two/; instance.content.should == "two content" when @path else raise "No valid key for #{instance.path.inspect}" end end end end diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb index 9268ef067..88d2345d8 100755 --- a/spec/integration/indirector/file_content/file_server_spec.rb +++ b/spec/integration/indirector/file_content/file_server_spec.rb @@ -1,94 +1,94 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_content/file_server' require 'shared_behaviours/file_server_terminus' require 'puppet_spec/files' describe Puppet::Indirector::FileContent::FileServer, " when finding files" do it_should_behave_like "Puppet::Indirector::FileServerTerminus" include PuppetSpec::Files before do @terminus = Puppet::Indirector::FileContent::FileServer.new @test_class = Puppet::FileServing::Content end it "should find plugin file content in the environment specified in the request" do path = tmpfile("file_content_with_env") Dir.mkdir(path) modpath = File.join(path, "mod") FileUtils.mkdir_p(File.join(modpath, "lib")) file = File.join(modpath, "lib", "file.rb") File.open(file, "w") { |f| f.puts "1" } Puppet.settings[:modulepath] = "/no/such/file" env = Puppet::Node::Environment.new("foo") env.stubs(:modulepath).returns [path] result = Puppet::FileServing::Content.indirection.search("plugins", :environment => "foo", :recurse => true) result.should_not be_nil result.length.should == 2 result[1].should be_instance_of(Puppet::FileServing::Content) result[1].content.should == "1\n" end it "should find file content in modules" do path = tmpfile("file_content") Dir.mkdir(path) modpath = File.join(path, "mymod") FileUtils.mkdir_p(File.join(modpath, "files")) file = File.join(modpath, "files", "myfile") File.open(file, "w") { |f| f.puts "1" } Puppet.settings[:modulepath] = path result = Puppet::FileServing::Content.indirection.find("modules/mymod/myfile") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\n" end it "should find file content in files when node name expansions are used" do Puppet::Util::Cacher.expire FileTest.stubs(:exists?).returns true FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true) @path = tmpfile("file_server_testing") Dir.mkdir(@path) subdir = File.join(@path, "mynode") Dir.mkdir(subdir) File.open(File.join(subdir, "myfile"), "w") { |f| f.puts "1" } # Use a real mount, so the integration is a bit deeper. @mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") @mount1.stubs(:allowed?).returns true @mount1.path = File.join(@path, "%h") @parser = stub 'parser', :changed? => false @parser.stubs(:parse).returns("one" => @mount1) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) path = File.join(@path, "myfile") result = Puppet::FileServing::Content.indirection.find("one/myfile", :environment => "foo", :node => "mynode") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\n" end end diff --git a/spec/integration/indirector/file_metadata/file_server_spec.rb b/spec/integration/indirector/file_metadata/file_server_spec.rb index 069e7af08..9e84134a6 100755 --- a/spec/integration/indirector/file_metadata/file_server_spec.rb +++ b/spec/integration/indirector/file_metadata/file_server_spec.rb @@ -1,18 +1,18 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_metadata/file_server' require 'shared_behaviours/file_server_terminus' describe Puppet::Indirector::FileMetadata::FileServer, " when finding files" do it_should_behave_like "Puppet::Indirector::FileServerTerminus" before do @terminus = Puppet::Indirector::FileMetadata::FileServer.new @test_class = Puppet::FileServing::Metadata end end diff --git a/spec/integration/indirector/node/ldap_spec.rb b/spec/integration/indirector/node/ldap_spec.rb index 3da6d0e73..7e53141dc 100755 --- a/spec/integration/indirector/node/ldap_spec.rb +++ b/spec/integration/indirector/node/ldap_spec.rb @@ -1,15 +1,14 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do it "should use a restrictive filter when searching for nodes in a class" do ldap = Puppet::Node.indirection.terminus(:ldap) Puppet::Node.indirection.stubs(:terminus).returns ldap ldap.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=foo))") Puppet::Node.indirection.search "eh", :class => "foo" end end diff --git a/spec/integration/network/client_spec.rb b/spec/integration/network/client_spec.rb index 07a491b1b..72174c75c 100755 --- a/spec/integration/network/client_spec.rb +++ b/spec/integration/network/client_spec.rb @@ -1,19 +1,18 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/client' describe Puppet::Network::Client do %w{ca file report runner status}.each do |name| it "should have a #{name} client" do Puppet::Network::Client.client(name).should be_instance_of(Class) end [:name, :handler, :drivername].each do |data| it "should have a #{data} value for the #{name} client" do Puppet::Network::Client.client(name).send(data).should_not be_nil end end end end diff --git a/spec/integration/network/formats_spec.rb b/spec/integration/network/formats_spec.rb index 8eb963e19..af505f267 100755 --- a/spec/integration/network/formats_spec.rb +++ b/spec/integration/network/formats_spec.rb @@ -1,107 +1,106 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/formats' class PsonIntTest attr_accessor :string def ==(other) other.class == self.class and string == other.string end def self.from_pson(data) new(data[0]) end def initialize(string) @string = string end def to_pson(*args) { 'type' => self.class.name, 'data' => [@string] }.to_pson(*args) end def self.canonical_order(s) s.gsub(/\{"data":\[(.*?)\],"type":"PsonIntTest"\}/,'{"type":"PsonIntTest","data":[\1]}') end end describe Puppet::Network::FormatHandler.format(:s) do before do @format = Puppet::Network::FormatHandler.format(:s) end it "should support certificates" do @format.should be_supported(Puppet::SSL::Certificate) end it "should not support catalogs" do @format.should_not be_supported(Puppet::Resource::Catalog) end end describe Puppet::Network::FormatHandler.format(:pson) do describe "when pson is absent", :if => (! Puppet.features.pson?) do before do @pson = Puppet::Network::FormatHandler.format(:pson) end it "should not be suitable" do @pson.should_not be_suitable end end describe "when pson is available", :if => Puppet.features.pson? do before do @pson = Puppet::Network::FormatHandler.format(:pson) end it "should be able to render an instance to pson" do instance = PsonIntTest.new("foo") PsonIntTest.canonical_order(@pson.render(instance)).should == PsonIntTest.canonical_order('{"type":"PsonIntTest","data":["foo"]}' ) end it "should be able to render arrays to pson" do @pson.render([1,2]).should == '[1,2]' end it "should be able to render arrays containing hashes to pson" do @pson.render([{"one"=>1},{"two"=>2}]).should == '[{"one":1},{"two":2}]' end it "should be able to render multiple instances to pson" do Puppet.features.add(:pson, :libs => ["pson"]) one = PsonIntTest.new("one") two = PsonIntTest.new("two") PsonIntTest.canonical_order(@pson.render([one,two])).should == PsonIntTest.canonical_order('[{"type":"PsonIntTest","data":["one"]},{"type":"PsonIntTest","data":["two"]}]') end it "should be able to intern pson into an instance" do @pson.intern(PsonIntTest, '{"type":"PsonIntTest","data":["foo"]}').should == PsonIntTest.new("foo") end it "should be able to intern pson with no class information into an instance" do @pson.intern(PsonIntTest, '["foo"]').should == PsonIntTest.new("foo") end it "should be able to intern multiple instances from pson" do @pson.intern_multiple(PsonIntTest, '[{"type": "PsonIntTest", "data": ["one"]},{"type": "PsonIntTest", "data": ["two"]}]').should == [ PsonIntTest.new("one"), PsonIntTest.new("two") ] end it "should be able to intern multiple instances from pson with no class information" do @pson.intern_multiple(PsonIntTest, '[["one"],["two"]]').should == [ PsonIntTest.new("one"), PsonIntTest.new("two") ] end end end diff --git a/spec/integration/network/handler_spec.rb b/spec/integration/network/handler_spec.rb index a3095c125..dc0837c13 100755 --- a/spec/integration/network/handler_spec.rb +++ b/spec/integration/network/handler_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/client' describe Puppet::Network::Handler do %w{ca filebucket fileserver master report runner status}.each do |name| it "should have a #{name} client" do Puppet::Network::Handler.handler(name).should be_instance_of(Class) end it "should have a name" do Puppet::Network::Handler.handler(name).name.to_s.downcase.should == name.to_s.downcase end it "should have an interface" do Puppet::Network::Handler.handler(name).interface.should_not be_nil end it "should have a prefix for the interface" do Puppet::Network::Handler.handler(name).interface.prefix.should_not be_nil end end end diff --git a/spec/integration/network/server/mongrel_spec.rb b/spec/integration/network/server/mongrel_spec.rb index aeaaad6ae..fd5903b9e 100755 --- a/spec/integration/network/server/mongrel_spec.rb +++ b/spec/integration/network/server/mongrel_spec.rb @@ -1,65 +1,64 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/server' require 'socket' describe Puppet::Network::Server do describe "when using mongrel", :if => Puppet.features.mongrel? do before :each do Puppet[:servertype] = 'mongrel' Puppet[:server] = '127.0.0.1' @params = { :port => 34346, :handlers => [ :node ] } @server = Puppet::Network::Server.new(@params) end after { Puppet.settings.clear } describe "before listening" do it "should not be reachable at the specified address and port" do lambda { TCPSocket.new('127.0.0.1', 34346) }.should raise_error(Errno::ECONNREFUSED) end end describe "when listening" do it "should be reachable on the specified address and port" do @server.listen lambda { TCPSocket.new('127.0.0.1', 34346) }.should_not raise_error end it "should default to '127.0.0.1' as its bind address" do @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.stubs(:unlisten) # we're breaking listening internally, so we have to keep it from unlistening @server.send(:http_server).expects(:listen).with { |args| args[:address] == "127.0.0.1" } @server.listen end it "should use any specified bind address" do Puppet[:bindaddress] = "0.0.0.0" @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.stubs(:unlisten) # we're breaking listening internally, so we have to keep it from unlistening @server.send(:http_server).expects(:listen).with { |args| args[:address] == "0.0.0.0" } @server.listen end it "should not allow multiple servers to listen on the same address and port" do @server.listen @server2 = Puppet::Network::Server.new(@params) lambda { @server2.listen }.should raise_error end end describe "after unlistening" do it "should not be reachable on the port and address assigned" do @server.listen @server.unlisten lambda { TCPSocket.new('127.0.0.1', 34346) }.should raise_error(Errno::ECONNREFUSED) end end after :each do @server.unlisten if @server.listening? end end end diff --git a/spec/integration/network/server/webrick_spec.rb b/spec/integration/network/server/webrick_spec.rb index 0e1bb4580..81c35af4f 100755 --- a/spec/integration/network/server/webrick_spec.rb +++ b/spec/integration/network/server/webrick_spec.rb @@ -1,87 +1,86 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/server' require 'puppet/ssl/certificate_authority' require 'socket' describe Puppet::Network::Server do describe "when using webrick" do before :each do Puppet[:servertype] = 'webrick' Puppet[:server] = '127.0.0.1' @params = { :port => 34343, :handlers => [ :node ], :xmlrpc_handlers => [ :status ] } # Get a safe temporary file @tmpfile = Tempfile.new("webrick_integration_testing") @dir = @tmpfile.path + "_dir" Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local ca = Puppet::SSL::CertificateAuthority.new ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.indirection.find(Puppet[:certname]) end after do @tmpfile.delete Puppet.settings.clear system("rm -rf #{@dir}") Puppet::SSL::Host.ca_location = :none Puppet::Util::Cacher.expire end describe "before listening" do it "should not be reachable at the specified address and port" do lambda { TCPSocket.new('127.0.0.1', 34343) }.should raise_error end end describe "when listening" do it "should be reachable on the specified address and port" do @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.listen lambda { TCPSocket.new('127.0.0.1', 34343) }.should_not raise_error end it "should default to '0.0.0.0' as its bind address" do Puppet.settings.clear Puppet[:servertype] = 'webrick' Puppet[:bindaddress].should == '0.0.0.0' end it "should use any specified bind address" do Puppet[:bindaddress] = "127.0.0.1" @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.stubs(:unlisten) # we're breaking listening internally, so we have to keep it from unlistening @server.send(:http_server).expects(:listen).with { |args| args[:address] == "127.0.0.1" } @server.listen end it "should not allow multiple servers to listen on the same address and port" do @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.listen @server2 = Puppet::Network::Server.new(@params.merge(:port => 34343)) lambda { @server2.listen }.should raise_error end after :each do @server.unlisten if @server && @server.listening? end end describe "after unlistening" do it "should not be reachable on the port and address assigned" do @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.listen @server.unlisten lambda { TCPSocket.new('127.0.0.1', 34343) }.should raise_error(Errno::ECONNREFUSED) end end end end diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index abf0108fa..25a14afd4 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -1,58 +1,57 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' describe Puppet::Node::Environment do include PuppetSpec::Files it "should be able to return each module from its environment with the environment, name, and path set correctly" do base = tmpfile("env_modules") Dir.mkdir(base) dirs = [] mods = {} %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir Dir.mkdir(dir) mod = "mod#{num}" moddir = File.join(dir, mod) mods[mod] = moddir Dir.mkdir(moddir) end environment = Puppet::Node::Environment.new("foo") environment.stubs(:modulepath).returns dirs environment.modules.each do |mod| mod.environment.should == environment mod.path.should == mods[mod.name] end end it "should not yield the same module from different module paths" do base = tmpfile("env_modules") Dir.mkdir(base) dirs = [] mods = {} %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir Dir.mkdir(dir) mod = "mod" moddir = File.join(dir, mod) mods[mod] = moddir Dir.mkdir(moddir) end environment = Puppet::Node::Environment.new("foo") environment.stubs(:modulepath).returns dirs mods = environment.modules mods.length.should == 1 mods[0].path.should == File.join(base, "dir1", "mod") end end diff --git a/spec/integration/node/facts_spec.rb b/spec/integration/node/facts_spec.rb index ee956a6b1..f54d7f9aa 100755 --- a/spec/integration/node/facts_spec.rb +++ b/spec/integration/node/facts_spec.rb @@ -1,47 +1,47 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-8. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' describe Puppet::Node::Facts do describe "when using the indirector" do after { Puppet::Util::Cacher.expire } it "should expire any cached node instances when it is saved" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml Puppet::Node::Facts.indirection.terminus(:yaml).should equal(Puppet::Node::Facts.indirection.terminus(:yaml)) terminus = Puppet::Node::Facts.indirection.terminus(:yaml) terminus.stubs :save Puppet::Node.indirection.expects(:expire).with("me") facts = Puppet::Node::Facts.new("me") Puppet::Node::Facts.indirection.save(facts) end it "should be able to delegate to the :yaml terminus" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml # Load now, before we stub the exists? method. terminus = Puppet::Node::Facts.indirection.terminus(:yaml) terminus.expects(:path).with("me").returns "/my/yaml/file" FileTest.expects(:exist?).with("/my/yaml/file").returns false Puppet::Node::Facts.indirection.find("me").should be_nil end it "should be able to delegate to the :facter terminus" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :facter Facter.expects(:to_hash).returns "facter_hash" facts = Puppet::Node::Facts.new("me") Puppet::Node::Facts.expects(:new).with("me", "facter_hash").returns facts Puppet::Node::Facts.indirection.find("me").should equal(facts) end end end diff --git a/spec/integration/node_spec.rb b/spec/integration/node_spec.rb index 4c8a2c2b1..e15ddfc1e 100755 --- a/spec/integration/node_spec.rb +++ b/spec/integration/node_spec.rb @@ -1,93 +1,93 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-23. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet/node' describe Puppet::Node do describe "when delegating indirection calls" do before do @name = "me" @node = Puppet::Node.new(@name) end it "should be able to use the exec terminus" do Puppet::Node.indirection.stubs(:terminus_class).returns :exec # Load now so we can stub terminus = Puppet::Node.indirection.terminus(:exec) terminus.expects(:query).with(@name).returns "myresults" terminus.expects(:translate).with(@name, "myresults").returns "translated_results" terminus.expects(:create_node).with(@name, "translated_results").returns @node Puppet::Node.indirection.find(@name).should equal(@node) end it "should be able to use the yaml terminus" do Puppet::Node.indirection.stubs(:terminus_class).returns :yaml # Load now, before we stub the exists? method. terminus = Puppet::Node.indirection.terminus(:yaml) terminus.expects(:path).with(@name).returns "/my/yaml/file" FileTest.expects(:exist?).with("/my/yaml/file").returns false Puppet::Node.indirection.find(@name).should be_nil end it "should have an ldap terminus" do Puppet::Node.indirection.terminus(:ldap).should_not be_nil end it "should be able to use the plain terminus" do Puppet::Node.indirection.stubs(:terminus_class).returns :plain # Load now, before we stub the exists? method. Puppet::Node.indirection.terminus(:plain) Puppet::Node.expects(:new).with(@name).returns @node Puppet::Node.indirection.find(@name).should equal(@node) end describe "and using the memory terminus" do before do @name = "me" @old_terminus = Puppet::Node.indirection.terminus_class @terminus = Puppet::Node.indirection.terminus(:memory) Puppet::Node.indirection.stubs(:terminus).returns @terminus @node = Puppet::Node.new(@name) end it "should find no nodes by default" do Puppet::Node.indirection.find(@name).should be_nil end it "should be able to find nodes that were previously saved" do Puppet::Node.indirection.save(@node) Puppet::Node.indirection.find(@name).should equal(@node) end it "should replace existing saved nodes when a new node with the same name is saved" do Puppet::Node.indirection.save(@node) two = Puppet::Node.new(@name) Puppet::Node.indirection.save(two) Puppet::Node.indirection.find(@name).should equal(two) end it "should be able to remove previously saved nodes" do Puppet::Node.indirection.save(@node) Puppet::Node.indirection.destroy(@node.name) Puppet::Node.indirection.find(@name).should be_nil end it "should fail when asked to destroy a node that does not exist" do proc { Puppet::Node.indirection.destroy(@node) }.should raise_error(ArgumentError) end end end end diff --git a/spec/integration/parser/collector_spec.rb b/spec/integration/parser/collector_spec.rb index c72e605cf..c14fa4184 100755 --- a/spec/integration/parser/collector_spec.rb +++ b/spec/integration/parser/collector_spec.rb @@ -1,38 +1,37 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/collector' describe Puppet::Parser::Collector do before do @scope = Puppet::Parser::Scope.new(:compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))) @resource = Puppet::Parser::Resource.new("file", "/tmp/testing", :scope => @scope, :source => "fakesource") {:owner => "root", :group => "bin", :mode => "644"}.each do |param, value| @resource[param] = value end end def query(text) code = "File <| #{text} |>" parser = Puppet::Parser::Parser.new(@scope.compiler) return parser.parse(code).code[0].query end {true => [%{title == "/tmp/testing"}, %{(title == "/tmp/testing")}, %{group == bin}, %{title == "/tmp/testing" and group == bin}, %{title == bin or group == bin}, %{title == "/tmp/testing" or title == bin}, %{title == "/tmp/testing"}, %{(title == "/tmp/testing" or title == bin) and group == bin}], false => [%{title == bin}, %{title == bin or (title == bin and group == bin)}, %{title != "/tmp/testing"}, %{title != "/tmp/testing" and group != bin}] }.each do |result, ary| ary.each do |string| it "should return '#{result}' when collecting resources with '#{string}'" do str, code = query(string).evaluate @scope code.should be_instance_of(Proc) code.call(@resource).should == result end end end end diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index 097e8b03a..9f6aae907 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -1,134 +1,133 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::Compiler do before :each do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end after do Puppet.settings.clear end it "should be able to determine the configuration version from a local version control repository" do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' @parser = Puppet::Parser::Parser.new "development" @compiler = Puppet::Parser::Compiler.new(@node) @compiler.catalog.version.should == version end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do Puppet[:code] = <<-PP class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP @node.stubs(:classes).returns(['foo', 'bar']) catalog = Puppet::Parser::Compiler.compile(@node) catalog.resource("Notify[foo_notify]").should_not be_nil catalog.resource("Notify[bar_notify]").should_not be_nil end describe "when resolving class references" do it "should favor local scope, even if there's an included class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include baz include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end it "should favor local scope, even if there's an unincluded class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['class', 'define', 'node'].each do |thing| it "should not allow #{thing} inside evaluated conditional constructs" do Puppet[:code] = <<-PP if true { #{thing} foo { } notify { decoy: } } PP begin Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) raise "compilation should have raised Puppet::Error" rescue Puppet::Error => e e.message.should =~ /at line 2/ end end end it "should not allow classes inside unevaluated conditional constructs" do Puppet[:code] = <<-PP if false { class foo { } } PP lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error) end end diff --git a/spec/integration/parser/functions/require_spec.rb b/spec/integration/parser/functions/require_spec.rb index 737efcce8..aa15f92be 100755 --- a/spec/integration/parser/functions/require_spec.rb +++ b/spec/integration/parser/functions/require_spec.rb @@ -1,44 +1,43 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "The require function" do before :each do @node = Puppet::Node.new("mynode") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.send(:evaluate_main) @compiler.catalog.client_version = "0.25" @scope = @compiler.topscope # preload our functions Puppet::Parser::Functions.function(:include) Puppet::Parser::Functions.function(:require) end it "should add a dependency between the 'required' class and our class" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass") @scope.function_require("requiredclass") @scope.resource["require"].should_not be_nil ref = @scope.resource["require"].shift ref.type.should == "Class" ref.title.should == "Requiredclass" end it "should queue relationships between the 'required' class and our classes" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass1") @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass2") @scope.function_require("requiredclass1") @scope.function_require("requiredclass2") @scope.resource["require"].should_not be_nil (ref1,ref2) = @scope.resource["require"] ref1.type.should == "Class" ref1.title.should == "Requiredclass1" ref2.type.should == "Class" ref2.title.should == "Requiredclass2" end end diff --git a/spec/integration/parser/functions_spec.rb b/spec/integration/parser/functions_spec.rb old mode 100644 new mode 100755 index 504d1fe5e..6791987d7 --- a/spec/integration/parser/functions_spec.rb +++ b/spec/integration/parser/functions_spec.rb @@ -1,21 +1,20 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::Functions do before :each do Puppet::Parser::Functions.rmfunction("template") if Puppet::Parser::Functions.function("template") end it "should support multiple threads autoloading the same function" do threads = [] lambda { 10.times { |a| threads << Thread.new { Puppet::Parser::Functions.function("template") } } }.should_not raise_error threads.each { |t| t.join } end -end \ No newline at end of file +end diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index b7dfcc3e0..65c9ee302 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -1,122 +1,121 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::Parser do module ParseMatcher class ParseAs def initialize(klass) @parser = Puppet::Parser::Parser.new "development" @class = klass end def result_instance @result.code[0] end def matches?(string) @string = string @result = @parser.parse(string) result_instance.instance_of?(@class) end def description "parse as a #{@class}" end def failure_message " expected #{@string} to parse as #{@class} but was #{result_instance.class}" end def negative_failure_message " expected #{@string} not to parse as #{@class}" end end def parse_as(klass) ParseAs.new(klass) end class ParseWith def initialize(block) @parser = Puppet::Parser::Parser.new "development" @block = block end def result_instance @result.code[0] end def matches?(string) @string = string @result = @parser.parse(string) @block.call(result_instance) end def description "parse with the block evaluating to true" end def failure_message " expected #{@string} to parse with a true result in the block" end def negative_failure_message " expected #{@string} not to parse with a true result in the block" end end def parse_with(&block) ParseWith.new(block) end end include ParseMatcher before :each do @resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser = Puppet::Parser::Parser.new "development" end describe "when parsing comments before statement" do it "should associate the documentation to the statement AST node" do ast = @parser.parse(""" # comment class test {} """) ast.code[0].should be_a(Puppet::Parser::AST::Hostclass) ast.code[0].name.should == 'test' ast.code[0].instantiate('')[0].doc.should == "comment\n" end end describe "when parsing" do it "should be able to parse normal left to right relationships" do "Notify[foo] -> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should be able to parse right to left relationships" do "Notify[foo] <- Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should be able to parse normal left to right subscriptions" do "Notify[foo] ~> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should be able to parse right to left subscriptions" do "Notify[foo] <~ Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should correctly set the arrow type of a relationship" do "Notify[foo] <~ Notify[bar]".should parse_with { |rel| rel.arrow == "<~" } end it "should be able to parse deep hash access" do %q{ $hash = { 'a' => { 'b' => { 'c' => 'it works' } } } $out = $hash['a']['b']['c'] }.should parse_with { |v| v.value.is_a?(Puppet::Parser::AST::ASTHash) } end end end diff --git a/spec/integration/parser/ruby_manifest_spec.rb b/spec/integration/parser/ruby_manifest_spec.rb old mode 100644 new mode 100755 index 110898206..7f3bb71e9 --- a/spec/integration/parser/ruby_manifest_spec.rb +++ b/spec/integration/parser/ruby_manifest_spec.rb @@ -1,128 +1,127 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'tempfile' require 'puppet_spec/files' describe "Pure ruby manifests" do include PuppetSpec::Files before do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") @test_dir = tmpdir('ruby_manifest_test') end after do Puppet.settings.clear end def write_file(name, contents) path = File.join(@test_dir, name) File.open(path, "w") { |f| f.write(contents) } path end def compile(contents) Puppet[:code] = contents Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end end it "should allow classes" do write_file('foo.rb', ["hostclass 'one' do notify('one_notify') end", "hostclass 'two' do notify('two_notify') end"].join("\n")) catalog = compile("import 'foo'\ninclude one") catalog.resource("Notify[one_notify]").should_not be_nil catalog.resource("Notify[two_notify]").should be_nil end it "should allow defines" do write_file('foo.rb', 'define "bar", :arg do notify("bar_#{@name}_#{@arg}") end') catalog = compile("import 'foo'\nbar { instance: arg => 'xyz' }") catalog.resource("Notify[bar_instance_xyz]").should_not be_nil catalog.resource("Bar[instance]").should_not be_nil end it "should allow node declarations" do write_file('foo.rb', "node 'mynode' do notify('mynode') end") catalog = compile("import 'foo'") node_declaration = catalog.resource("Notify[mynode]") node_declaration.should_not be_nil node_declaration.title.should == 'mynode' end it "should allow access to the environment" do write_file('foo.rb', ["hostclass 'bar' do", " if environment.is_a? Puppet::Node::Environment", " notify('success')", " end", "end"].join("\n")) compile("import 'foo'\ninclude bar").resource("Notify[success]").should_not be_nil end it "should allow creation of resources of built-in types" do write_file('foo.rb', "hostclass 'bar' do file 'test_file', :owner => 'root', :mode => '644' end") catalog = compile("import 'foo'\ninclude bar") file = catalog.resource("File[test_file]") file.should be_a(Puppet::Resource) file.type.should == 'File' file.title.should == 'test_file' file.exported.should_not be file.virtual.should_not be file[:owner].should == 'root' file[:mode].should == '644' file[:stage].should be_nil # TODO: is this correct behavior? end it "should allow calling user-defined functions" do write_file('foo.rb', "hostclass 'bar' do user_func 'name', :arg => 'xyz' end") catalog = compile(['define user_func($arg) { notify {"n_$arg": } }', 'import "foo"', 'include bar'].join("\n")) catalog.resource("Notify[n_xyz]").should_not be_nil catalog.resource("User_func[name]").should_not be_nil end it "should be properly cached for multiple compiles" do # Note: we can't test this by calling compile() twice, because # that sets Puppet[:code], which clears out all cached # environments. Puppet[:filetimeout] = 1000 write_file('foo.rb', "hostclass 'bar' do notify('success') end") Puppet[:code] = "import 'foo'\ninclude bar" # Compile the catalog and check it catalog = Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end catalog.resource("Notify[success]").should_not be_nil # Secretly change the file to make it invalid. This change # shouldn't be noticed because the we've set a high # Puppet[:filetimeout]. write_file('foo.rb', "raise 'should not be executed'") # Compile the catalog a second time and make sure it's still ok. catalog = Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end catalog.resource("Notify[success]").should_not be_nil end it "should be properly reloaded when stale" do Puppet[:filetimeout] = -1 # force stale check to happen all the time write_file('foo.rb', "hostclass 'bar' do notify('version1') end") catalog = compile("import 'foo'\ninclude bar") catalog.resource("Notify[version1]").should_not be_nil sleep 1 # so that timestamp will change forcing file reload write_file('foo.rb', "hostclass 'bar' do notify('version2') end") catalog = compile("import 'foo'\ninclude bar") catalog.resource("Notify[version1]").should be_nil catalog.resource("Notify[version2]").should_not be_nil end end diff --git a/spec/integration/provider/mailalias/aliases_spec.rb b/spec/integration/provider/mailalias/aliases_spec.rb index 19e430ba1..232704e61 100755 --- a/spec/integration/provider/mailalias/aliases_spec.rb +++ b/spec/integration/provider/mailalias/aliases_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env ruby -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' provider_class = Puppet::Type.type(:mailalias).provider(:aliases) describe provider_class do # #1560, in which we corrupt the format of complex mail aliases. it_should_behave_like "all parsedfile providers", provider_class end diff --git a/spec/integration/provider/mount_spec.rb b/spec/integration/provider/mount_spec.rb old mode 100644 new mode 100755 index d6f25fe1d..4af0dca4a --- a/spec/integration/provider/mount_spec.rb +++ b/spec/integration/provider/mount_spec.rb @@ -1,151 +1,151 @@ -require File.dirname(__FILE__) + '/../../spec_helper' +require 'spec_helper' require 'puppet/file_bucket/dipper' describe "mount provider (integration)" do include PuppetSpec::Files def create_fake_fstab(initially_contains_entry) File.open(@fake_fstab, 'w') do |f| if initially_contains_entry f.puts("/dev/disk1s1\t/Volumes/foo_disk\tmsdos\tlocal\t0\t0") end end end before :each do @fake_fstab = tmpfile('fstab') @current_options = "local" @current_device = "/dev/disk1s1" Puppet::Type.type(:mount).defaultprovider.stubs(:default_target).returns(@fake_fstab) Facter.stubs(:value).with(:operatingsystem).returns('Darwin') Puppet::Util::ExecutionStub.set do |command, options| case command[0] when %r{/s?bin/mount} if command.length == 1 if @mounted "#{@current_device} on /Volumes/foo_disk (msdos, #{@current_options})\n" else '' end else command.length.should == 4 command[1].should == '-o' command[3].should == '/Volumes/foo_disk' @mounted.should == false # verify that we don't try to call "mount" redundantly @current_options = command[2] @current_device = check_fstab(true) @mounted = true '' end when %r{/s?bin/umount} command.length.should == 2 command[1].should == '/Volumes/foo_disk' @mounted.should == true # "umount" doesn't work when device not mounted (see #6632) @mounted = false '' else fail "Unexpected command #{command.inspect} executed" end end end after :each do Puppet::Type::Mount::ProviderParsed.clear # Work around bug #6628 end def check_fstab(expected_to_be_present) # Verify that the fake fstab has the expected data in it - fstab_contents = File.read(@fake_fstab).lines.map(&:chomp).reject { |x| x =~ /^#|^$/ } + fstab_contents = File.read(@fake_fstab).split("\n").reject { |x| x =~ /^#|^$/ } if expected_to_be_present fstab_contents.length().should == 1 device, rest_of_line = fstab_contents[0].split(/\t/,2) rest_of_line.should == "/Volumes/foo_disk\tmsdos\t#{@desired_options}\t0\t0" device else fstab_contents.length().should == 0 nil end end def run_in_catalog(settings) resource = Puppet::Type.type(:mount).new(settings.merge(:name => "/Volumes/foo_disk", :device => "/dev/disk1s1", :fstype => "msdos")) Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to the filebucket resource.expects(:err).never catalog = Puppet::Resource::Catalog.new catalog.host_config = false # Stop Puppet from doing a bunch of magic catalog.add_resource resource catalog.apply end [false, true].each do |initial_state| describe "When initially #{initial_state ? 'mounted' : 'unmounted'}" do before :each do @mounted = initial_state end [false, true].each do |initial_fstab_entry| describe "When there is #{initial_fstab_entry ? 'an' : 'no'} initial fstab entry" do before :each do create_fake_fstab(initial_fstab_entry) end [:defined, :present, :mounted, :unmounted, :absent].each do |ensure_setting| expected_final_state = case ensure_setting when :mounted true when :unmounted, :absent false when :defined, :present initial_state else fail "Unknown ensure_setting #{ensure_setting}" end expected_fstab_data = (ensure_setting != :absent) describe "When setting ensure => #{ensure_setting}" do ["local", "journaled"].each do |options_setting| describe "When setting options => #{options_setting}" do it "should leave the system in the #{expected_final_state ? 'mounted' : 'unmounted'} state, #{expected_fstab_data ? 'with' : 'without'} data in /etc/fstab" do @desired_options = options_setting run_in_catalog(:ensure=>ensure_setting, :options => options_setting) @mounted.should == expected_final_state if expected_fstab_data check_fstab(expected_fstab_data).should == "/dev/disk1s1" else check_fstab(expected_fstab_data).should == nil end if @mounted if ![:defined, :present].include?(ensure_setting) @current_options.should == @desired_options elsif initial_fstab_entry @current_options.should == @desired_options else @current_options.should == 'local' #Workaround for #6645 end end end end end end end end end end end describe "When the wrong device is mounted" do it "should remount the correct device" do pending "Due to bug 6309" @mounted = true @current_device = "/dev/disk2s2" create_fake_fstab(true) @desired_options = "local" run_in_catalog(:ensure=>:mounted, :options=>'local') @current_device.should=="/dev/disk1s1" @mounted.should==true @current_options.should=='local' check_fstab(true).should == "/dev/disk1s1" end end end diff --git a/spec/integration/provider/package_spec.rb b/spec/integration/provider/package_spec.rb index cc03c57a7..2d75826e8 100755 --- a/spec/integration/provider/package_spec.rb +++ b/spec/integration/provider/package_spec.rb @@ -1,24 +1,23 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "Package Provider" do Puppet::Type.type(:package).providers.each do |name| provider = Puppet::Type.type(:package).provider(name) describe name, :if => provider.suitable? do it "should fail when asked to install an invalid package" do pending("This test hangs forever with recent versions of RubyGems") if provider.name == :gem pkg = Puppet::Type.newpackage :name => "nosuch#{provider.name}", :provider => provider.name lambda { pkg.provider.install }.should raise_error end it "should be able to get a list of existing packages" do provider.instances.each do |package| package.should be_instance_of(provider) package.properties[:provider].should == provider.name end end end end end diff --git a/spec/integration/provider/service/init_spec.rb b/spec/integration/provider/service/init_spec.rb index c74ce29fe..ef7d98b4c 100755 --- a/spec/integration/provider/service/init_spec.rb +++ b/spec/integration/provider/service/init_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider = Puppet::Type.type(:service).provider(:init) describe provider do describe "when running on FreeBSD", :if => (Facter.value(:operatingsystem) == "FreeBSD") do it "should set its default path to include /etc/init.d and /usr/local/etc/init.d" do provider.defpath.should == ["/etc/rc.d", "/usr/local/etc/rc.d"] end end describe "when running on HP-UX", :if => (Facter.value(:operatingsystem) == "HP-UX")do it "should set its default path to include /sbin/init.d" do provider.defpath.should == "/sbin/init.d" end end describe "when not running on FreeBSD or HP-UX", :if => (! %w{HP-UX FreeBSD}.include?(Facter.value(:operatingsystem))) do it "should set its default path to include /etc/init.d" do provider.defpath.should == "/etc/init.d" end end end diff --git a/spec/integration/reference/providers_spec.rb b/spec/integration/reference/providers_spec.rb index d6e19cb59..6d87ee02e 100755 --- a/spec/integration/reference/providers_spec.rb +++ b/spec/integration/reference/providers_spec.rb @@ -1,17 +1,16 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/reference' reference = Puppet::Util::Reference.reference(:providers) describe reference do it "should exist" do reference.should_not be_nil end it "should be able to be rendered as markdown" do lambda { reference.to_markdown }.should_not raise_error end end diff --git a/spec/integration/reports_spec.rb b/spec/integration/reports_spec.rb index a721d75d8..d5a08d28a 100755 --- a/spec/integration/reports_spec.rb +++ b/spec/integration/reports_spec.rb @@ -1,18 +1,18 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-12. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet/reports' describe Puppet::Reports, " when using report types" do before do Puppet.settings.stubs(:use) end it "should load report types as modules" do Puppet::Reports.report(:store).should be_instance_of(Module) end end diff --git a/spec/integration/resource/catalog_spec.rb b/spec/integration/resource/catalog_spec.rb index 21162655e..41a475d98 100755 --- a/spec/integration/resource/catalog_spec.rb +++ b/spec/integration/resource/catalog_spec.rb @@ -1,60 +1,60 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-4-8. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' describe Puppet::Resource::Catalog do describe "when pson is available", :if => Puppet.features.pson? do it "should support pson" do Puppet::Resource::Catalog.supported_formats.should be_include(:pson) end end describe "when using the indirector" do after { Puppet::Util::Cacher.expire } before do # This is so the tests work w/out networking. Facter.stubs(:to_hash).returns({"hostname" => "foo.domain.com"}) Facter.stubs(:value).returns("eh") end it "should be able to delegate to the :yaml terminus" do Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :yaml # Load now, before we stub the exists? method. terminus = Puppet::Resource::Catalog.indirection.terminus(:yaml) terminus.expects(:path).with("me").returns "/my/yaml/file" FileTest.expects(:exist?).with("/my/yaml/file").returns false Puppet::Resource::Catalog.indirection.find("me").should be_nil end it "should be able to delegate to the :compiler terminus" do Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :compiler # Load now, before we stub the exists? method. compiler = Puppet::Resource::Catalog.indirection.terminus(:compiler) node = mock 'node' node.stub_everything Puppet::Node.indirection.expects(:find).returns(node) compiler.expects(:compile).with(node).returns nil Puppet::Resource::Catalog.indirection.find("me").should be_nil end it "should pass provided node information directly to the terminus" do terminus = mock 'terminus' Puppet::Resource::Catalog.indirection.stubs(:terminus).returns terminus node = mock 'node' terminus.expects(:find).with { |request| request.options[:use_node] == node } Puppet::Resource::Catalog.indirection.find("me", :use_node => node) end end end diff --git a/spec/integration/resource/type_collection_spec.rb b/spec/integration/resource/type_collection_spec.rb index f6ba9c7af..6ea2e7fe7 100755 --- a/spec/integration/resource/type_collection_spec.rb +++ b/spec/integration/resource/type_collection_spec.rb @@ -1,96 +1,95 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' require 'puppet/resource/type_collection' describe Puppet::Resource::TypeCollection do describe "when autoloading from modules" do include PuppetSpec::Files before do @dir = tmpfile("autoload_testing") Puppet[:modulepath] = @dir FileUtils.mkdir_p @dir @code = Puppet::Resource::TypeCollection.new("env") Puppet::Node::Environment.new("env").stubs(:known_resource_types).returns @code end # Setup a module. def mk_module(name, files = {}) mdir = File.join(@dir, name) mandir = File.join(mdir, "manifests") FileUtils.mkdir_p mandir defs = files.delete(:define) Dir.chdir(mandir) do files.each do |file, classes| File.open("#{file}.pp", "w") do |f| classes.each { |klass| if defs f.puts "define #{klass} {}" else f.puts "class #{klass} {}" end } end end end end it "should return nil when a class can't be found or loaded" do @code.find_hostclass('', 'nosuchclass').should be_nil end it "should load the module's init file first" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("", name).name.should == name end it "should load the module's init file even when searching from a different namespace" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("other::ns", name).name.should == name end it "should be able to load definitions from the module base file" do name = "simpdef" mk_module(name, :define => true, :init => [name]) @code.find_definition("", name).name.should == name end it "should be able to load qualified classes from the module base file" do modname = "both" name = "sub" mk_module(modname, :init => %w{both both::sub}) @code.find_hostclass("both", name).name.should == "both::sub" end it "should be able load classes from a separate file" do modname = "separate" name = "sub" mk_module(modname, :init => %w{separate}, :sub => %w{separate::sub}) @code.find_hostclass("separate", name).name.should == "separate::sub" end it "should not fail when loading from a separate file if there is no module file" do modname = "alone" name = "sub" mk_module(modname, :sub => %w{alone::sub}) lambda { @code.find_hostclass("alone", name) }.should_not raise_error end it "should be able to load definitions from their own file" do name = "mymod" mk_module(name, :define => true, :mydefine => ["mymod::mydefine"]) @code.find_definition("", "mymod::mydefine").name.should == "mymod::mydefine" end end end diff --git a/spec/integration/ssl/certificate_authority_spec.rb b/spec/integration/ssl/certificate_authority_spec.rb index 17b56ea98..c5e145459 100755 --- a/spec/integration/ssl/certificate_authority_spec.rb +++ b/spec/integration/ssl/certificate_authority_spec.rb @@ -1,134 +1,134 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-17. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/ssl/certificate_authority' require 'tempfile' describe Puppet::SSL::CertificateAuthority do before do # Get a safe temporary file file = Tempfile.new("ca_integration_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local @ca = Puppet::SSL::CertificateAuthority.new end after { Puppet::SSL::Host.ca_location = :none system("rm -rf #{@dir}") Puppet.settings.clear Puppet::Util::Cacher.expire Puppet::SSL::CertificateAuthority.instance_variable_set("@instance", nil) } it "should create a CA host" do @ca.host.should be_ca end it "should be able to generate a certificate" do @ca.generate_ca_certificate @ca.host.certificate.should be_instance_of(Puppet::SSL::Certificate) end it "should be able to generate a new host certificate" do @ca.generate("newhost") Puppet::SSL::Certificate.indirection.find("newhost").should be_instance_of(Puppet::SSL::Certificate) end it "should be able to revoke a host certificate" do @ca.generate("newhost") @ca.revoke("newhost") lambda { @ca.verify("newhost") }.should raise_error end it "should have a CRL" do @ca.generate_ca_certificate @ca.crl.should_not be_nil end it "should be able to read in a previously created CRL" do @ca.generate_ca_certificate # Create it to start with. @ca.crl Puppet::SSL::CertificateAuthority.new.crl.should_not be_nil end describe "when signing certificates" do before do @host = Puppet::SSL::Host.new("luke.madstop.com") # We have to provide the key, since when we're in :ca_only mode, we can only interact # with the CA key. key = Puppet::SSL::Key.new(@host.name) key.generate @host.key = key @host.generate_certificate_request path = File.join(Puppet[:requestdir], "luke.madstop.com.pem") end it "should be able to sign certificates" do @ca.sign("luke.madstop.com") end it "should save the signed certificate" do @ca.sign("luke.madstop.com") Puppet::SSL::Certificate.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::Certificate) end it "should be able to sign multiple certificates" do @other = Puppet::SSL::Host.new("other.madstop.com") okey = Puppet::SSL::Key.new(@other.name) okey.generate @other.key = okey @other.generate_certificate_request @ca.sign("luke.madstop.com") @ca.sign("other.madstop.com") Puppet::SSL::Certificate.indirection.find("other.madstop.com").should be_instance_of(Puppet::SSL::Certificate) Puppet::SSL::Certificate.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::Certificate) end it "should save the signed certificate to the :signeddir" do @ca.sign("luke.madstop.com") client_cert = File.join(Puppet[:signeddir], "luke.madstop.com.pem") File.read(client_cert).should == Puppet::SSL::Certificate.indirection.find("luke.madstop.com").content.to_s end it "should save valid certificates" do @ca.sign("luke.madstop.com") unless ssl = Puppet::Util::which('openssl') pending "No ssl available" else ca_cert = Puppet[:cacert] client_cert = File.join(Puppet[:signeddir], "luke.madstop.com.pem") output = %x{openssl verify -CAfile #{ca_cert} #{client_cert}} $CHILD_STATUS.should == 0 end end end end diff --git a/spec/integration/ssl/certificate_request_spec.rb b/spec/integration/ssl/certificate_request_spec.rb index f1040055b..688466c37 100755 --- a/spec/integration/ssl/certificate_request_spec.rb +++ b/spec/integration/ssl/certificate_request_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-17. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/ssl/certificate_request' require 'tempfile' describe Puppet::SSL::CertificateRequest do before do # Get a safe temporary file file = Tempfile.new("csr_integration_testing") @dir = file.path file.delete Dir.mkdir(@dir) Puppet.settings.clear Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :none @csr = Puppet::SSL::CertificateRequest.new("luke.madstop.com") @key = OpenSSL::PKey::RSA.new(512) end after do system("rm -rf #{@dir}") Puppet.settings.clear # This is necessary so the terminus instances don't lie around. Puppet::Util::Cacher.expire end it "should be able to generate CSRs" do @csr.generate(@key) end it "should be able to save CSRs" do Puppet::SSL::CertificateRequest.indirection.save(@csr) end it "should be able to find saved certificate requests via the Indirector" do @csr.generate(@key) Puppet::SSL::CertificateRequest.indirection.save(@csr) Puppet::SSL::CertificateRequest.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::CertificateRequest) end it "should save the completely CSR when saving" do @csr.generate(@key) Puppet::SSL::CertificateRequest.indirection.save(@csr) Puppet::SSL::CertificateRequest.indirection.find("luke.madstop.com").content.to_s.should == @csr.content.to_s end end diff --git a/spec/integration/ssl/certificate_revocation_list_spec.rb b/spec/integration/ssl/certificate_revocation_list_spec.rb index ddf7952f3..051a81569 100755 --- a/spec/integration/ssl/certificate_revocation_list_spec.rb +++ b/spec/integration/ssl/certificate_revocation_list_spec.rb @@ -1,43 +1,43 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-5-5. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/ssl/certificate_revocation_list' require 'tempfile' describe Puppet::SSL::CertificateRevocationList do before do # Get a safe temporary file file = Tempfile.new("ca_integration_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local end after { Puppet::SSL::Host.ca_location = :none system("rm -rf #{@dir}") Puppet.settings.clear # This is necessary so the terminus instances don't lie around. Puppet::Util::Cacher.expire } it "should be able to read in written out CRLs with no revoked certificates" do ca = Puppet::SSL::CertificateAuthority.new raise "CRL not created" unless FileTest.exist?(Puppet[:hostcrl]) crl = Puppet::SSL::CertificateRevocationList.new("crl_int_testing") crl.read(Puppet[:hostcrl]) end end diff --git a/spec/integration/ssl/host_spec.rb b/spec/integration/ssl/host_spec.rb index 84160b019..e9c37c151 100755 --- a/spec/integration/ssl/host_spec.rb +++ b/spec/integration/ssl/host_spec.rb @@ -1,91 +1,91 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-17. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/ssl/host' require 'tempfile' describe Puppet::SSL::Host do before do # Get a safe temporary file file = Tempfile.new("host_integration_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local @host = Puppet::SSL::Host.new("luke.madstop.com") @ca = Puppet::SSL::CertificateAuthority.new end after { Puppet::SSL::Host.ca_location = :none system("rm -rf #{@dir}") Puppet.settings.clear Puppet::Util::Cacher.expire } it "should be considered a CA host if its name is equal to 'ca'" do Puppet::SSL::Host.new(Puppet::SSL::CA_NAME).should be_ca end describe "when managing its key" do it "should be able to generate and save a key" do @host.generate_key end it "should save the key such that the Indirector can find it" do @host.generate_key Puppet::SSL::Key.indirection.find(@host.name).content.to_s.should == @host.key.to_s end it "should save the private key into the :privatekeydir" do @host.generate_key File.read(File.join(Puppet.settings[:privatekeydir], "luke.madstop.com.pem")).should == @host.key.to_s end end describe "when managing its certificate request" do it "should be able to generate and save a certificate request" do @host.generate_certificate_request end it "should save the certificate request such that the Indirector can find it" do @host.generate_certificate_request Puppet::SSL::CertificateRequest.indirection.find(@host.name).content.to_s.should == @host.certificate_request.to_s end it "should save the private certificate request into the :privatekeydir" do @host.generate_certificate_request File.read(File.join(Puppet.settings[:requestdir], "luke.madstop.com.pem")).should == @host.certificate_request.to_s end end describe "when the CA host" do it "should never store its key in the :privatekeydir" do Puppet.settings.use(:main, :ssl, :ca) @ca = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name) @ca.generate_key FileTest.should_not be_exist(File.join(Puppet[:privatekeydir], "ca.pem")) end end it "should pass the verification of its own SSL store" do @host.generate @ca = Puppet::SSL::CertificateAuthority.new @ca.sign(@host.name) @host.ssl_store.verify(@host.certificate.content).should be_true end end diff --git a/spec/integration/transaction/report_spec.rb b/spec/integration/transaction/report_spec.rb index c38f31081..183d93f76 100755 --- a/spec/integration/transaction/report_spec.rb +++ b/spec/integration/transaction/report_spec.rb @@ -1,29 +1,29 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-8. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' describe Puppet::Transaction::Report do describe "when using the indirector" do after do Puppet::Util::Cacher.expire Puppet.settings.stubs(:use) end it "should be able to delegate to the :processor terminus" do Puppet::Transaction::Report.indirection.stubs(:terminus_class).returns :processor terminus = Puppet::Transaction::Report.indirection.terminus(:processor) Facter.stubs(:value).returns "host.domain.com" report = Puppet::Transaction::Report.new("apply") terminus.expects(:process).with(report) Puppet::Transaction::Report.indirection.save(report) end end end diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb index e608faa27..41a1ebac3 100755 --- a/spec/integration/transaction_spec.rb +++ b/spec/integration/transaction_spec.rb @@ -1,285 +1,284 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' require 'puppet/transaction' require 'puppet_spec/files' describe Puppet::Transaction do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end def mk_catalog(*resources) catalog = Puppet::Resource::Catalog.new(Puppet::Node.new("mynode")) resources.each { |res| catalog.add_resource res } catalog end it "should not apply generated resources if the parent resource fails" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false catalog.add_resource resource child_resource = Puppet::Type.type(:file).new :path => "/foo/bar/baz", :backup => false resource.expects(:eval_generate).returns([child_resource]) transaction = Puppet::Transaction.new(catalog) resource.expects(:retrieve).raises "this is a failure" resource.stubs(:err) child_resource.expects(:retrieve).never transaction.evaluate end it "should not apply virtual resources" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false resource.virtual = true catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) resource.expects(:evaluate).never transaction.evaluate end it "should apply exported resources" do catalog = Puppet::Resource::Catalog.new path = tmpfile("exported_files") resource = Puppet::Type.type(:file).new :path => path, :backup => false, :ensure => :file resource.exported = true catalog.add_resource resource catalog.apply FileTest.should be_exist(path) end it "should not apply virtual exported resources" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false resource.exported = true resource.virtual = true catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) resource.expects(:evaluate).never transaction.evaluate end # Verify that one component requiring another causes the contained # resources in the requiring component to get refreshed. it "should propagate events from a contained resource through its container to its dependent container's contained resources" do transaction = nil file = Puppet::Type.type(:file).new :path => tmpfile("event_propagation"), :ensure => :present execfile = File.join(tmpdir("exec_event"), "exectestingness2") exec = Puppet::Type.type(:exec).new :command => "touch #{execfile}", :path => ENV['PATH'] catalog = mk_catalog(file) fcomp = Puppet::Type.type(:component).new(:name => "Foo[file]") catalog.add_resource fcomp catalog.add_edge(fcomp, file) ecomp = Puppet::Type.type(:component).new(:name => "Foo[exec]") catalog.add_resource ecomp catalog.add_resource exec catalog.add_edge(ecomp, exec) ecomp[:subscribe] = Puppet::Resource.new(:foo, "file") exec[:refreshonly] = true exec.expects(:refresh) catalog.apply end # Make sure that multiple subscriptions get triggered. it "should propagate events to all dependent resources" do path = tmpfile("path") file1 = tmpfile("file1") file2 = tmpfile("file2") file = Puppet::Type.type(:file).new( :path => path, :ensure => "file" ) exec1 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch #{file1}", :refreshonly => true, :subscribe => Puppet::Resource.new(:file, path) ) exec2 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch #{file2}", :refreshonly => true, :subscribe => Puppet::Resource.new(:file, path) ) catalog = mk_catalog(file, exec1, exec2) catalog.apply FileTest.should be_exist(file1) FileTest.should be_exist(file2) end it "should not let one failed refresh result in other refreshes failing" do path = tmpfile("path") newfile = tmpfile("file") file = Puppet::Type.type(:file).new( :path => path, :ensure => "file" ) exec1 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch /this/cannot/possibly/exist", :logoutput => true, :refreshonly => true, :subscribe => file, :title => "one" ) exec2 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch #{newfile}", :logoutput => true, :refreshonly => true, :subscribe => [file, exec1], :title => "two" ) exec1.stubs(:err) catalog = mk_catalog(file, exec1, exec2) catalog.apply FileTest.should be_exists(newfile) end it "should still trigger skipped resources" do catalog = mk_catalog catalog.add_resource(*Puppet::Type.type(:schedule).mkdefaultschedules) Puppet[:ignoreschedules] = false file = Puppet::Type.type(:file).new( :name => tmpfile("file"), :ensure => "file", :backup => false ) fname = tmpfile("exec") exec = Puppet::Type.type(:exec).new( :name => "touch #{fname}", :path => "/usr/bin:/bin", :schedule => "monthly", :subscribe => Puppet::Resource.new("file", file.name) ) catalog.add_resource(file, exec) # Run it once catalog.apply FileTest.should be_exists(fname) # Now remove it, so it can get created again File.unlink(fname) file[:content] = "some content" catalog.apply FileTest.should be_exists(fname) # Now remove it, so it can get created again File.unlink(fname) # And tag our exec exec.tag("testrun") # And our file, so it runs file.tag("norun") Puppet[:tags] = "norun" file[:content] = "totally different content" catalog.apply FileTest.should be_exists(fname) end it "should not attempt to evaluate resources with failed dependencies" do exec = Puppet::Type.type(:exec).new( :command => "/bin/mkdir /this/path/cannot/possibly/exit", :title => "mkdir" ) file1 = Puppet::Type.type(:file).new( :title => "file1", :path => tmpfile("file1"), :require => exec, :ensure => :file ) file2 = Puppet::Type.type(:file).new( :title => "file2", :path => tmpfile("file2"), :require => file1, :ensure => :file ) catalog = mk_catalog(exec, file1, file2) catalog.apply FileTest.should_not be_exists(file1[:path]) FileTest.should_not be_exists(file2[:path]) end it "should not trigger subscribing resources on failure" do file1 = tmpfile("file1") file2 = tmpfile("file2") create_file1 = Puppet::Type.type(:exec).new( :command => "/usr/bin/touch #{file1}" ) exec = Puppet::Type.type(:exec).new( :command => "/bin/mkdir /this/path/cannot/possibly/exit", :title => "mkdir", :notify => create_file1 ) create_file2 = Puppet::Type.type(:exec).new( :command => "/usr/bin/touch #{file2}", :subscribe => exec ) catalog = mk_catalog(exec, create_file1, create_file2) catalog.apply FileTest.should_not be_exists(file1) FileTest.should_not be_exists(file2) end # #801 -- resources only checked in noop should be rescheduled immediately. it "should immediately reschedule noop resources" do Puppet::Type.type(:schedule).mkdefaultschedules resource = Puppet::Type.type(:notify).new(:name => "mymessage", :noop => true) catalog = Puppet::Resource::Catalog.new catalog.add_resource resource trans = catalog.apply trans.resource_harness.should be_scheduled(trans.resource_status(resource), resource) end end diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index 513b96e41..4bed8c6c1 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1,512 +1,511 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' describe Puppet::Type.type(:file) do include PuppetSpec::Files before do # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end it "should not attempt to manage files that do not exist if no means of creating the file is specified" do file = Puppet::Type.type(:file).new :path => "/my/file", :mode => "755" catalog = Puppet::Resource::Catalog.new catalog.add_resource file file.parameter(:mode).expects(:retrieve).never transaction = Puppet::Transaction.new(catalog) transaction.resource_harness.evaluate(file).should_not be_failed end describe "when writing files" do it "should backup files to a filebucket when one is configured" do bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo" catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.add_resource bucket File.open(file[:path], "w") { |f| f.puts "bar" } md5 = Digest::MD5.hexdigest(File.read(file[:path])) catalog.apply bucket.bucket.getfile(md5).should == "bar\n" end it "should backup files in the local directory when a backup string is provided" do file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => ".bak", :content => "foo" catalog = Puppet::Resource::Catalog.new catalog.add_resource file File.open(file[:path], "w") { |f| f.puts "bar" } catalog.apply backup = file[:path] + ".bak" FileTest.should be_exist(backup) File.read(backup).should == "bar\n" end it "should fail if no backup can be performed" do dir = tmpfile("backups") Dir.mkdir(dir) path = File.join(dir, "testfile") file = Puppet::Type.type(:file).new :path => path, :backup => ".bak", :content => "foo" catalog = Puppet::Resource::Catalog.new catalog.add_resource file File.open(file[:path], "w") { |f| f.puts "bar" } # Create a directory where the backup should be so that writing to it fails Dir.mkdir(File.join(dir, "testfile.bak")) Puppet::Util::Log.stubs(:newmessage) catalog.apply File.read(file[:path]).should == "bar\n" end it "should not backup symlinks" do link = tmpfile("link") dest1 = tmpfile("dest1") dest2 = tmpfile("dest2") bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = Puppet::Type.type(:file).new :path => link, :target => dest2, :ensure => :link, :backup => "mybucket" catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.add_resource bucket File.open(dest1, "w") { |f| f.puts "whatever" } File.symlink(dest1, link) md5 = Digest::MD5.hexdigest(File.read(file[:path])) catalog.apply File.readlink(link).should == dest2 Find.find(bucket[:path]) { |f| File.file?(f) }.should be_nil end it "should backup directories to the local filesystem by copying the whole directory" do file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => ".bak", :content => "foo", :force => true catalog = Puppet::Resource::Catalog.new catalog.add_resource file Dir.mkdir(file[:path]) otherfile = File.join(file[:path], "foo") File.open(otherfile, "w") { |f| f.print "yay" } catalog.apply backup = file[:path] + ".bak" FileTest.should be_directory(backup) File.read(File.join(backup, "foo")).should == "yay" end it "should backup directories to filebuckets by backing up each file separately" do bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo", :force => true catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.add_resource bucket Dir.mkdir(file[:path]) foofile = File.join(file[:path], "foo") barfile = File.join(file[:path], "bar") File.open(foofile, "w") { |f| f.print "fooyay" } File.open(barfile, "w") { |f| f.print "baryay" } foomd5 = Digest::MD5.hexdigest(File.read(foofile)) barmd5 = Digest::MD5.hexdigest(File.read(barfile)) catalog.apply bucket.bucket.getfile(foomd5).should == "fooyay" bucket.bucket.getfile(barmd5).should == "baryay" end it "should propagate failures encountered when renaming the temporary file" do file = Puppet::Type.type(:file).new :path => tmpfile("fail_rename"), :content => "foo" file.stubs(:remove_existing) # because it tries to make a backup catalog = Puppet::Resource::Catalog.new catalog.add_resource file File.open(file[:path], "w") { |f| f.print "bar" } File.expects(:rename).raises ArgumentError lambda { file.write(:content) }.should raise_error(Puppet::Error) File.read(file[:path]).should == "bar" end end describe "when recursing" do def build_path(dir) Dir.mkdir(dir) File.chmod(0750, dir) @dirs = [dir] @files = [] %w{one two}.each do |subdir| fdir = File.join(dir, subdir) Dir.mkdir(fdir) File.chmod(0750, fdir) @dirs << fdir %w{three}.each do |file| ffile = File.join(fdir, file) @files << ffile File.open(ffile, "w") { |f| f.puts "test #{file}" } File.chmod(0640, ffile) end end end it "should be able to recurse over a nonexistent file" do @path = tmpfile("file_integration_tests") @file = Puppet::Type::File.new( :name => @path, :mode => 0644, :recurse => true, :backup => false ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file lambda { @file.eval_generate }.should_not raise_error end it "should be able to recursively set properties on existing files" do @path = tmpfile("file_integration_tests") build_path(@path) @file = Puppet::Type::File.new( :name => @path, :mode => 0644, :recurse => true, :backup => false ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @dirs.each do |path| (File.stat(path).mode & 007777).should == 0755 end @files.each do |path| (File.stat(path).mode & 007777).should == 0644 end end it "should be able to recursively make links to other files" do source = tmpfile("file_link_integration_source") build_path(source) dest = tmpfile("file_link_integration_dest") @file = Puppet::Type::File.new(:name => dest, :target => source, :recurse => true, :ensure => :link, :backup => false) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @dirs.each do |path| link_path = path.sub(source, dest) File.lstat(link_path).should be_directory end @files.each do |path| link_path = path.sub(source, dest) File.lstat(link_path).ftype.should == "link" end end it "should be able to recursively copy files" do source = tmpfile("file_source_integration_source") build_path(source) dest = tmpfile("file_source_integration_dest") @file = Puppet::Type::File.new(:name => dest, :source => source, :recurse => true, :backup => false) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @dirs.each do |path| newpath = path.sub(source, dest) File.lstat(newpath).should be_directory end @files.each do |path| newpath = path.sub(source, dest) File.lstat(newpath).ftype.should == "file" end end it "should not recursively manage files managed by a more specific explicit file" do dir = tmpfile("recursion_vs_explicit_1") subdir = File.join(dir, "subdir") file = File.join(subdir, "file") FileUtils.mkdir_p(subdir) File.open(file, "w") { |f| f.puts "" } base = Puppet::Type::File.new(:name => dir, :recurse => true, :backup => false, :mode => "755") sub = Puppet::Type::File.new(:name => subdir, :recurse => true, :backup => false, :mode => "644") @catalog = Puppet::Resource::Catalog.new @catalog.add_resource base @catalog.add_resource sub @catalog.apply (File.stat(file).mode & 007777).should == 0644 end it "should recursively manage files even if there is an explicit file whose name is a prefix of the managed file" do dir = tmpfile("recursion_vs_explicit_2") managed = File.join(dir, "file") generated = File.join(dir, "file_with_a_name_starting_with_the_word_file") FileUtils.mkdir_p(dir) File.open(managed, "w") { |f| f.puts "" } File.open(generated, "w") { |f| f.puts "" } @catalog = Puppet::Resource::Catalog.new @catalog.add_resource Puppet::Type::File.new(:name => dir, :recurse => true, :backup => false, :mode => "755") @catalog.add_resource Puppet::Type::File.new(:name => managed, :recurse => true, :backup => false, :mode => "644") @catalog.apply (File.stat(generated).mode & 007777).should == 0755 end end describe "when generating resources" do before do @source = tmpfile("generating_in_catalog_source") @dest = tmpfile("generating_in_catalog_dest") Dir.mkdir(@source) s1 = File.join(@source, "one") s2 = File.join(@source, "two") File.open(s1, "w") { |f| f.puts "uno" } File.open(s2, "w") { |f| f.puts "dos" } @file = Puppet::Type::File.new(:name => @dest, :source => @source, :recurse => true, :backup => false) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file end it "should add each generated resource to the catalog" do @catalog.apply do |trans| @catalog.resource(:file, File.join(@dest, "one")).should be_instance_of(@file.class) @catalog.resource(:file, File.join(@dest, "two")).should be_instance_of(@file.class) end end it "should have an edge to each resource in the relationship graph" do @catalog.apply do |trans| one = @catalog.resource(:file, File.join(@dest, "one")) @catalog.relationship_graph.edge?(@file, one).should be two = @catalog.resource(:file, File.join(@dest, "two")) @catalog.relationship_graph.edge?(@file, two).should be end end end describe "when copying files" do # Ticket #285. it "should be able to copy files with pound signs in their names" do source = tmpfile("filewith#signs") dest = tmpfile("destwith#signs") File.open(source, "w") { |f| f.print "foo" } file = Puppet::Type::File.new(:name => dest, :source => source) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "foo" end it "should be able to copy files with spaces in their names" do source = tmpfile("filewith spaces") dest = tmpfile("destwith spaces") File.open(source, "w") { |f| f.print "foo" } File.chmod(0755, source) file = Puppet::Type::File.new(:path => dest, :source => source) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "foo" (File.stat(dest).mode & 007777).should == 0755 end it "should be able to copy individual files even if recurse has been specified" do source = tmpfile("source") dest = tmpfile("dest") File.open(source, "w") { |f| f.print "foo" } file = Puppet::Type::File.new(:name => dest, :source => source, :recurse => true) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "foo" end end it "should be able to create files when 'content' is specified but 'ensure' is not" do dest = tmpfile("files_with_content") file = Puppet::Type.type(:file).new( :name => dest, :content => "this is some content, yo" ) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "this is some content, yo" end it "should create files with content if both 'content' and 'ensure' are set" do dest = tmpfile("files_with_content") file = Puppet::Type.type(:file).new( :name => dest, :ensure => "file", :content => "this is some content, yo" ) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "this is some content, yo" end it "should delete files with sources but that are set for deletion" do dest = tmpfile("dest_source_with_ensure") source = tmpfile("source_source_with_ensure") File.open(source, "w") { |f| f.puts "yay" } File.open(dest, "w") { |f| f.puts "boo" } file = Puppet::Type.type(:file).new( :name => dest, :ensure => :absent, :source => source, :backup => false ) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.should_not be_exist(dest) end describe "when purging files" do before do @sourcedir = tmpfile("purge_source") @destdir = tmpfile("purge_dest") Dir.mkdir(@sourcedir) Dir.mkdir(@destdir) @sourcefile = File.join(@sourcedir, "sourcefile") @copiedfile = File.join(@destdir, "sourcefile") @localfile = File.join(@destdir, "localfile") @purgee = File.join(@destdir, "to_be_purged") File.open(@localfile, "w") { |f| f.puts "rahtest" } File.open(@sourcefile, "w") { |f| f.puts "funtest" } # this file should get removed File.open(@purgee, "w") { |f| f.puts "footest" } @lfobj = Puppet::Type.newfile( :title => "localfile", :path => @localfile, :content => "rahtest\n", :ensure => :file, :backup => false ) @destobj = Puppet::Type.newfile( :title => "destdir", :path => @destdir, :source => @sourcedir, :backup => false, :purge => true, :recurse => true ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @lfobj, @destobj end it "should still copy remote files" do @catalog.apply FileTest.should be_exist(@copiedfile) end it "should not purge managed, local files" do @catalog.apply FileTest.should be_exist(@localfile) end it "should purge files that are neither remote nor otherwise managed" do @catalog.apply FileTest.should_not be_exist(@purgee) end end end diff --git a/spec/integration/type/package_spec.rb b/spec/integration/type/package_spec.rb index 8e852f261..7d03cb9c0 100755 --- a/spec/integration/type/package_spec.rb +++ b/spec/integration/type/package_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:package), "when choosing a default package provider" do before do # the default provider is cached. Puppet::Type.type(:package).defaultprovider = nil end def provider_name(os) {"Ubuntu" => :apt, "Debian" => :apt, "Darwin" => :pkgdmg, "RedHat" => :up2date, "Fedora" => :yum, "FreeBSD" => :ports, "OpenBSD" => :openbsd, "Solaris" => :sun}[os] end it "should have a default provider" do Puppet::Type.type(:package).defaultprovider.should_not be_nil end it "should choose the correct provider each platform" do unless default_provider = provider_name(Facter.value(:operatingsystem)) pending("No default provider specified in this test for #{Facter.value(:operatingsystem)}") end Puppet::Type.type(:package).defaultprovider.name.should == default_provider end end diff --git a/spec/integration/type/tidy_spec.rb b/spec/integration/type/tidy_spec.rb index 084ebac6d..675aaf4cd 100755 --- a/spec/integration/type/tidy_spec.rb +++ b/spec/integration/type/tidy_spec.rb @@ -1,32 +1,31 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' require 'puppet/file_bucket/dipper' describe Puppet::Type.type(:tidy) do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end # Testing #355. it "should be able to remove dead links" do dir = tmpfile("tidy_link_testing") link = File.join(dir, "link") target = tmpfile("no_such_file_tidy_link_testing") Dir.mkdir(dir) File.symlink(target, link) tidy = Puppet::Type.type(:tidy).new :path => dir, :recurse => true catalog = Puppet::Resource::Catalog.new catalog.add_resource(tidy) catalog.apply FileTest.should_not be_symlink(link) end end diff --git a/spec/integration/type_spec.rb b/spec/integration/type_spec.rb index 62f8fb7d2..4be01d558 100755 --- a/spec/integration/type_spec.rb +++ b/spec/integration/type_spec.rb @@ -1,22 +1,21 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/type' describe Puppet::Type do it "should not lose its provider list when it is reloaded" do type = Puppet::Type.newtype(:integration_test) do newparam(:name) {} end provider = type.provide(:myprovider) {} # reload it type = Puppet::Type.newtype(:integration_test) do newparam(:name) {} end type.provider(:myprovider).should equal(provider) end end diff --git a/spec/integration/util/autoload_spec.rb b/spec/integration/util/autoload_spec.rb index f9b913502..92fc6554c 100755 --- a/spec/integration/util/autoload_spec.rb +++ b/spec/integration/util/autoload_spec.rb @@ -1,114 +1,113 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/autoload' require 'fileutils' class AutoloadIntegrator @things = [] def self.newthing(name) @things << name end def self.thing?(name) @things.include? name end def self.clear @things.clear end end require 'puppet_spec/files' describe Puppet::Util::Autoload do include PuppetSpec::Files def with_file(name, *path) path = File.join(*path) # Now create a file to load File.open(path, "w") { |f| f.puts "\nAutoloadIntegrator.newthing(:#{name.to_s})\n" } yield File.delete(path) end def with_loader(name, path) dir = tmpfile(name + path) $LOAD_PATH << dir Dir.mkdir(dir) rbdir = File.join(dir, path.to_s) Dir.mkdir(rbdir) loader = Puppet::Util::Autoload.new(name, path) yield rbdir, loader Dir.rmdir(rbdir) Dir.rmdir(dir) $LOAD_PATH.pop AutoloadIntegrator.clear end it "should make instances available by the loading class" do loader = Puppet::Util::Autoload.new("foo", "bar") Puppet::Util::Autoload["foo"].should == loader end it "should not fail when asked to load a missing file" do Puppet::Util::Autoload.new("foo", "bar").load(:eh).should be_false end it "should load and return true when it successfully loads a file" do with_loader("foo", "bar") { |dir,loader| with_file(:mything, dir, "mything.rb") { loader.load(:mything).should be_true loader.should be_loaded(:mything) AutoloadIntegrator.should be_thing(:mything) } } end it "should consider a file loaded when asked for the name without an extension" do with_loader("foo", "bar") { |dir,loader| with_file(:noext, dir, "noext.rb") { loader.load(:noext) loader.should be_loaded(:noext) } } end it "should consider a file loaded when asked for the name with an extension" do with_loader("foo", "bar") { |dir,loader| with_file(:noext, dir, "withext.rb") { loader.load(:withext) loader.should be_loaded("withext.rb") } } end it "should register the fact that the instance is loaded with the Autoload base class" do with_loader("foo", "bar") { |dir,loader| with_file(:baseload, dir, "baseload.rb") { loader.load(:baseload) Puppet::Util::Autoload.should be_loaded("bar/withext.rb") } } end it "should be able to load files directly from modules" do modulepath = tmpfile("autoload_module_testing") libdir = File.join(modulepath, "mymod", "lib", "foo") FileUtils.mkdir_p(libdir) file = File.join(libdir, "plugin.rb") Puppet[:modulepath] = modulepath with_loader("foo", "foo") do |dir, loader| with_file(:plugin, file.split("/")) do loader.load(:plugin) loader.should be_loaded("plugin.rb") end end end end diff --git a/spec/integration/util/feature_spec.rb b/spec/integration/util/feature_spec.rb index b038cf308..c3c13764d 100755 --- a/spec/integration/util/feature_spec.rb +++ b/spec/integration/util/feature_spec.rb @@ -1,54 +1,53 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/feature' require 'puppet_spec/files' describe Puppet::Util::Feature do include PuppetSpec::Files it "should be able to load features from disk" do libdir = tmpfile("feature_lib") Dir.mkdir(libdir) $LOAD_PATH << libdir $features = Puppet::Util::Feature.new("feature_lib") Dir.mkdir(File.join(libdir, "feature_lib")) File.open(File.join(libdir, "feature_lib", "able_to_load.rb"), "w") do |f| f.puts "$features.add(:able_to_load) { true }" end $features.should be_able_to_load end # TODO: Make this a spec test or remove it. def test_dynamic_loading $features = @features cleanup { $features = nil } # Now create a feature and make sure it loads. FileUtils.mkdir_p(@path) nope = File.join(@path, "nope.rb") File.open(nope, "w") { |f| f.puts "$features.add(:nope, :libs => %w{nosuchlib})" } assert_nothing_raised("Failed to autoload features") do assert(! @features.nope?, "'nope' returned true") end # First make sure "yep?" returns false assert_nothing_raised("Missing feature threw an exception") do assert(! @features.notyep?, "'notyep' returned true before definition") end yep = File.join(@path, "yep.rb") File.open(yep, "w") { |f| f.puts "$features.add(:yep, :libs => %w{puppet})" } assert(@features.yep?, "false 'yep' is apparently cached or feature could not be loaded") end end diff --git a/spec/integration/util/file_locking_spec.rb b/spec/integration/util/file_locking_spec.rb index 1914fadf7..9e829df0e 100755 --- a/spec/integration/util/file_locking_spec.rb +++ b/spec/integration/util/file_locking_spec.rb @@ -1,57 +1,56 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/file_locking' describe Puppet::Util::FileLocking do before :each do @file = Tempfile.new("puppetspec") filepath = @file.path @file.close!() @file = filepath @data = {:a => :b, :c => "A string", :d => "another string", :e => %w{an array of strings}} File.open(@file, "w") { |f| f.puts YAML.dump(@data) } end it "should be able to keep file corruption from happening when there are multiple writers threads" do threads = [] sync = Sync.new 9.times { |a| threads << Thread.new { 9.times { |b| sync.synchronize(Sync::SH) { Puppet::Util::FileLocking.readlock(@file) { |f| YAML.load(f.read).should == @data } } sleep 0.01 sync.synchronize(Sync::EX) { Puppet::Util::FileLocking.writelock(@file) { |f| f.puts YAML.dump(@data) } } } } } threads.each { |th| th.join } end it "should be able to keep file corruption from happening when there are multiple writers processes" do unless Process.fork 50.times { |b| Puppet::Util::FileLocking.writelock(@file) { |f| f.puts YAML.dump(@data) } sleep 0.01 } Kernel.exit! end 50.times { |c| Puppet::Util::FileLocking.readlock(@file) { |f| YAML.load(f.read).should == @data } } end end diff --git a/spec/integration/util/rdoc/parser_spec.rb b/spec/integration/util/rdoc/parser_spec.rb index 9cb0c9e4a..ce316309b 100755 --- a/spec/integration/util/rdoc/parser_spec.rb +++ b/spec/integration/util/rdoc/parser_spec.rb @@ -1,59 +1,58 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' describe RDoc::Parser do require 'puppet_spec/files' include PuppetSpec::Files before :each do tmpdir = tmpfile('rdoc_parser_tmp') Dir.mkdir(tmpdir) @parsedfile = File.join(tmpdir, 'init.pp') File.open(@parsedfile, 'w') do |f| f.puts '# comment' f.puts 'class ::test {}' end @top_level = stub_everything 'toplevel', :file_relative_name => @parsedfile @module = stub_everything 'module' @puppet_top_level = RDoc::PuppetTopLevel.new(@top_level) RDoc::PuppetTopLevel.stubs(:new).returns(@puppet_top_level) @puppet_top_level.expects(:add_module).returns(@module) @parser = RDoc::Parser.new(@top_level, @parsedfile, nil, Options.instance, RDoc::Stats.new) end after(:each) do File.unlink(@parsedfile) end def get_test_class(toplevel) # toplevel -> main -> test toplevel.classes[0].classes[0] end it "should parse to RDoc data structure" do @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Resource::Type) } @parser.scan end it "should get a PuppetClass for the main class" do @parser.scan.classes[0].should be_a(RDoc::PuppetClass) end it "should produce a PuppetClass whose name is test" do get_test_class(@parser.scan).name.should == "test" end it "should produce a PuppetClass whose comment is 'comment'" do get_test_class(@parser.scan).comment.should == "comment\n" end end diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index d3b5ec594..b05c63107 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -1,30 +1,29 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' describe Puppet::Util::Settings do include PuppetSpec::Files def minimal_default_settings { :noop => {:default => false, :desc => "noop"} } end it "should be able to make needed directories" do settings = Puppet::Util::Settings.new settings.setdefaults :main, minimal_default_settings.update( :maindir => [tmpfile("main"), "a"] ) settings.use(:main) File.should be_directory(settings[:maindir]) end it "should make its directories with the corret modes" do settings = Puppet::Util::Settings.new settings.setdefaults :main, minimal_default_settings.update( :maindir => {:default => tmpfile("main"), :desc => "a", :mode => 0750} ) settings.use(:main) (File.stat(settings[:maindir]).mode & 007777).should == 0750 end end diff --git a/spec/lib/puppet/face/basetest.rb b/spec/lib/puppet/face/basetest.rb new file mode 100755 index 000000000..00616f74f --- /dev/null +++ b/spec/lib/puppet/face/basetest.rb @@ -0,0 +1 @@ +Puppet::Face.define(:basetest, '0.0.1') diff --git a/spec/lib/puppet/face/huzzah.rb b/spec/lib/puppet/face/huzzah.rb new file mode 100755 index 000000000..3428c6816 --- /dev/null +++ b/spec/lib/puppet/face/huzzah.rb @@ -0,0 +1,5 @@ +require 'puppet/face' +Puppet::Face.define(:huzzah, '2.0.1') do + summary "life is a thing for celebration" + action :bar do "is where beer comes from" end +end diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb old mode 100644 new mode 100755 index 38c51a572..30fb4fc42 --- a/spec/lib/puppet_spec/files.rb +++ b/spec/lib/puppet_spec/files.rb @@ -1,43 +1,53 @@ require 'fileutils' require 'tempfile' # A support module for testing files. module PuppetSpec::Files + # This code exists only to support tests that run as root, pretty much. + # Once they have finally been eliminated this can all go... --daniel 2011-04-08 + if Puppet.features.posix? then + def self.in_tmp(path) + path =~ /^\/tmp/ or path =~ /^\/var\/folders/ + end + elsif Puppet.features.microsoft_windows? + def self.in_tmp(path) + tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp")) + path =~ /^#{tempdir}/ + end + else + fail "Help! Can't find in_tmp for this platform" + end + def self.cleanup - if defined?($tmpfiles) - $tmpfiles.each do |file| - file = File.expand_path(file) - if Puppet.features.posix? and file !~ /^\/tmp/ and file !~ /^\/var\/folders/ - puts "Not deleting tmpfile #{file} outside of /tmp or /var/folders" - next - elsif Puppet.features.microsoft_windows? - tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp")) - if file !~ /^#{tempdir}/ - puts "Not deleting tmpfile #{file} outside of #{tempdir}" - next - end - end - if FileTest.exist?(file) - system("chmod -R 755 '#{file}'") - system("rm -rf '#{file}'") - end + $global_tempfiles ||= [] + while path = $global_tempfiles.pop do + fail "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path) + + begin + FileUtils.rm_r path, :secure => true + rescue Errno::ENOENT + # nothing to do end - $tmpfiles.clear end end def tmpfile(name) + # Generate a temporary file, just for the name... source = Tempfile.new(name) path = source.path source.close! - $tmpfiles ||= [] - $tmpfiles << path + + # ...record it for cleanup, + $global_tempfiles ||= [] + $global_tempfiles << File.expand_path(path) + + # ...and bam. path end def tmpdir(name) - file = tmpfile(name) - FileUtils.mkdir_p(file) - file + path = tmpfile(name) + FileUtils.mkdir_p(path) + path end end diff --git a/spec/lib/puppet_spec/fixtures.rb b/spec/lib/puppet_spec/fixtures.rb old mode 100644 new mode 100755 diff --git a/spec/lib/puppet_spec/verbose.rb b/spec/lib/puppet_spec/verbose.rb old mode 100644 new mode 100755 diff --git a/spec/monkey_patches/alias_should_to_must.rb b/spec/monkey_patches/alias_should_to_must.rb old mode 100644 new mode 100755 diff --git a/spec/monkey_patches/publicize_methods.rb b/spec/monkey_patches/publicize_methods.rb old mode 100644 new mode 100755 diff --git a/spec/shared_behaviours/all_parsedfile_providers.rb b/spec/shared_behaviours/all_parsedfile_providers.rb old mode 100644 new mode 100755 diff --git a/spec/shared_behaviours/file_server_terminus.rb b/spec/shared_behaviours/file_server_terminus.rb old mode 100644 new mode 100755 index 94a044d2e..f59169382 --- a/spec/shared_behaviours/file_server_terminus.rb +++ b/spec/shared_behaviours/file_server_terminus.rb @@ -1,45 +1,45 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. shared_examples_for "Puppet::Indirector::FileServerTerminus" do # This only works if the shared behaviour is included before # the 'before' block in the including context. before do Puppet::Util::Cacher.expire FileTest.stubs(:exists?).returns true FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true) @path = Tempfile.new("file_server_testing") path = @path.path @path.close! @path = path Dir.mkdir(@path) File.open(File.join(@path, "myfile"), "w") { |f| f.print "my content" } # Use a real mount, so the integration is a bit deeper. @mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") @mount1.path = @path @parser = stub 'parser', :changed? => false @parser.stubs(:parse).returns("one" => @mount1) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) # Stub out the modules terminus @modules = mock 'modules terminus' @request = Puppet::Indirector::Request.new(:indirection, :method, "puppet://myhost/one/myfile") end it "should use the file server configuration to find files" do @modules.stubs(:find).returns(nil) @terminus.indirection.stubs(:terminus).with(:modules).returns(@modules) path = File.join(@path, "myfile") @terminus.find(@request).should be_instance_of(@test_class) end end diff --git a/spec/shared_behaviours/file_serving.rb b/spec/shared_behaviours/file_serving.rb old mode 100644 new mode 100755 index 84173448a..3afab5b59 --- a/spec/shared_behaviours/file_serving.rb +++ b/spec/shared_behaviours/file_serving.rb @@ -1,71 +1,71 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. shared_examples_for "Puppet::FileServing::Files" do it "should use the rest terminus when the 'puppet' URI scheme is used and a host name is present" do uri = "puppet://myhost/fakemod/my/file" # It appears that the mocking somehow interferes with the caching subsystem. # This mock somehow causes another terminus to get generated. term = @indirection.terminus(:rest) @indirection.stubs(:terminus).with(:rest).returns term term.expects(:find) @indirection.find(uri) end it "should use the rest terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is not 'puppet' or 'apply'" do uri = "puppet:///fakemod/my/file" Puppet.settings.stubs(:value).returns "foo" Puppet.settings.stubs(:value).with(:name).returns("puppetd") Puppet.settings.stubs(:value).with(:modulepath).returns("") @indirection.terminus(:rest).expects(:find) @indirection.find(uri) end it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'puppet'" do uri = "puppet:///fakemod/my/file" Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => [])) Puppet.settings.stubs(:value).returns "" Puppet.settings.stubs(:value).with(:name).returns("puppet") Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever") @indirection.terminus(:file_server).expects(:find) @indirection.terminus(:file_server).stubs(:authorized?).returns(true) @indirection.find(uri) end it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'apply'" do uri = "puppet:///fakemod/my/file" Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => [])) Puppet.settings.stubs(:value).returns "" Puppet.settings.stubs(:value).with(:name).returns("apply") Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever") @indirection.terminus(:file_server).expects(:find) @indirection.terminus(:file_server).stubs(:authorized?).returns(true) @indirection.find(uri) end it "should use the file terminus when the 'file' URI scheme is used" do uri = "file:///fakemod/my/file" @indirection.terminus(:file).expects(:find) @indirection.find(uri) end it "should use the file terminus when a fully qualified path is provided" do uri = "/fakemod/my/file" @indirection.terminus(:file).expects(:find) @indirection.find(uri) end it "should use the configuration to test whether the request is allowed" do uri = "fakemod/my/file" mount = mock 'mount' config = stub 'configuration', :split_path => [mount, "eh"] @indirection.terminus(:file_server).stubs(:configuration).returns config @indirection.terminus(:file_server).expects(:find) mount.expects(:allowed?).returns(true) @indirection.find(uri, :node => "foo", :ip => "bar") end end diff --git a/spec/shared_behaviours/memory_terminus.rb b/spec/shared_behaviours/memory_terminus.rb old mode 100644 new mode 100755 diff --git a/spec/shared_behaviours/path_parameters.rb b/spec/shared_behaviours/path_parameters.rb old mode 100644 new mode 100755 diff --git a/spec/shared_behaviours/things_that_declare_options.rb b/spec/shared_behaviours/things_that_declare_options.rb new file mode 100755 index 000000000..ac358eacd --- /dev/null +++ b/spec/shared_behaviours/things_that_declare_options.rb @@ -0,0 +1,141 @@ +# encoding: UTF-8 +shared_examples_for "things that declare options" do + it "should support options without arguments" do + subject = add_options_to { option "--bar" } + subject.should be_option :bar + end + + it "should support options with an empty block" do + subject = add_options_to do + option "--foo" do + # this section deliberately left blank + end + end + subject.should be + subject.should be_option :foo + end + + { "--foo=" => :foo }.each do |input, option| + it "should accept #{name.inspect}" do + subject = add_options_to { option input } + subject.should be_option option + end + end + + it "should support option documentation" do + text = "Sturm und Drang (German pronunciation: [ˈʃtʊʁm ʊnt ˈdʁaŋ]) …" + + subject = add_options_to do + option "--foo" do + desc text + end + end + + subject.get_option(:foo).desc.should == text + end + + it "should list all the options" do + subject = add_options_to do + option "--foo" + option "--bar" + end + subject.options.should =~ [:foo, :bar] + end + + it "should detect conflicts in long options" do + expect { + add_options_to do + option "--foo" + option "--foo" + end + }.should raise_error ArgumentError, /Option foo conflicts with existing option foo/i + end + + it "should detect conflicts in short options" do + expect { + add_options_to do + option "-f" + option "-f" + end + }.should raise_error ArgumentError, /Option f conflicts with existing option f/ + end + + ["-f", "--foo"].each do |option| + ["", " FOO", "=FOO", " [FOO]", "=[FOO]"].each do |argument| + input = option + argument + it "should detect conflicts within a single option like #{input.inspect}" do + expect { + add_options_to do + option input, input + end + }.should raise_error ArgumentError, /duplicates existing alias/ + end + end + end + + + # Verify the range of interesting conflicts to check for ordering causing + # the behaviour to change, or anything exciting like that. + [ %w{--foo}, %w{-f}, %w{-f --foo}, %w{--baz -f}, + %w{-f --baz}, %w{-b --foo}, %w{--foo -b} + ].each do |conflict| + base = %w{--foo -f} + it "should detect conflicts between #{base.inspect} and #{conflict.inspect}" do + expect { + add_options_to do + option *base + option *conflict + end + }.should raise_error ArgumentError, /conflicts with existing option/ + end + end + + it "should fail if we are not consistent about taking an argument" do + expect { add_options_to do option "--foo=bar", "--bar" end }. + should raise_error ArgumentError, /inconsistent about taking an argument/ + end + + it "should accept optional arguments" do + subject = add_options_to do option "--foo=[baz]", "--bar=[baz]" end + [:foo, :bar].each do |name| + subject.should be_option name + end + end + + describe "#takes_argument?" do + it "should detect an argument being absent" do + subject = add_options_to do option "--foo" end + subject.get_option(:foo).should_not be_takes_argument + end + ["=FOO", " FOO", "=[FOO]", " [FOO]"].each do |input| + it "should detect an argument given #{input.inspect}" do + subject = add_options_to do option "--foo#{input}" end + subject.get_option(:foo).should be_takes_argument + end + end + end + + describe "#optional_argument?" do + it "should be false if no argument is present" do + option = add_options_to do option "--foo" end.get_option(:foo) + option.should_not be_takes_argument + option.should_not be_optional_argument + end + + ["=FOO", " FOO"].each do |input| + it "should be false if the argument is mandatory (like #{input.inspect})" do + option = add_options_to do option "--foo#{input}" end.get_option(:foo) + option.should be_takes_argument + option.should_not be_optional_argument + end + end + + ["=[FOO]", " [FOO]"].each do |input| + it "should be true if the argument is optional (like #{input.inspect})" do + option = add_options_to do option "--foo#{input}" end.get_option(:foo) + option.should be_takes_argument + option.should be_optional_argument + end + end + end +end diff --git a/spec/spec.opts b/spec/spec.opts index 91cd6427e..425f0edd3 100644 --- a/spec/spec.opts +++ b/spec/spec.opts @@ -1,6 +1,4 @@ --format s --colour ---loadby -mtime --backtrace diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb old mode 100644 new mode 100755 index 13470428e..1187c1caf --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,69 +1,74 @@ -unless defined?(SPEC_HELPER_IS_LOADED) -SPEC_HELPER_IS_LOADED = 1 - dir = File.expand_path(File.dirname(__FILE__)) - -$LOAD_PATH.unshift("#{dir}/") -$LOAD_PATH.unshift("#{dir}/lib") # a spec-specific test lib dir -$LOAD_PATH.unshift("#{dir}/../lib") +$LOAD_PATH.unshift File.join(dir, 'lib') # Don't want puppet getting the command line arguments for rake or autotest ARGV.clear require 'puppet' require 'mocha' gem 'rspec', '>=2.0.0' +require 'rspec/expectations' # So everyone else doesn't have to include this base constant. module PuppetSpec FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR) end require 'pathname' +require 'tmpdir' + require 'lib/puppet_spec/verbose' require 'lib/puppet_spec/files' require 'lib/puppet_spec/fixtures' require 'monkey_patches/alias_should_to_must' require 'monkey_patches/publicize_methods' Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| require behaviour.relative_path_from(Pathname.new(dir)) end RSpec.configure do |config| include PuppetSpec::Fixtures config.mock_with :mocha config.before :each do + GC.disable + # these globals are set by Application $puppet_application_mode = nil $puppet_application_name = nil Signal.stubs(:trap) # Set the confdir and vardir to gibberish so that tests # have to be correctly mocked. Puppet[:confdir] = "/dev/null" Puppet[:vardir] = "/dev/null" # Avoid opening ports to the outside world Puppet.settings[:bindaddress] = "127.0.0.1" @logs = [] Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs)) end config.after :each do Puppet.settings.clear Puppet::Node::Environment.clear Puppet::Util::Storage.clear Puppet::Util::ExecutionStub.reset PuppetSpec::Files.cleanup @logs.clear Puppet::Util::Log.close_all + + GC.enable end end +RSpec::Matchers.define :have_matching_element do |expected| + match do |actual| + actual.any? { |item| item =~ expected } + end end diff --git a/spec/unit/agent/locker_spec.rb b/spec/unit/agent/locker_spec.rb index e7c8929c7..341859e3b 100755 --- a/spec/unit/agent/locker_spec.rb +++ b/spec/unit/agent/locker_spec.rb @@ -1,100 +1,99 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/agent' require 'puppet/agent/locker' class LockerTester include Puppet::Agent::Locker end describe Puppet::Agent::Locker do before do @locker = LockerTester.new @locker.stubs(:lockfile_path).returns "/my/lock" end it "should use a Pidlock instance as its lockfile" do @locker.lockfile.should be_instance_of(Puppet::Util::Pidlock) end it "should use 'lockfile_path' to determine its lockfile path" do @locker.expects(:lockfile_path).returns "/my/lock" lock = Puppet::Util::Pidlock.new("/my/lock") Puppet::Util::Pidlock.expects(:new).with("/my/lock").returns lock @locker.lockfile end it "should reuse the same lock file each time" do @locker.lockfile.should equal(@locker.lockfile) end it "should use the lock file to anonymously lock the process when disabled" do @locker.lockfile.expects(:lock).with(:anonymous => true) @locker.disable end it "should use the lock file to anonymously unlock the process when enabled" do @locker.lockfile.expects(:unlock).with(:anonymous => true) @locker.enable end it "should have a method that yields when a lock is attained" do @locker.lockfile.expects(:lock).returns true yielded = false @locker.lock do yielded = true end yielded.should be_true end it "should return true when the lock method successfully locked" do @locker.lockfile.expects(:lock).returns true @locker.lock {}.should be_true end it "should return true when the lock method does not receive the lock" do @locker.lockfile.expects(:lock).returns false @locker.lock {}.should be_false end it "should not yield when the lock method does not receive the lock" do @locker.lockfile.expects(:lock).returns false yielded = false @locker.lock { yielded = true } yielded.should be_false end it "should not unlock when a lock was not received" do @locker.lockfile.expects(:lock).returns false @locker.lockfile.expects(:unlock).never @locker.lock {} end it "should unlock after yielding upon obtaining a lock" do @locker.lockfile.stubs(:lock).returns true @locker.lockfile.expects(:unlock) @locker.lock {} end it "should unlock after yielding upon obtaining a lock, even if the block throws an exception" do @locker.lockfile.stubs(:lock).returns true @locker.lockfile.expects(:unlock) lambda { @locker.lock { raise "foo" } }.should raise_error(RuntimeError) end it "should be considered running if the lockfile is locked" do @locker.lockfile.expects(:locked?).returns true @locker.should be_running end end diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb index a3a54bf7d..bfa44f61c 100755 --- a/spec/unit/agent_spec.rb +++ b/spec/unit/agent_spec.rb @@ -1,281 +1,281 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-12. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet/agent' class AgentTestClient def run # no-op end def stop # no-op end end def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Agent do before do @agent = Puppet::Agent.new(AgentTestClient) # So we don't actually try to hit the filesystem. @agent.stubs(:lock).yields # make Puppet::Application safe for stubbing; restore in an :after block; silence warnings for this. without_warnings { Puppet::Application = Class.new(Puppet::Application) } Puppet::Application.stubs(:clear?).returns(true) Puppet::Application.class_eval do class << self def controlled_run(&block) block.call end end end end after do # restore Puppet::Application from stub-safe subclass, and silence warnings without_warnings { Puppet::Application = Puppet::Application.superclass } end it "should set its client class at initialization" do Puppet::Agent.new("foo").client_class.should == "foo" end it "should include the Locker module" do Puppet::Agent.ancestors.should be_include(Puppet::Agent::Locker) end it "should create an instance of its client class and run it when asked to run" do client = mock 'client' AgentTestClient.expects(:new).returns client client.expects(:run) @agent.stubs(:running?).returns false @agent.run end it "should determine its lock file path by asking the client class" do AgentTestClient.expects(:lockfile_path).returns "/my/lock" @agent.lockfile_path.should == "/my/lock" end it "should be considered running if the lock file is locked" do lockfile = mock 'lockfile' @agent.expects(:lockfile).returns lockfile lockfile.expects(:locked?).returns true @agent.should be_running end describe "when being run" do before do @agent.stubs(:running?).returns false end it "should splay" do @agent.expects(:splay) @agent.stubs(:running?).returns false @agent.run end it "should do nothing if already running" do @agent.expects(:running?).returns true AgentTestClient.expects(:new).never @agent.run end it "should use Puppet::Application.controlled_run to manage process state behavior" do calls = sequence('calls') Puppet::Application.expects(:controlled_run).yields.in_sequence(calls) AgentTestClient.expects(:new).once.in_sequence(calls) @agent.run end it "should not fail if a client class instance cannot be created" do AgentTestClient.expects(:new).raises "eh" Puppet.expects(:err) @agent.run end it "should not fail if there is an exception while running its client" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).raises "eh" Puppet.expects(:err) @agent.run end it "should use a mutex to restrict multi-threading" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client mutex = mock 'mutex' @agent.expects(:sync).returns mutex mutex.expects(:synchronize) client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it @agent.run end it "should use a filesystem lock to restrict multiple processes running the agent" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client @agent.expects(:lock) client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it @agent.run end it "should make its client instance available while running" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).with { @agent.client.should equal(client); true } @agent.run end it "should run the client instance with any arguments passed to it" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).with("testargs") @agent.run("testargs") end end describe "when splaying" do before do Puppet.settings.stubs(:value).with(:splay).returns true Puppet.settings.stubs(:value).with(:splaylimit).returns "10" end it "should do nothing if splay is disabled" do Puppet.settings.expects(:value).returns false @agent.expects(:sleep).never @agent.splay end it "should do nothing if it has already splayed" do @agent.expects(:splayed?).returns true @agent.expects(:sleep).never @agent.splay end it "should log that it is splaying" do @agent.stubs :sleep Puppet.expects :info @agent.splay end it "should sleep for a random portion of the splaylimit plus 1" do Puppet.settings.expects(:value).with(:splaylimit).returns "50" @agent.expects(:rand).with(51).returns 10 @agent.expects(:sleep).with(10) @agent.splay end it "should mark that it has splayed" do @agent.stubs(:sleep) @agent.splay @agent.should be_splayed end end describe "when checking execution state" do describe 'with regular run status' do before :each do Puppet::Application.stubs(:restart_requested?).returns(false) Puppet::Application.stubs(:stop_requested?).returns(false) Puppet::Application.stubs(:interrupted?).returns(false) Puppet::Application.stubs(:clear?).returns(true) end it 'should be false for :stopping?' do @agent.stopping?.should be_false end it 'should be false for :needing_restart?' do @agent.needing_restart?.should be_false end end describe 'with a stop requested' do before :each do Puppet::Application.stubs(:clear?).returns(false) Puppet::Application.stubs(:restart_requested?).returns(false) Puppet::Application.stubs(:stop_requested?).returns(true) Puppet::Application.stubs(:interrupted?).returns(true) end it 'should be true for :stopping?' do @agent.stopping?.should be_true end it 'should be false for :needing_restart?' do @agent.needing_restart?.should be_false end end describe 'with a restart requested' do before :each do Puppet::Application.stubs(:clear?).returns(false) Puppet::Application.stubs(:restart_requested?).returns(true) Puppet::Application.stubs(:stop_requested?).returns(false) Puppet::Application.stubs(:interrupted?).returns(true) end it 'should be false for :stopping?' do @agent.stopping?.should be_false end it 'should be true for :needing_restart?' do @agent.needing_restart?.should be_true end end end describe "when starting" do before do @agent.stubs(:observe_signal) end it "should create a timer with the runinterval, a tolerance of 1, and :start? set to true" do Puppet.settings.expects(:value).with(:runinterval).returns 5 timer = stub 'timer', :sound_alarm => nil EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).returns timer @agent.stubs(:run) @agent.start end it "should run once immediately" do timer = mock 'timer' EventLoop::Timer.expects(:new).returns timer timer.expects(:sound_alarm) @agent.start end it "should run within the block passed to the timer" do timer = stub 'timer', :sound_alarm => nil EventLoop::Timer.expects(:new).returns(timer).yields @agent.expects(:run) @agent.start end end end diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index 804057868..03cf14429 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -1,593 +1,592 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/agent' require 'puppet/application/agent' require 'puppet/network/server' require 'puppet/daemon' require 'puppet/network/handler' describe Puppet::Application::Agent do before :each do @puppetd = Puppet::Application[:agent] @puppetd.stubs(:puts) @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet[:daemonize] = false @agent = stub_everything 'agent' Puppet::Agent.stubs(:new).returns(@agent) @puppetd.preinit Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) end it "should operate in agent run_mode" do @puppetd.class.run_mode.name.should == :agent end it "should ask Puppet::Application to parse Puppet configuration file" do @puppetd.should_parse_config?.should be_true end it "should declare a main command" do @puppetd.should respond_to(:main) end it "should declare a onetime command" do @puppetd.should respond_to(:onetime) end it "should declare a fingerprint command" do @puppetd.should respond_to(:fingerprint) end it "should declare a preinit block" do @puppetd.should respond_to(:preinit) end describe "in preinit" do it "should catch INT" do Signal.expects(:trap).with { |arg,block| arg == :INT } @puppetd.preinit end it "should init client to true" do @puppetd.preinit @puppetd.options[:client].should be_true end it "should init fqdn to nil" do @puppetd.preinit @puppetd.options[:fqdn].should be_nil end it "should init serve to []" do @puppetd.preinit @puppetd.options[:serve].should == [] end it "should use MD5 as default digest algorithm" do @puppetd.preinit @puppetd.options[:digest].should == :MD5 end it "should not fingerprint by default" do @puppetd.preinit @puppetd.options[:fingerprint].should be_false end end describe "when handling options" do before do @puppetd.command_line.stubs(:args).returns([]) end [:centrallogging, :disable, :enable, :debug, :fqdn, :test, :verbose, :digest].each do |option| it "should declare handle_#{option} method" do @puppetd.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @puppetd.options.expects(:[]=).with(option, 'arg') @puppetd.send("handle_#{option}".to_sym, 'arg') end end it "should set an existing handler on server" do Puppet::Network::Handler.stubs(:handler).with("handler").returns(true) @puppetd.handle_serve("handler") @puppetd.options[:serve].should == [ :handler ] end it "should set client to false with --no-client" do @puppetd.handle_no_client(nil) @puppetd.options[:client].should be_false end it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do Puppet[:onetime] = true Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(0) @puppetd.setup_host end it "should use supplied waitforcert when --onetime is specified" do Puppet[:onetime] = true @puppetd.handle_waitforcert(60) Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(60) @puppetd.setup_host end it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(120) @puppetd.setup_host end it "should set the log destination with --logdest" do @puppetd.options.stubs(:[]=).with { |opt,val| opt == :setdest } Puppet::Log.expects(:newdestination).with("console") @puppetd.handle_logdest("console") end it "should put the setdest options to true" do @puppetd.options.expects(:[]=).with(:setdest,true) @puppetd.handle_logdest("console") end it "should parse the log destination from the command line" do @puppetd.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @puppetd.parse_options end it "should store the waitforcert options with --waitforcert" do @puppetd.options.expects(:[]=).with(:waitforcert,42) @puppetd.handle_waitforcert("42") end it "should set args[:Port] with --port" do @puppetd.handle_port("42") @puppetd.args[:Port].should == "42" end end describe "during setup" do before :each do @puppetd.options.stubs(:[]) Puppet.stubs(:info) FileTest.stubs(:exists?).returns(true) Puppet[:libdir] = "/dev/null/lib" Puppet::SSL::Host.stubs(:ca_location=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Transaction::Report.indirection.stubs(:cache_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) @host = stub_everything 'host' Puppet::SSL::Host.stubs(:new).returns(@host) Puppet.stubs(:settraps) end describe "with --test" do before :each do #Puppet.settings.stubs(:handlearg) @puppetd.options.stubs(:[]=) end it "should call setup_test" do @puppetd.options.stubs(:[]).with(:test).returns(true) @puppetd.expects(:setup_test) @puppetd.setup end it "should set options[:verbose] to true" do @puppetd.options.expects(:[]=).with(:verbose,true) @puppetd.setup_test end it "should set options[:onetime] to true" do Puppet[:onetime] = false @puppetd.setup_test Puppet[:onetime].should == true end it "should set options[:detailed_exitcodes] to true" do @puppetd.options.expects(:[]=).with(:detailed_exitcodes,true) @puppetd.setup_test end end it "should call setup_logs" do @puppetd.expects(:setup_logs) @puppetd.setup end describe "when setting up logs" do before :each do Puppet::Util::Log.stubs(:newdestination) end it "should set log level to debug if --debug was passed" do @puppetd.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:level=).with(:debug) @puppetd.setup_logs end it "should set log level to info if --verbose was passed" do @puppetd.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:level=).with(:info) @puppetd.setup_logs end [:verbose, :debug].each do |level| it "should set console as the log destination with level #{level}" do @puppetd.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @puppetd.setup_logs end end it "should set syslog as the log destination if no --logdest" do @puppetd.options.stubs(:[]).with(:setdest).returns(false) Puppet::Util::Log.expects(:newdestination).with(:syslog) @puppetd.setup_logs end end it "should print puppet config if asked to in Puppet config" do @puppetd.stubs(:exit) Puppet[:configprint] = "pluginsync" Puppet.settings.expects(:print_configs) @puppetd.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet[:modulepath] = '/my/path' Puppet[:configprint] = "modulepath" Puppet::Util::Settings.any_instance.expects(:puts).with('/my/path') lambda { @puppetd.setup }.should raise_error(SystemExit) end it "should set a central log destination with --centrallogs" do @puppetd.options.stubs(:[]).with(:centrallogs).returns(true) Puppet[:server] = "puppet.reductivelabs.com" Puppet::Util::Log.stubs(:newdestination).with(:syslog) Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") @puppetd.setup end it "should use :main, :puppetd, and :ssl" do Puppet.settings.expects(:use).with(:main, :agent, :ssl) @puppetd.setup end it "should install a remote ca location" do Puppet::SSL::Host.expects(:ca_location=).with(:remote) @puppetd.setup end it "should install a none ca location in fingerprint mode" do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:none) @puppetd.setup end it "should tell the report handler to use REST" do Puppet::Transaction::Report.indirection.expects(:terminus_class=).with(:rest) @puppetd.setup end it "should tell the report handler to cache locally as yaml" do Puppet::Transaction::Report.indirection.expects(:cache_class=).with(:yaml) @puppetd.setup end it "should change the catalog_terminus setting to 'rest'" do Puppet[:catalog_terminus] = :foo @puppetd.setup Puppet[:catalog_terminus].should == :rest end it "should tell the catalog handler to use cache" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:yaml) @puppetd.setup end it "should change the facts_terminus setting to 'facter'" do Puppet[:facts_terminus] = :foo @puppetd.setup Puppet[:facts_terminus].should == :facter end it "should create an agent" do Puppet::Agent.stubs(:new).with(Puppet::Configurer) @puppetd.setup end [:enable, :disable].each do |action| it "should delegate to enable_disable_client if we #{action} the agent" do @puppetd.options.stubs(:[]).with(action).returns(true) @puppetd.expects(:enable_disable_client).with(@agent) @puppetd.setup end end describe "when enabling or disabling agent" do [:enable, :disable].each do |action| it "should call client.#{action}" do @puppetd.stubs(:exit) @puppetd.options.stubs(:[]).with(action).returns(true) @agent.expects(action) @puppetd.enable_disable_client(@agent) end end it "should finally exit" do lambda { @puppetd.enable_disable_client(@agent) }.should raise_error(SystemExit) end end it "should inform the daemon about our agent if :client is set to 'true'" do @puppetd.options.expects(:[]).with(:client).returns true @daemon.expects(:agent=).with(@agent) @puppetd.setup end it "should not inform the daemon about our agent if :client is set to 'false'" do @puppetd.options[:client] = false @daemon.expects(:agent=).never @puppetd.setup end it "should daemonize if needed" do Puppet[:daemonize] = true @daemon.expects(:daemonize) @puppetd.setup end it "should wait for a certificate" do @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) @host.expects(:wait_for_cert).with(123) @puppetd.setup end it "should not wait for a certificate in fingerprint mode" do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) @host.expects(:wait_for_cert).never @puppetd.setup end it "should setup listen if told to and not onetime" do Puppet[:listen] = true @puppetd.options.stubs(:[]).with(:onetime).returns(false) @puppetd.expects(:setup_listen) @puppetd.setup end describe "when setting up listen" do before :each do Puppet[:authconfig] = 'auth' FileTest.stubs(:exists?).with('auth').returns(true) File.stubs(:exist?).returns(true) @puppetd.options.stubs(:[]).with(:serve).returns([]) @puppetd.stubs(:exit) @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) end it "should exit if no authorization file" do Puppet.stubs(:err) FileTest.stubs(:exists?).with(Puppet[:authconfig]).returns(false) @puppetd.expects(:exit) @puppetd.setup_listen end it "should create a server to listen on at least the Runner handler" do Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Runner] } @puppetd.setup_listen end it "should create a server to listen for specific handlers" do @puppetd.options.stubs(:[]).with(:serve).returns([:handler]) Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:handler] } @puppetd.setup_listen end it "should use puppet default port" do Puppet[:puppetport] = 32768 Puppet::Network::Server.expects(:new).with { |args| args[:port] == 32768 } @puppetd.setup_listen end end end describe "when running" do before :each do @puppetd.agent = @agent @puppetd.daemon = @daemon @puppetd.options.stubs(:[]).with(:fingerprint).returns(false) end it "should dispatch to fingerprint if --fingerprint is used" do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) @puppetd.stubs(:fingerprint) @puppetd.run_command end it "should dispatch to onetime if --onetime is used" do @puppetd.options.stubs(:[]).with(:onetime).returns(true) @puppetd.stubs(:onetime) @puppetd.run_command end it "should dispatch to main if --onetime and --fingerprint are not used" do @puppetd.options.stubs(:[]).with(:onetime).returns(false) @puppetd.stubs(:main) @puppetd.run_command end describe "with --onetime" do before :each do @agent.stubs(:run).returns(:report) @puppetd.options.stubs(:[]).with(:client).returns(:client) @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(false) @puppetd.stubs(:exit).with(0) Puppet.stubs(:newservice) end it "should exit if no defined --client" do $stderr.stubs(:puts) @puppetd.options.stubs(:[]).with(:client).returns(nil) @puppetd.expects(:exit).with(43) @puppetd.onetime end it "should setup traps" do @daemon.expects(:set_signal_traps) @puppetd.onetime end it "should let the agent run" do @agent.expects(:run).returns(:report) @puppetd.onetime end it "should finish by exiting with 0 error code" do @puppetd.expects(:exit).with(0) @puppetd.onetime end describe "and --detailed-exitcodes" do before :each do @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(true) end it "should exit with report's computed exit status" do Puppet[:noop] = false report = stub 'report', :exit_status => 666 @agent.stubs(:run).returns(report) @puppetd.expects(:exit).with(666) @puppetd.onetime end it "should exit with the report's computer exit status, even if --noop is set." do Puppet[:noop] = true report = stub 'report', :exit_status => 666 @agent.stubs(:run).returns(report) @puppetd.expects(:exit).with(666) @puppetd.onetime end end end describe "with --fingerprint" do before :each do @cert = stub_everything 'cert' @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) @puppetd.options.stubs(:[]).with(:digest).returns(:MD5) @host = stub_everything 'host' @puppetd.stubs(:host).returns(@host) end it "should fingerprint the certificate if it exists" do @host.expects(:certificate).returns(@cert) @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" @puppetd.fingerprint end it "should fingerprint the certificate request if no certificate have been signed" do @host.expects(:certificate).returns(nil) @host.expects(:certificate_request).returns(@cert) @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" @puppetd.fingerprint end it "should display the fingerprint" do @host.stubs(:certificate).returns(@cert) @cert.stubs(:fingerprint).with(:MD5).returns("DIGEST") Puppet.expects(:notice).with("DIGEST") @puppetd.fingerprint end end describe "without --onetime and --fingerprint" do before :each do Puppet.stubs(:notice) @puppetd.options.stubs(:[]).with(:client) end it "should start our daemon" do @daemon.expects(:start) @puppetd.main end end end end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 0c6df2cf8..dca2a4156 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -1,434 +1,390 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/apply' require 'puppet/file_bucket/dipper' require 'puppet/configurer' describe Puppet::Application::Apply do before :each do @apply = Puppet::Application[:apply] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @apply.options.expects(:[]=).with(option, 'arg') @apply.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @apply.options.expects(:[]=).with(:code, 'arg') @apply.send("handle_execute".to_sym, 'arg') end it "should ask Puppet::Application to parse Puppet configuration file" do @apply.should_parse_config?.should be_true end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @apply.handle_logdest("console") end it "should put the logset options to true" do @apply.options.expects(:[]=).with(:logset,true) @apply.handle_logdest("console") end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) Puppet::Transaction::Report.indirection.stubs(:cache_class=) @apply.options.stubs(:[]).with(any_parameters) end it "should set show_diff on --noop" do Puppet.stubs(:[]=) Puppet.stubs(:[]).with(:config) Puppet.stubs(:[]).with(:noop).returns(true) Puppet.expects(:[]=).with(:show_diff, true) @apply.setup end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @apply.setup end it "should set INT trap" do Signal.expects(:trap).with(:INT) @apply.setup end it "should set log level to debug if --debug was passed" do @apply.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @apply.setup end it "should set log level to info if --verbose was passed" do @apply.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @apply.setup end it "should print puppet config if asked to in Puppet config" do @apply.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @apply.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @apply.setup }.should raise_error(SystemExit) end it "should tell the report handler to cache locally as yaml" do Puppet::Transaction::Report.indirection.expects(:cache_class=).with(:yaml) @apply.setup end end describe "when executing" do - it "should dispatch to parseonly if parseonly is set" do - @apply.stubs(:options).returns({}) - Puppet.stubs(:[]).with(:parseonly).returns(true) - - @apply.expects(:parseonly) - @apply.run_command - end - it "should dispatch to 'apply' if it was called with 'apply'" do @apply.options[:catalog] = "foo" @apply.expects(:apply) @apply.run_command end - it "should dispatch to main if parseonly is not set" do + it "should dispatch to main otherwise" do @apply.stubs(:options).returns({}) - Puppet.stubs(:[]).with(:parseonly).returns(false) @apply.expects(:main) @apply.run_command end - describe "the parseonly command" do - before :each do - @environment = Puppet::Node::Environment.new("env") - Puppet.stubs(:[]).with(:environment).returns(@environment) - Puppet.stubs(:[]).with(:manifest).returns("site.pp") - Puppet.stubs(:err) - @apply.stubs(:exit) - @apply.options.stubs(:[]).with(:code).returns "some code" - end - - it "should use the environment to parse the file" do - @environment.stubs(:perform_initial_import) - @apply.parseonly - end - - it "should exit with exit code 0 if no error" do - @apply.expects(:exit).with(0) - @apply.parseonly - end - - it "should exit with exit code 1 if error" do - @environment.stubs(:perform_initial_import).raises(Puppet::ParseError) - @apply.expects(:exit).with(1) - @apply.parseonly - end - - it "should exit with exit code 1 if error, even if --noop is set" do - Puppet[:noop] = true - @environment.stubs(:perform_initial_import).raises(Puppet::ParseError) - @apply.expects(:exit).with(1) - @apply.parseonly - end - end - describe "the main command" do before :each do Puppet.stubs(:[]) Puppet.settings.stubs(:use) Puppet.stubs(:[]).with(:prerun_command).returns "" Puppet.stubs(:[]).with(:postrun_command).returns "" Puppet.stubs(:[]).with(:trace).returns(true) @apply.options.stubs(:[]) @facts = stub_everything 'facts' Puppet::Node::Facts.indirection.stubs(:find).returns(@facts) @node = stub_everything 'node' Puppet::Node.indirection.stubs(:find).returns(@node) @catalog = stub_everything 'catalog' @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.indirection.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = stub_everything 'transaction' @catalog.stubs(:apply).returns(@transaction) @apply.stubs(:exit) Puppet::Util::Storage.stubs(:load) Puppet::Configurer.any_instance.stubs(:save_last_run_summary) # to prevent it from trying to write files end it "should set the code to run from --code" do @apply.options.stubs(:[]).with(:code).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @apply.main end it "should set the code to run from STDIN if no arguments" do @apply.command_line.stubs(:args).returns([]) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @apply.main end it "should set the manifest if a file is passed on command line and the file exists" do File.stubs(:exist?).with('site.pp').returns true @apply.command_line.stubs(:args).returns(['site.pp']) Puppet.expects(:[]=).with(:manifest,"site.pp") @apply.main end it "should raise an error if a file is passed on command line and the file does not exist" do File.stubs(:exist?).with('noexist.pp').returns false @apply.command_line.stubs(:args).returns(['noexist.pp']) lambda { @apply.main }.should raise_error(RuntimeError, 'Could not find file noexist.pp') end it "should set the manifest to the first file and warn other files will be skipped" do File.stubs(:exist?).with('starwarsIV').returns true File.expects(:exist?).with('starwarsI').never @apply.command_line.stubs(:args).returns(['starwarsIV', 'starwarsI', 'starwarsII']) Puppet.expects(:[]=).with(:manifest,"starwarsIV") Puppet.expects(:warning).with('Only one file can be applied per run. Skipping starwarsI, starwarsII') @apply.main end it "should collect the node facts" do Puppet::Node::Facts.indirection.expects(:find).returns(@facts) @apply.main end it "should raise an error if we can't find the node" do Puppet::Node::Facts.indirection.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should look for the node" do Puppet::Node.indirection.expects(:find).returns(@node) @apply.main end it "should raise an error if we can't find the node" do Puppet::Node.indirection.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should merge in our node the loaded facts" do @facts.stubs(:values).returns("values") @node.expects(:merge).with("values") @apply.main end it "should load custom classes if loadclasses" do @apply.options.stubs(:[]).with(:loadclasses).returns(true) Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") @node.expects(:classes=) @apply.main end it "should compile the catalog" do Puppet::Resource::Catalog.indirection.expects(:find).returns(@catalog) @apply.main end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) @apply.main end it "should finalize the catalog" do @catalog.expects(:finalize) @apply.main end it "should call the prerun and postrun commands on a Configurer instance" do Puppet::Configurer.any_instance.expects(:execute_prerun_command) Puppet::Configurer.any_instance.expects(:execute_postrun_command) @apply.main end it "should apply the catalog" do @catalog.expects(:apply).returns(stub_everything('transaction')) @apply.main end it "should save the last run summary" do Puppet.stubs(:[]).with(:noop).returns(false) report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.stubs(:new).returns(report) Puppet::Configurer.any_instance.expects(:save_last_run_summary).with(report) @apply.main end describe "with detailed_exitcodes" do it "should exit with report's computed exit status" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) @apply.expects(:exit).with(666) @apply.main end it "should exit with report's computed exit status, even if --noop is set" do Puppet.stubs(:[]).with(:noop).returns(true) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) @apply.expects(:exit).with(666) @apply.main end it "should always exit with 0 if option is disabled" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(false) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(0) @apply.main end it "should always exit with 0 if --noop" do Puppet.stubs(:[]).with(:noop).returns(true) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(0) @apply.main end end end describe "the 'apply' command" do it "should read the catalog in from disk if a file name is provided" do @apply.options[:catalog] = "/my/catalog.pson" File.expects(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should read the catalog in from stdin if '-' is provided" do @apply.options[:catalog] = "-" $stdin.expects(:read).returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should deserialize the catalog from the default format" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should fail helpfully if deserializing fails" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" lambda { @apply.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) @apply.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog catalog.expects(:to_ral).returns "mycatalog" configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:run).with(:catalog => "mycatalog") @apply.apply end end end end diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb index 4315bb8d8..a1b5eb19a 100755 --- a/spec/unit/application/cert_spec.rb +++ b/spec/unit/application/cert_spec.rb @@ -1,234 +1,222 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') - +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/cert' describe Puppet::Application::Cert do before :each do @cert_app = Puppet::Application[:cert] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end it "should operate in master run_mode" do @cert_app.class.run_mode.name.should equal(:master) end it "should ask Puppet::Application to parse Puppet configuration file" do @cert_app.should_parse_config?.should be_true end it "should declare a main command" do @cert_app.should respond_to(:main) end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject{ |m| m == :destroy }.each do |method| it "should declare option --#{method}" do @cert_app.should respond_to("handle_#{method}".to_sym) end end it "should set log level to info with the --verbose option" do Puppet::Log.expects(:level=).with(:info) @cert_app.handle_verbose(0) end it "should set log level to debug with the --debug option" do Puppet::Log.expects(:level=).with(:debug) @cert_app.handle_debug(0) end it "should set the fingerprint digest with the --digest option" do @cert_app.handle_digest(:digest) @cert_app.digest.should == :digest end it "should set cert_mode to :destroy for --clean" do @cert_app.handle_clean(0) @cert_app.subcommand.should == :destroy end it "should set all to true for --all" do @cert_app.handle_all(0) @cert_app.all.should be_true end it "should set signed to true for --signed" do @cert_app.handle_signed(0) @cert_app.signed.should be_true end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject { |m| m == :destroy }.each do |method| it "should set cert_mode to #{method} with option --#{method}" do @cert_app.send("handle_#{method}".to_sym, nil) @cert_app.subcommand.should == method end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::SSL::Host.stubs(:ca_location=) Puppet::SSL::CertificateAuthority.stubs(:new) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @cert_app.setup end it "should print puppet config if asked to in Puppet config" do @cert_app.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @cert_app.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @cert_app.setup }.should raise_error(SystemExit) end it "should set the CA location to 'only'" do Puppet::SSL::Host.expects(:ca_location=).with(:only) @cert_app.setup end it "should create a new certificate authority" do Puppet::SSL::CertificateAuthority.expects(:new) @cert_app.setup end it "should set the ca_location to :local if the cert_mode is generate" do @cert_app.subcommand = 'generate' Puppet::SSL::Host.expects(:ca_location=).with(:local) @cert_app.setup end it "should set the ca_location to :local if the cert_mode is destroy" do @cert_app.subcommand = 'destroy' Puppet::SSL::Host.expects(:ca_location=).with(:local) @cert_app.setup end it "should set the ca_location to :only if the cert_mode is print" do @cert_app.subcommand = 'print' Puppet::SSL::Host.expects(:ca_location=).with(:only) @cert_app.setup end end describe "when running" do before :each do @cert_app.all = false @ca = stub_everything 'ca' @cert_app.ca = @ca @cert_app.command_line.stubs(:args).returns([]) end it "should delegate to the CertificateAuthority" do @ca.expects(:apply) @cert_app.main end it "should delegate with :all if option --all was given" do @cert_app.handle_all(0) @ca.expects(:apply).with { |cert_mode,to| to[:to] == :all } @cert_app.main end it "should delegate to ca.apply with the hosts given on command line" do @cert_app.command_line.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |cert_mode,to| to[:to] == ["host"]} @cert_app.main end it "should send the currently set digest" do @cert_app.command_line.stubs(:args).returns(["host"]) @cert_app.handle_digest(:digest) @ca.expects(:apply).with { |cert_mode,to| to[:digest] == :digest} @cert_app.main end it "should revoke cert if cert_mode is clean" do @cert_app.subcommand = :destroy @cert_app.command_line.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |cert_mode,to| cert_mode == :revoke } @ca.expects(:apply).with { |cert_mode,to| cert_mode == :destroy } @cert_app.main end end describe "when identifying subcommands" do before :each do @cert_app.all = false @ca = stub_everything 'ca' @cert_app.ca = @ca end - it "should SystemExit after printing help message" do - # Make the help method silent for testing; this is a bit nasty, but we - # can't identify a cleaner method. Help welcome. --daniel 2011-02-22 - Puppet.features.stubs(:usage?).returns(false) - @cert_app.stubs(:puts) - - @cert_app.command_line.stubs(:args).returns([]) - expect { @cert_app.parse_options }.should raise_error SystemExit - end - %w{list revoke generate sign print verify fingerprint}.each do |cmd| short = cmd[0,1] [cmd, "--#{cmd}", "-#{short}"].each do |option| # In our command line '-v' was eaten by 'verbose', so we can't consume # it here; this is a special case from our otherwise standard # processing. --daniel 2011-02-22 next if option == "-v" it "should recognise '#{option}'" do args = [option, "fun.example.com"] @cert_app.command_line.stubs(:args).returns(args) @cert_app.parse_options @cert_app.subcommand.should == cmd.to_sym args.should == ["fun.example.com"] end end end %w{clean --clean -c}.each do |ugly| it "should recognise the '#{ugly}' option as destroy" do args = [ugly, "fun.example.com"] @cert_app.command_line.stubs(:args).returns(args) @cert_app.parse_options @cert_app.subcommand.should == :destroy args.should == ["fun.example.com"] end end end end diff --git a/spec/unit/application/certificate_spec.rb b/spec/unit/application/certificate_spec.rb new file mode 100755 index 000000000..27d6ac81b --- /dev/null +++ b/spec/unit/application/certificate_spec.rb @@ -0,0 +1,20 @@ +require 'puppet/application/certificate' + +describe Puppet::Application::Certificate do + it "should have a 'ca-location' option" do + # REVISIT: This is delegated from the face, and we will have a test there, + # so is this actually a valuable test? --daniel 2011-04-07 + subject.command_line.stubs(:args).returns %w{list} + subject.preinit + subject.parse_options + subject.should respond_to(:handle_ca_location) + end + + it "should accept the ca-location option" do + subject.command_line.stubs(:args).returns %w{--ca-location local list} + subject.preinit + subject.parse_options + subject.setup + subject.arguments.should == [{ :ca_location => "local" }] + end +end diff --git a/spec/unit/application/config_spec.rb b/spec/unit/application/config_spec.rb new file mode 100755 index 000000000..dc2fb5717 --- /dev/null +++ b/spec/unit/application/config_spec.rb @@ -0,0 +1,9 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/application/config' + +describe Puppet::Application::Config do + it "should be a subclass of Puppet::Application::FaceBase" do + Puppet::Application::Config.superclass.should equal(Puppet::Application::FaceBase) + end +end diff --git a/spec/unit/application/configurer_spec.rb b/spec/unit/application/configurer_spec.rb new file mode 100755 index 000000000..791a367ea --- /dev/null +++ b/spec/unit/application/configurer_spec.rb @@ -0,0 +1,32 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/application/configurer' +require 'puppet/indirector/catalog/rest' +require 'puppet/indirector/report/rest' +require 'tempfile' + +describe "Puppet::Application::Configurer" do + it "should retrieve and apply a catalog and submit a report" do + pending "REVISIT: 2.7 changes broke this, and we want the merge published" + + dirname = Dir.mktmpdir("puppetdir") + Puppet[:vardir] = dirname + Puppet[:confdir] = dirname + Puppet[:certname] = "foo" + @catalog = Puppet::Resource::Catalog.new + @file = Puppet::Resource.new(:file, File.join(dirname, "tmp_dir_resource"), :parameters => {:ensure => :present}) + @catalog.add_resource(@file) + + @report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.stubs(:new).returns(@report) + + Puppet::Resource::Catalog::Rest.any_instance.stubs(:find).returns(@catalog) + @report.expects(:save) + + Puppet::Util::Log.stubs(:newdestination) + + Puppet::Application::Configurer.new.run + + @report.status.should == "changed" + end +end diff --git a/spec/unit/application/describe_spec.rb b/spec/unit/application/describe_spec.rb index 47b98a17b..e79ac21b7 100755 --- a/spec/unit/application/describe_spec.rb +++ b/spec/unit/application/describe_spec.rb @@ -1,84 +1,83 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/describe' describe Puppet::Application::Describe do before :each do @describe = Puppet::Application[:describe] end it "should ask Puppet::Application to not parse Puppet configuration file" do @describe.should_parse_config?.should be_false end it "should declare a main command" do @describe.should respond_to(:main) end it "should declare a preinit block" do @describe.should respond_to(:preinit) end [:providers,:list,:meta].each do |option| it "should declare handle_#{option} method" do @describe.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @describe.options.expects(:[]=).with("#{option}".to_sym, 'arg') @describe.send("handle_#{option}".to_sym, 'arg') end end describe "in preinit" do it "should set options[:parameteers] to true" do @describe.preinit @describe.options[:parameters].should be_true end end describe "when handling parameters" do it "should set options[:parameters] to false" do @describe.handle_short(nil) @describe.options[:parameters].should be_false end end describe "during setup" do it "should collect arguments in options[:types]" do @describe.command_line.stubs(:args).returns(['1','2']) @describe.setup @describe.options[:types].should == ['1','2'] end end describe "when running" do before :each do @typedoc = stub 'type_doc' TypeDoc.stubs(:new).returns(@typedoc) end it "should call list_types if options list is set" do @describe.options[:list] = true @typedoc.expects(:list_types) @describe.run_command end it "should call format_type for each given types" do @describe.options[:list] = false @describe.options[:types] = ['type'] @typedoc.expects(:format_type).with('type', @describe.options) @describe.run_command end end end diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb index f432184d3..66a833b9d 100755 --- a/spec/unit/application/doc_spec.rb +++ b/spec/unit/application/doc_spec.rb @@ -1,349 +1,348 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/doc' require 'puppet/util/reference' require 'puppet/util/rdoc' describe Puppet::Application::Doc do before :each do @doc = Puppet::Application[:doc] @doc.stubs(:puts) @doc.preinit Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end it "should ask Puppet::Application to not parse Puppet configuration file" do @doc.should_parse_config?.should be_false end it "should declare a other command" do @doc.should respond_to(:other) end it "should declare a rdoc command" do @doc.should respond_to(:rdoc) end it "should declare a fallback for unknown options" do @doc.should respond_to(:handle_unknown) end it "should declare a preinit block" do @doc.should respond_to(:preinit) end describe "in preinit" do it "should set references to []" do @doc.preinit @doc.options[:references].should == [] end it "should init mode to text" do @doc.preinit @doc.options[:mode].should == :text end it "should init format to to_markdown" do @doc.preinit @doc.options[:format].should == :to_markdown end end describe "when handling options" do [:all, :outputdir, :verbose, :debug, :charset].each do |option| it "should declare handle_#{option} method" do @doc.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @doc.options.expects(:[]=).with(option, 'arg') @doc.send("handle_#{option}".to_sym, 'arg') end end it "should store the format if valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(true) @doc.options.expects(:[]=).with(:format, 'to_format') @doc.handle_format('format') end it "should raise an error if the format is not valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(false) lambda { @doc.handle_format('format') } end it "should store the mode if valid" do Puppet::Util::Reference.stubs(:modes).returns(stub('mode', :include? => true)) @doc.options.expects(:[]=).with(:mode, :mode) @doc.handle_mode('mode') end it "should store the mode if :rdoc" do Puppet::Util::Reference.modes.stubs(:include?).with('rdoc').returns(false) @doc.options.expects(:[]=).with(:mode, :rdoc) @doc.handle_mode('rdoc') end it "should raise an error if the mode is not valid" do Puppet::Util::Reference.modes.stubs(:include?).with('unknown').returns(false) lambda { @doc.handle_mode('unknown') } end it "should list all references on list and exit" do reference = stubs 'reference' ref = stubs 'ref' Puppet::Util::Reference.stubs(:references).returns([reference]) Puppet::Util::Reference.expects(:reference).with(reference).returns(ref) ref.expects(:doc) @doc.expects(:exit) @doc.handle_list(nil) end it "should add reference to references list with --reference" do @doc.options[:references] = [:ref1] @doc.handle_reference('ref2') @doc.options[:references].should == [:ref1,:ref2] end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) @doc.command_line.stubs(:args).returns([]) end it "should default to rdoc mode if there are command line arguments" do @doc.command_line.stubs(:args).returns(["1"]) @doc.stubs(:setup_rdoc) @doc.options.expects(:[]=).with(:mode,:rdoc) @doc.setup end it "should call setup_rdoc in rdoc mode" do @doc.options.stubs(:[]).with(:mode).returns(:rdoc) @doc.expects(:setup_rdoc) @doc.setup end it "should call setup_reference if not rdoc" do @doc.options.stubs(:[]).with(:mode).returns(:test) @doc.expects(:setup_reference) @doc.setup end describe "in non-rdoc mode" do it "should get all non-dynamic reference if --all" do @doc.options.stubs(:[]).with(:all).returns(true) @doc.options.stubs(:[]).with(:references).returns([]) static = stub 'static', :dynamic? => false dynamic = stub 'dynamic', :dynamic? => true Puppet::Util::Reference.stubs(:reference).with(:static).returns(static) Puppet::Util::Reference.stubs(:reference).with(:dynamic).returns(dynamic) Puppet::Util::Reference.stubs(:references).returns([:static,:dynamic]) @doc.options.stubs(:[]=).with(:references, [:static]) @doc.setup_reference end it "should default to :type if no references" do @doc.options.stubs(:[]).with(:all).returns(false) array = stub 'array', :empty? => true @doc.options.stubs(:[]).with(:references).returns(array) array.expects(:<<).with(:type) @doc.setup_reference end end describe "in rdoc mode" do before :each do @doc.options.stubs(:[]).returns(false) Puppet.stubs(:parse_config) Puppet::Util::Log.stubs(:level=) Puppet::Util::Log.stubs(:newdestination) end describe "when there are unknown args" do it "should expand --modulepath if any" do @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] Puppet.settings.stubs(:handlearg) File.expects(:expand_path).with("path") @doc.setup_rdoc end it "should expand --manifestdir if any" do @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] Puppet.settings.stubs(:handlearg) File.expects(:expand_path).with("path") @doc.setup_rdoc end it "should give them to Puppet.settings" do @doc.unknown_args = [ { :opt => :option, :arg => :argument } ] Puppet.settings.expects(:handlearg).with(:option,:argument) @doc.setup_rdoc end end it "should operate in master run_mode" do @doc.class.run_mode.name.should == :master @doc.setup_rdoc end it "should parse puppet configuration" do Puppet.expects(:parse_config) @doc.setup_rdoc end it "should set log level to debug if --debug" do @doc.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:level=).with(:debug) @doc.setup_rdoc end it "should set log level to info if --verbose" do @doc.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:level=).with(:info) @doc.setup_rdoc end it "should set log destination to console if --verbose" do @doc.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup_rdoc end it "should set log destination to console if --debug" do @doc.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup_rdoc end end end describe "when running" do describe "in rdoc mode" do before :each do @doc.manifest = false Puppet.stubs(:info) Puppet.stubs(:[]).with(:trace).returns(false) @env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(@env) @env.stubs(:modulepath).returns(['modules']) @env.stubs(:[]).with(:manifest).returns('manifests/site.pp') Puppet.stubs(:[]).with(:modulepath).returns('modules') Puppet.stubs(:[]).with(:manifestdir).returns('manifests') @doc.options.stubs(:[]).with(:all).returns(false) @doc.options.stubs(:[]).with(:outputdir).returns('doc') @doc.options.stubs(:[]).with(:charset).returns(nil) Puppet.settings.stubs(:[]=).with(:document_all, false) Puppet.settings.stubs(:setdefaults) Puppet::Util::RDoc.stubs(:rdoc) @doc.stubs(:exit) File.stubs(:expand_path).with('modules').returns('modules') File.stubs(:expand_path).with('manifests').returns('manifests') @doc.command_line.stubs(:args).returns([]) end it "should set document_all on --all" do @doc.options.expects(:[]).with(:all).returns(true) Puppet.settings.expects(:[]=).with(:document_all, true) @doc.rdoc end it "should call Puppet::Util::RDoc.rdoc in full mode" do Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) @doc.rdoc end it "should call Puppet::Util::RDoc.rdoc with a charset if --charset has been provided" do @doc.options.expects(:[]).with(:charset).returns("utf-8") Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], "utf-8") @doc.rdoc end it "should call Puppet::Util::RDoc.rdoc in full mode with outputdir set to doc if no --outputdir" do @doc.options.expects(:[]).with(:outputdir).returns(false) Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) @doc.rdoc end it "should call Puppet::Util::RDoc.manifestdoc in manifest mode" do @doc.manifest = true Puppet::Util::RDoc.expects(:manifestdoc) @doc.rdoc end it "should get modulepath and manifestdir values from the environment" do @env.expects(:modulepath).returns(['envmodules1','envmodules2']) @env.expects(:[]).with(:manifest).returns('envmanifests/site.pp') Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil) @doc.rdoc end end describe "in the other modes" do it "should get reference in given format" do reference = stub 'reference' @doc.options.stubs(:[]).with(:mode).returns(:none) @doc.options.stubs(:[]).with(:references).returns([:ref]) require 'puppet/util/reference' Puppet::Util::Reference.expects(:reference).with(:ref).returns(reference) @doc.options.stubs(:[]).with(:format).returns(:format) @doc.stubs(:exit) reference.expects(:send).with { |format,contents| format == :format }.returns('doc') @doc.other end end end end diff --git a/spec/unit/application/face_base_spec.rb b/spec/unit/application/face_base_spec.rb new file mode 100755 index 000000000..939712ef8 --- /dev/null +++ b/spec/unit/application/face_base_spec.rb @@ -0,0 +1,187 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/application/face_base' +require 'tmpdir' + +class Puppet::Application::FaceBase::Basetest < Puppet::Application::FaceBase +end + +describe Puppet::Application::FaceBase do + before :all do + Puppet::Face.define(:basetest, '0.0.1') do + option("--[no-]boolean") + option("--mandatory MANDATORY") + option("--optional [OPTIONAL]") + + action :foo do + option("--action") + when_invoked { |*args| args.length } + end + end + end + + let :app do + app = Puppet::Application::FaceBase::Basetest.new + app.command_line.stubs(:subcommand_name).returns('subcommand') + Puppet::Util::Log.stubs(:newdestination) + app + end + + describe "#find_global_settings_argument" do + it "should not match --ca to --ca-location" do + option = mock('ca option', :optparse_args => ["--ca"]) + Puppet.settings.expects(:each).yields(:ca, option) + + app.find_global_settings_argument("--ca-location").should be_nil + end + end + + describe "#parse_options" do + before :each do + app.command_line.stubs(:args).returns %w{} + end + + describe "parsing the command line" do + context "with just an action" do + before :all do + # We have to stub Signal.trap to avoid a crazy mess where we take + # over signal handling and make it impossible to cancel the test + # suite run. + # + # It would be nice to fix this elsewhere, but it is actually hard to + # capture this in rspec 2.5 and all. :( --daniel 2011-04-08 + Signal.stubs(:trap) + app.command_line.stubs(:args).returns %w{foo} + app.preinit + app.parse_options + end + + it "should set the face based on the type" do + app.face.name.should == :basetest + end + + it "should set the format based on the face default" do + app.format.should == :pson + end + + it "should find the action" do + app.action.should be + app.action.name.should == :foo + end + end + + it "should fail if no action is given" do + expect { app.preinit; app.parse_options }. + to raise_error OptionParser::MissingArgument, /No action given/ + end + + it "should report a sensible error when options with = fail" do + app.command_line.stubs(:args).returns %w{--action=bar foo} + expect { app.preinit; app.parse_options }. + to raise_error OptionParser::InvalidOption, /invalid option: --action/ + end + + it "should fail if an action option is before the action" do + app.command_line.stubs(:args).returns %w{--action foo} + expect { app.preinit; app.parse_options }. + to raise_error OptionParser::InvalidOption, /invalid option: --action/ + end + + it "should fail if an unknown option is before the action" do + app.command_line.stubs(:args).returns %w{--bar foo} + expect { app.preinit; app.parse_options }. + to raise_error OptionParser::InvalidOption, /invalid option: --bar/ + end + + it "should fail if an unknown option is after the action" do + app.command_line.stubs(:args).returns %w{foo --bar} + expect { app.preinit; app.parse_options }. + to raise_error OptionParser::InvalidOption, /invalid option: --bar/ + end + + it "should accept --bar as an argument to a mandatory option after action" do + app.command_line.stubs(:args).returns %w{foo --mandatory --bar} + app.preinit + app.parse_options + app.action.name.should == :foo + app.options.should == { :mandatory => "--bar" } + end + + it "should accept --bar as an argument to a mandatory option before action" do + app.command_line.stubs(:args).returns %w{--mandatory --bar foo} + app.preinit + app.parse_options + app.action.name.should == :foo + app.options.should == { :mandatory => "--bar" } + end + + it "should not skip when --foo=bar is given" do + app.command_line.stubs(:args).returns %w{--mandatory=bar --bar foo} + expect { app.preinit; app.parse_options }. + to raise_error OptionParser::InvalidOption, /invalid option: --bar/ + end + + { "boolean options before" => %w{--trace foo}, + "boolean options after" => %w{foo --trace} + }.each do |name, args| + it "should accept global boolean settings #{name} the action" do + app.command_line.stubs(:args).returns args + app.preinit + app.parse_options + Puppet[:trace].should be_true + end + end + + { "before" => %w{--syslogfacility user1 foo}, + " after" => %w{foo --syslogfacility user1} + }.each do |name, args| + it "should accept global settings with arguments #{name} the action" do + app.command_line.stubs(:args).returns args + app.preinit + app.parse_options + Puppet[:syslogfacility].should == "user1" + end + end + end + end + + describe "#setup" do + it "should remove the action name from the arguments" do + app.command_line.stubs(:args).returns %w{--mandatory --bar foo} + app.preinit + app.parse_options + app.setup + app.arguments.should == [{ :mandatory => "--bar" }] + end + + it "should pass positional arguments" do + app.command_line.stubs(:args).returns %w{--mandatory --bar foo bar baz quux} + app.preinit + app.parse_options + app.setup + app.arguments.should == ['bar', 'baz', 'quux', { :mandatory => "--bar" }] + end + end + + describe "#main" do + before :each do + app.expects(:exit).with(0) + + app.face = Puppet::Face[:basetest, '0.0.1'] + app.action = app.face.get_action(:foo) + app.format = :pson + app.arguments = ["myname", "myarg"] + end + + it "should send the specified verb and name to the face" do + app.face.expects(:foo).with(*app.arguments) + app.main + end + + it "should use its render method to render any result" do + app.expects(:render).with(app.arguments.length + 1) + app.stubs(:puts) # meh. Don't print nil, thanks. --daniel 2011-04-12 + app.main + end + end +end diff --git a/spec/unit/application/faces_spec.rb b/spec/unit/application/faces_spec.rb new file mode 100755 index 000000000..cc159b6a5 --- /dev/null +++ b/spec/unit/application/faces_spec.rb @@ -0,0 +1,14 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/application/faces' + +describe Puppet::Application::Faces do + it "should be an application" do + Puppet::Application::Faces.superclass.should equal(Puppet::Application) + end + + it "should always call 'list'" do + subject.expects(:list) + subject.main + end +end diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb old mode 100644 new mode 100755 index 013e358d8..8ba86be9e --- a/spec/unit/application/filebucket_spec.rb +++ b/spec/unit/application/filebucket_spec.rb @@ -1,224 +1,223 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/filebucket' require 'puppet/file_bucket/dipper' describe Puppet::Application::Filebucket do before :each do @filebucket = Puppet::Application[:filebucket] end it "should ask Puppet::Application to not parse Puppet configuration file" do @filebucket.should_parse_config?.should be_false end it "should declare a get command" do @filebucket.should respond_to(:get) end it "should declare a backup command" do @filebucket.should respond_to(:backup) end it "should declare a restore command" do @filebucket.should respond_to(:restore) end [:bucket, :debug, :local, :remote, :verbose].each do |option| it "should declare handle_#{option} method" do @filebucket.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @filebucket.options.expects(:[]=).with("#{option}".to_sym, 'arg') @filebucket.send("handle_#{option}".to_sym, 'arg') end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) @filebucket.options.stubs(:[]).with(any_parameters) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @filebucket.setup end it "should trap INT" do Signal.expects(:trap).with(:INT) @filebucket.setup end it "should set log level to debug if --debug was passed" do @filebucket.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @filebucket.setup end it "should set log level to info if --verbose was passed" do @filebucket.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @filebucket.setup end it "should Parse puppet config" do Puppet.expects(:parse_config) @filebucket.setup end it "should print puppet config if asked to in Puppet config" do @filebucket.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @filebucket.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @filebucket.setup }.should raise_error(SystemExit) end describe "with local bucket" do before :each do @filebucket.options.stubs(:[]).with(:local).returns(true) end it "should create a client with the default bucket if none passed" do Puppet.stubs(:[]).with(:bucketdir).returns("path") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } @filebucket.setup end it "should create a local Dipper with the given bucket" do @filebucket.options.stubs(:[]).with(:bucket).returns("path") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } @filebucket.setup end end describe "with remote bucket" do it "should create a remote Client to the configured server" do Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Server] == "puppet.reductivelabs.com" } @filebucket.setup end end end describe "when running" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) @filebucket.options.stubs(:[]).with(any_parameters) @client = stub 'client' Puppet::FileBucket::Dipper.stubs(:new).returns(@client) @filebucket.setup end it "should use the first non-option parameter as the dispatch" do @filebucket.command_line.stubs(:args).returns(['get']) @filebucket.expects(:get) @filebucket.run_command end describe "the command get" do before :each do @filebucket.stubs(:print) @filebucket.stubs(:args).returns([]) end it "should call the client getfile method" do @client.expects(:getfile) @filebucket.get end it "should call the client getfile method with the given md5" do md5="DEADBEEF" @filebucket.stubs(:args).returns([md5]) @client.expects(:getfile).with(md5) @filebucket.get end it "should print the file content" do @client.stubs(:getfile).returns("content") @filebucket.expects(:print).returns("content") @filebucket.get end end describe "the command backup" do it "should call the client backup method for each given parameter" do @filebucket.stubs(:puts) FileTest.stubs(:exists?).returns(true) FileTest.stubs(:readable?).returns(true) @filebucket.stubs(:args).returns(["file1", "file2"]) @client.expects(:backup).with("file1") @client.expects(:backup).with("file2") @filebucket.backup end end describe "the command restore" do it "should call the client getfile method with the given md5" do md5="DEADBEEF" file="testfile" @filebucket.stubs(:args).returns([file, md5]) @client.expects(:restore).with(file,md5) @filebucket.restore end end end end diff --git a/spec/unit/application/indirection_base_spec.rb b/spec/unit/application/indirection_base_spec.rb new file mode 100755 index 000000000..63ab11eed --- /dev/null +++ b/spec/unit/application/indirection_base_spec.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/application/indirection_base' +require 'puppet/face/indirector' + +######################################################################## +# Stub for testing; the names are critical, sadly. --daniel 2011-03-30 +class Puppet::Application::TestIndirection < Puppet::Application::IndirectionBase +end + +face = Puppet::Face::Indirector.define(:testindirection, '0.0.1') do +end +# REVISIT: This horror is required because we don't allow anything to be +# :current except for if it lives on, and is loaded from, disk. --daniel 2011-03-29 +face.instance_variable_set('@version', :current) +Puppet::Face.register(face) +######################################################################## + + +describe Puppet::Application::IndirectionBase do + subject { Puppet::Application::TestIndirection.new } + + it "should accept a terminus command line option" do + # It would be nice not to have to stub this, but whatever... writing an + # entire indirection stack would cause us more grief. --daniel 2011-03-31 + terminus = mock("test indirection terminus") + Puppet::Indirector::Indirection.expects(:instance). + with(:testindirection).twice.returns() + + subject.command_line. + instance_variable_set('@args', %w{--terminus foo save}) + + # Not a very nice thing. :( + $stderr.stubs(:puts) + + expect { subject.run }.should raise_error SystemExit + end +end diff --git a/spec/unit/application/inspect_spec.rb b/spec/unit/application/inspect_spec.rb index d334a87ee..fda61c6e4 100755 --- a/spec/unit/application/inspect_spec.rb +++ b/spec/unit/application/inspect_spec.rb @@ -1,278 +1,277 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/inspect' require 'puppet/resource/catalog' require 'puppet/indirector/catalog/yaml' require 'puppet/indirector/report/rest' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::Application::Inspect do include PuppetSpec::Files before :each do @inspect = Puppet::Application[:inspect] end describe "during setup" do it "should print its configuration if asked" do Puppet[:configprint] = "all" Puppet.settings.expects(:print_configs).returns(true) lambda { @inspect.setup }.should raise_error(SystemExit) end it "should fail if reporting is turned off" do Puppet[:report] = false lambda { @inspect.setup }.should raise_error(/report=true/) end end describe "when executing" do before :each do Puppet[:report] = true @inspect.options[:logset] = true Puppet::Transaction::Report::Rest.any_instance.stubs(:save) @inspect.setup end it "should retrieve the local catalog" do Puppet::Resource::Catalog::Yaml.any_instance.expects(:find).with {|request| request.key == Puppet[:certname] }.returns(Puppet::Resource::Catalog.new) @inspect.run_command end it "should save the report to REST" do Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(Puppet::Resource::Catalog.new) Puppet::Transaction::Report::Rest.any_instance.expects(:save).with {|request| request.instance.host == Puppet[:certname] } @inspect.run_command end it "should audit the specified properties" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") file.puts("file contents") file.close resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command properties = events.inject({}) do |property_values, event| property_values.merge(event.property => event.previous_value) end properties["ensure"].should == :file properties["content"].should == "{md5}#{Digest::MD5.hexdigest("file contents\n")}" properties.has_key?("target").should == false end it "should set audited to true for all events" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command events.each do |event| event.audited.should == true end end it "should not report irrelevent attributes if the resource is absent" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) file.delete catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command properties = events.inject({}) do |property_values, event| property_values.merge(event.property => event.previous_value) end properties.should == {"ensure" => :absent} end describe "when archiving to a bucket" do before :each do Puppet[:archive_files] = true Puppet[:archive_file_server] = "filebucketserver" @catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(@catalog) end describe "when auditing files" do before :each do @file = tmpfile("foo") @resource = Puppet::Resource.new(:file, @file, :parameters => {:audit => "content"}) @catalog.add_resource(@resource) end it "should send an existing file to the file bucket" do File.open(@file, 'w') { |f| f.write('stuff') } Puppet::FileBucketFile::Rest.any_instance.expects(:head).with do |request| request.server == Puppet[:archive_file_server] end.returns(false) Puppet::FileBucketFile::Rest.any_instance.expects(:save).with do |request| request.server == Puppet[:archive_file_server] and request.instance.contents == 'stuff' end @inspect.run_command end it "should not send unreadable files" do File.open(@file, 'w') { |f| f.write('stuff') } File.chmod(0, @file) Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should not try to send non-existent files" do Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should not try to send files whose content we are not auditing" do @resource[:audit] = "group" Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should continue if bucketing a file fails" do File.open(@file, 'w') { |f| f.write('stuff') } Puppet::FileBucketFile::Rest.any_instance.stubs(:head).returns false Puppet::FileBucketFile::Rest.any_instance.stubs(:save).raises "failure" Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| @report = request.instance end @inspect.run_command @report.logs.first.should_not == nil @report.logs.first.message.should =~ /Could not back up/ end end describe "when auditing non-files" do before :each do Puppet::Type.newtype(:stub_type) do newparam(:name) do desc "The name var" isnamevar end newproperty(:content) do desc "content" def retrieve :whatever end end end @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) end after :each do Puppet::Type.rmtype(:stub_type) end it "should not try to send non-files" do Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end end end describe "when there are failures" do before :each do Puppet::Type.newtype(:stub_type) do newparam(:name) do desc "The name var" isnamevar end newproperty(:content) do desc "content" def retrieve raise "failed" end end end @catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(@catalog) Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| @report = request.instance end end after :each do Puppet::Type.rmtype(:stub_type) end it "should mark the report failed and create failed events for each property" do @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) @inspect.run_command @report.status.should == "failed" - @report.logs.select{|log| log.message =~ /Could not inspect/}.count.should == 1 - @report.resource_statuses.count.should == 1 - @report.resource_statuses['Stub_type[foo]'].events.count.should == 1 + @report.logs.select{|log| log.message =~ /Could not inspect/}.size.should == 1 + @report.resource_statuses.size.should == 1 + @report.resource_statuses['Stub_type[foo]'].events.size.should == 1 event = @report.resource_statuses['Stub_type[foo]'].events.first event.property.should == "content" event.status.should == "failure" event.audited.should == true event.instance_variables.should_not include("@previous_value") end it "should continue to the next resource" do @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @other_resource = Puppet::Resource.new(:stub_type, 'bar', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) @catalog.add_resource(@other_resource) @inspect.run_command - @report.resource_statuses.count.should == 2 + @report.resource_statuses.size.should == 2 @report.resource_statuses.keys.should =~ ['Stub_type[foo]', 'Stub_type[bar]'] end end end after :all do Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet::Transaction::Report.indirection.terminus_class = :processor end end diff --git a/spec/unit/application/kick_spec.rb b/spec/unit/application/kick_spec.rb index ce0e0c7d0..29e4caea4 100755 --- a/spec/unit/application/kick_spec.rb +++ b/spec/unit/application/kick_spec.rb @@ -1,311 +1,310 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/kick' describe Puppet::Application::Kick, :if => Puppet.features.posix? do before :each do require 'puppet/util/ldap/connection' Puppet::Util::Ldap::Connection.stubs(:new).returns(stub_everything) @kick = Puppet::Application[:kick] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end describe ".new" do it "should take a command-line object as an argument" do command_line = stub_everything "command_line" lambda{ Puppet::Application::Kick.new( command_line ) }.should_not raise_error end end it "should ask Puppet::Application to not parse Puppet configuration file" do @kick.should_parse_config?.should be_false end it "should declare a main command" do @kick.should respond_to(:main) end it "should declare a test command" do @kick.should respond_to(:test) end it "should declare a preinit block" do @kick.should respond_to(:preinit) end describe "during preinit" do before :each do @kick.stubs(:trap) end it "should catch INT and TERM" do @kick.stubs(:trap).with { |arg,block| arg == :INT or arg == :TERM } @kick.preinit end it "should set parallel option to 1" do @kick.preinit @kick.options[:parallel].should == 1 end it "should set verbose by default" do @kick.preinit @kick.options[:verbose].should be_true end it "should set fqdn by default" do @kick.preinit @kick.options[:fqdn].should be_true end it "should set ignoreschedules to 'false'" do @kick.preinit @kick.options[:ignoreschedules].should be_false end it "should set foreground to 'false'" do @kick.preinit @kick.options[:foreground].should be_false end end describe "when applying options" do before do @kick.preinit end [:all, :foreground, :debug, :ping, :test].each do |option| it "should declare handle_#{option} method" do @kick.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @kick.options.expects(:[]=).with(option, 'arg') @kick.send("handle_#{option}".to_sym, 'arg') end end it "should add to the host list with the host option" do @kick.handle_host('host') @kick.hosts.should == ['host'] end it "should add to the tag list with the tag option" do @kick.handle_tag('tag') @kick.tags.should == ['tag'] end it "should add to the class list with the class option" do @kick.handle_class('class') @kick.classes.should == ['class'] end end describe "during setup" do before :each do @kick.classes = [] @kick.tags = [] @kick.hosts = [] Puppet::Log.stubs(:level=) @kick.stubs(:trap) @kick.stubs(:puts) Puppet.stubs(:parse_config) @kick.options.stubs(:[]).with(any_parameters) end it "should set log level to debug if --debug was passed" do @kick.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @kick.setup end it "should set log level to info if --verbose was passed" do @kick.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @kick.setup end it "should Parse puppet config" do Puppet.expects(:parse_config) @kick.setup end describe "when using the ldap node terminus" do before :each do Puppet.stubs(:[]).with(:node_terminus).returns("ldap") end it "should pass the fqdn option to search" do @kick.options.stubs(:[]).with(:fqdn).returns(:something) @kick.options.stubs(:[]).with(:all).returns(true) @kick.stubs(:puts) Puppet::Node.indirection.expects(:search).with("whatever",:fqdn => :something).returns([]) @kick.setup end it "should search for all nodes if --all" do @kick.options.stubs(:[]).with(:all).returns(true) @kick.stubs(:puts) Puppet::Node.indirection.expects(:search).with("whatever",:fqdn => nil).returns([]) @kick.setup end it "should search for nodes including given classes" do @kick.options.stubs(:[]).with(:all).returns(false) @kick.stubs(:puts) @kick.classes = ['class'] Puppet::Node.indirection.expects(:search).with("whatever", :class => "class", :fqdn => nil).returns([]) @kick.setup end end describe "when using regular nodes" do it "should fail if some classes have been specified" do $stderr.stubs(:puts) @kick.classes = ['class'] @kick.expects(:exit).with(24) @kick.setup end end end describe "when running" do before :each do @kick.stubs(:puts) end it "should dispatch to test if --test is used" do @kick.options.stubs(:[]).with(:test).returns(true) @kick.expects(:test) @kick.run_command end it "should dispatch to main if --test is not used" do @kick.options.stubs(:[]).with(:test).returns(false) @kick.expects(:main) @kick.run_command end describe "the test command" do it "should exit with exit code 0 " do @kick.expects(:exit).with(0) @kick.test end end describe "the main command" do before :each do @kick.options.stubs(:[]).with(:parallel).returns(1) @kick.options.stubs(:[]).with(:ping).returns(false) @kick.options.stubs(:[]).with(:ignoreschedules).returns(false) @kick.options.stubs(:[]).with(:foreground).returns(false) @kick.options.stubs(:[]).with(:debug).returns(false) @kick.stubs(:print) @kick.stubs(:exit) @kick.preinit @kick.stubs(:parse_options) @kick.setup $stderr.stubs(:puts) end it "should create as much childs as --parallel" do @kick.options.stubs(:[]).with(:parallel).returns(3) @kick.hosts = ['host1', 'host2', 'host3'] @kick.stubs(:exit).raises(SystemExit) Process.stubs(:wait).returns(1).then.returns(2).then.returns(3).then.raises(Errno::ECHILD) @kick.expects(:fork).times(3).returns(1).then.returns(2).then.returns(3) lambda { @kick.main }.should raise_error end it "should delegate to run_for_host per host" do @kick.hosts = ['host1', 'host2'] @kick.stubs(:exit).raises(SystemExit) @kick.stubs(:fork).returns(1).yields Process.stubs(:wait).returns(1).then.raises(Errno::ECHILD) @kick.expects(:run_for_host).times(2) lambda { @kick.main }.should raise_error end describe "during call of run_for_host" do before do require 'puppet/run' options = { :background => true, :ignoreschedules => false, :tags => [] } @kick.preinit @agent_run = Puppet::Run.new( options.dup ) @agent_run.stubs(:status).returns("success") Puppet::Run.indirection.expects(:terminus_class=).with( :rest ) Puppet::Run.expects(:new).with( options ).returns(@agent_run) end it "should call run on a Puppet::Run for the given host" do Puppet::Run.indirection.expects(:save).with(@agent_run, 'https://host:8139/production/run/host').returns(@agent_run) @kick.run_for_host('host') end it "should exit the child with 0 on success" do @agent_run.stubs(:status).returns("success") @kick.expects(:exit).with(0) @kick.run_for_host('host') end it "should exit the child with 3 on running" do @agent_run.stubs(:status).returns("running") @kick.expects(:exit).with(3) @kick.run_for_host('host') end it "should exit the child with 12 on unknown answer" do @agent_run.stubs(:status).returns("whatever") @kick.expects(:exit).with(12) @kick.run_for_host('host') end end end end end diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb old mode 100644 new mode 100755 index 14478a61a..ea5d3f518 --- a/spec/unit/application/master_spec.rb +++ b/spec/unit/application/master_spec.rb @@ -1,451 +1,410 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/master' require 'puppet/daemon' require 'puppet/network/server' describe Puppet::Application::Master do before :each do @master = Puppet::Application[:master] @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::SSL::Host.stubs(:ca_location=) end it "should operate in master run_mode" do @master.class.run_mode.name.should equal(:master) end it "should ask Puppet::Application to parse Puppet configuration file" do @master.should_parse_config?.should be_true end it "should declare a main command" do @master.should respond_to(:main) end - it "should declare a parseonly command" do - @master.should respond_to(:parseonly) - end - it "should declare a compile command" do @master.should respond_to(:compile) end it "should declare a preinit block" do @master.should respond_to(:preinit) end describe "during preinit" do before :each do @master.stubs(:trap) end it "should catch INT" do @master.stubs(:trap).with { |arg,block| arg == :INT } @master.preinit end it "should create a Puppet Daemon" do Puppet::Daemon.expects(:new).returns(@daemon) @master.preinit end it "should give ARGV to the Daemon" do argv = stub 'argv' ARGV.stubs(:dup).returns(argv) @daemon.expects(:argv=).with(argv) @master.preinit end end [:debug,:verbose].each do |option| it "should declare handle_#{option} method" do @master.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @master.options.expects(:[]=).with(option, 'arg') @master.send("handle_#{option}".to_sym, 'arg') end end describe "when applying options" do before do @master.command_line.stubs(:args).returns([]) end it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @master.handle_logdest("console") end it "should put the setdest options to true" do @master.options.expects(:[]=).with(:setdest,true) @master.handle_logdest("console") end it "should parse the log destination from ARGV" do @master.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @master.parse_options end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet::SSL::CertificateAuthority.stubs(:instance) Puppet::SSL::CertificateAuthority.stubs(:ca?) Puppet.settings.stubs(:use) @master.options.stubs(:[]).with(any_parameters) end it "should set log level to debug if --debug was passed" do @master.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @master.setup end it "should set log level to info if --verbose was passed" do @master.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @master.setup end it "should set console as the log destination if no --logdest and --daemonize" do @master.stubs(:[]).with(:daemonize).returns(:false) Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should set syslog as the log destination if no --logdest and not --daemonize" do Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should set syslog as the log destination if --rack" do @master.options.stubs(:[]).with(:rack).returns(:true) Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should print puppet config if asked to in Puppet config" do @master.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @master.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @master.setup }.should raise_error(SystemExit) end it "should tell Puppet.settings to use :main,:ssl,:master and :metrics category" do Puppet.settings.expects(:use).with(:main,:master,:ssl,:metrics) @master.setup end it "should cache class in yaml" do Puppet::Node.indirection.expects(:cache_class=).with(:yaml) @master.setup end describe "with no ca" do it "should set the ca_location to none" do Puppet::SSL::Host.expects(:ca_location=).with(:none) @master.setup end end describe "with a ca configured" do before :each do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) end it "should set the ca_location to local" do Puppet::SSL::Host.expects(:ca_location=).with(:local) @master.setup end it "should tell Puppet.settings to use :ca category" do Puppet.settings.expects(:use).with(:ca) @master.setup end it "should instantiate the CertificateAuthority singleton" do Puppet::SSL::CertificateAuthority.expects(:instance) @master.setup end end end describe "when running" do before do @master.preinit end - it "should dispatch to parseonly if parseonly is set" do - Puppet.stubs(:[]).with(:parseonly).returns(true) - @master.options[:node] = nil - - @master.expects(:parseonly) - @master.run_command - end - it "should dispatch to compile if called with --compile" do @master.options[:node] = "foo" @master.expects(:compile) @master.run_command end - it "should dispatch to main if parseonly is not set" do - Puppet.stubs(:[]).with(:parseonly).returns(false) + it "should dispatch to main otherwise" do @master.options[:node] = nil @master.expects(:main) @master.run_command end - - describe "the parseonly command" do - before :each do - @environment = Puppet::Node::Environment.new("env") - Puppet.stubs(:[]).with(:environment).returns(@environment) - Puppet.stubs(:[]).with(:manifest).returns("site.pp") - Puppet.stubs(:err) - @master.stubs(:exit) - end - - it "should use a Puppet Resource Type Collection to parse the file" do - @environment.expects(:perform_initial_import) - @master.parseonly - end - - it "should exit with exit code 0 if no error" do - @master.expects(:exit).with(0) - @master.parseonly - end - - it "should exit with exit code 1 if error" do - @environment.stubs(:perform_initial_import).raises(Puppet::ParseError) - @master.expects(:exit).with(1) - @master.parseonly - end - end - describe "the compile command" do before do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @master.stubs(:jj) @master.stubs(:exit) Puppet.features.stubs(:pson?).returns true end it "should fail if pson isn't available" do Puppet.features.expects(:pson?).returns false lambda { @master.compile }.should raise_error end it "should compile a catalog for the specified node" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).with("foo").returns Puppet::Resource::Catalog.new $stdout.stubs(:puts) @master.compile end it "should convert the catalog to a pure-resource catalog and use 'jj' to pretty-print the catalog" do catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.indirection.expects(:find).returns catalog catalog.expects(:to_resource).returns("rescat") @master.options[:node] = "foo" @master.expects(:jj).with("rescat") @master.compile end it "should exit with error code 30 if no catalog can be found" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).returns nil @master.expects(:exit).with(30) $stderr.expects(:puts) @master.compile end it "should exit with error code 30 if there's a failure" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).raises ArgumentError @master.expects(:exit).with(30) $stderr.expects(:puts) @master.compile end end describe "the main command" do before :each do @master.preinit @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) @app = stub_everything 'app' Puppet::SSL::Host.stubs(:localhost) Puppet::SSL::CertificateAuthority.stubs(:ca?) Process.stubs(:uid).returns(1000) Puppet.stubs(:service) Puppet.stubs(:[]) Puppet.stubs(:notice) Puppet.stubs(:start) end it "should create a Server" do Puppet::Network::Server.expects(:new) @master.main end it "should give the server to the daemon" do @daemon.expects(:server=).with(@server) @master.main end it "should create the server with the right XMLRPC handlers" do Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket]} @master.main end it "should create the server with a :ca xmlrpc handler if needed" do Puppet.stubs(:[]).with(:ca).returns(true) Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers].include?(:CA) } @master.main end it "should generate a SSL cert for localhost" do Puppet::SSL::Host.expects(:localhost) @master.main end it "should make sure to *only* hit the CA for data" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:only) @master.main end it "should drop privileges if running as root" do Puppet.features.stubs(:root?).returns true Puppet::Util.expects(:chuser) @master.main end it "should daemonize if needed" do Puppet.stubs(:[]).with(:daemonize).returns(true) @daemon.expects(:daemonize) @master.main end it "should start the service" do @daemon.expects(:start) @master.main end describe "with --rack", :if => Puppet.features.rack? do before do require 'puppet/network/http/rack' Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) end it "it should create the app with REST and XMLRPC support" do @master.options.stubs(:[]).with(:rack).returns(:true) Puppet::Network::HTTP::Rack.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket] and args[:protocols] == [:rest, :xmlrpc] } @master.main end it "it should not start a daemon" do @master.options.stubs(:[]).with(:rack).returns(:true) @daemon.expects(:start).never @master.main end it "it should return the app" do @master.options.stubs(:[]).with(:rack).returns(:true) app = @master.main app.should equal(@app) end end end end end diff --git a/spec/unit/application/queue_spec.rb b/spec/unit/application/queue_spec.rb index bb485ac3c..87713ca97 100755 --- a/spec/unit/application/queue_spec.rb +++ b/spec/unit/application/queue_spec.rb @@ -1,183 +1,182 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/queue' require 'puppet/indirector/catalog/queue' describe Puppet::Application::Queue do before :each do @queue = Puppet::Application[:queue] @queue.stubs(:puts) @daemon = stub_everything 'daemon', :daemonize => nil Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) end it "should ask Puppet::Application to parse Puppet configuration file" do @queue.should_parse_config?.should be_true end it "should declare a main command" do @queue.should respond_to(:main) end it "should declare a preinit block" do @queue.should respond_to(:preinit) end describe "in preinit" do it "should catch INT" do Signal.expects(:trap).with { |arg,block| arg == :INT } @queue.preinit end it "should init :verbose to false" do @queue.preinit @queue.options[:verbose].should be_false end it "should init :debug to false" do @queue.preinit @queue.options[:debug].should be_false end it "should create a Daemon instance and copy ARGV to it" do ARGV.expects(:dup).returns "eh" daemon = mock("daemon") daemon.expects(:argv=).with("eh") Puppet::Daemon.expects(:new).returns daemon @queue.preinit end end describe "when handling options" do [:debug, :verbose].each do |option| it "should declare handle_#{option} method" do @queue.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @queue.options.expects(:[]=).with(option, 'arg') @queue.send("handle_#{option}".to_sym, 'arg') end end end describe "during setup" do before :each do @queue.options.stubs(:[]) @queue.daemon.stubs(:daemonize) Puppet.stubs(:info) Puppet.features.stubs(:stomp?).returns true Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet.stubs(:settraps) Puppet.settings.stubs(:print_config?) Puppet.settings.stubs(:print_config) end it "should fail if the stomp feature is missing" do Puppet.features.expects(:stomp?).returns false lambda { @queue.setup }.should raise_error(ArgumentError) end it "should print puppet config if asked to in Puppet config" do @queue.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @queue.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @queue.setup }.should raise_error(SystemExit) end it "should call setup_logs" do @queue.expects(:setup_logs) @queue.setup end describe "when setting up logs" do before :each do Puppet::Util::Log.stubs(:newdestination) end it "should set log level to debug if --debug was passed" do @queue.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:level=).with(:debug) @queue.setup_logs end it "should set log level to info if --verbose was passed" do @queue.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:level=).with(:info) @queue.setup_logs end [:verbose, :debug].each do |level| it "should set console as the log destination with level #{level}" do @queue.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @queue.setup_logs end end end it "should configure the Catalog class to use ActiveRecord" do Puppet::Resource::Catalog.indirection.expects(:terminus_class=).with(:active_record) @queue.setup end it "should daemonize if needed" do Puppet.expects(:[]).with(:daemonize).returns(true) @queue.daemon.expects(:daemonize) @queue.setup end end describe "when running" do before :each do @queue.stubs(:sleep_forever) Puppet::Resource::Catalog::Queue.stubs(:subscribe) Thread.list.each { |t| t.stubs(:join) } end it "should subscribe to the queue" do Puppet::Resource::Catalog::Queue.expects(:subscribe) @queue.main end it "should log and save each catalog passed by the queue" do catalog = Puppet::Resource::Catalog.new('eh') Puppet::Resource::Catalog.indirection.expects(:save).with(catalog) Puppet::Resource::Catalog::Queue.expects(:subscribe).yields(catalog) Puppet.expects(:notice).times(2) @queue.main end it "should join all of the running threads" do Thread.list.each { |t| t.expects(:join) } @queue.main end end end diff --git a/spec/unit/application/resource_spec.rb b/spec/unit/application/resource_spec.rb index 3e3f8296b..673bd65d7 100755 --- a/spec/unit/application/resource_spec.rb +++ b/spec/unit/application/resource_spec.rb @@ -1,233 +1,232 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application/resource' describe Puppet::Application::Resource do before :each do @resource = Puppet::Application[:resource] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Resource.indirection.stubs(:terminus_class=) end it "should ask Puppet::Application to not parse Puppet configuration file" do @resource.should_parse_config?.should be_false end it "should declare a main command" do @resource.should respond_to(:main) end it "should declare a host option" do @resource.should respond_to(:handle_host) end it "should declare a types option" do @resource.should respond_to(:handle_types) end it "should declare a param option" do @resource.should respond_to(:handle_param) end it "should declare a preinit block" do @resource.should respond_to(:preinit) end describe "in preinit" do it "should set hosts to nil" do @resource.preinit @resource.host.should be_nil end it "should init extra_params to empty array" do @resource.preinit @resource.extra_params.should == [] end it "should load Facter facts" do Facter.expects(:loadfacts).once @resource.preinit end end describe "when handling options" do [:debug, :verbose, :edit].each do |option| it "should declare handle_#{option} method" do @resource.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @resource.options.expects(:[]=).with(option, 'arg') @resource.send("handle_#{option}".to_sym, 'arg') end end it "should set options[:host] to given host" do @resource.handle_host(:whatever) @resource.host.should == :whatever end it "should load an display all types with types option" do type1 = stub_everything 'type1', :name => :type1 type2 = stub_everything 'type2', :name => :type2 Puppet::Type.stubs(:loadall) Puppet::Type.stubs(:eachtype).multiple_yields(type1,type2) @resource.stubs(:exit) @resource.expects(:puts).with(['type1','type2']) @resource.handle_types(nil) end it "should add param to extra_params list" do @resource.extra_params = [ :param1 ] @resource.handle_param("whatever") @resource.extra_params.should == [ :param1, :whatever ] end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @resource.setup end it "should set log level to debug if --debug was passed" do @resource.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @resource.setup end it "should set log level to info if --verbose was passed" do @resource.options.stubs(:[]).with(:debug).returns(false) @resource.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @resource.setup end it "should Parse puppet config" do Puppet.expects(:parse_config) @resource.setup end end describe "when running" do before :each do @type = stub_everything 'type', :properties => [] @resource.command_line.stubs(:args).returns(['type']) Puppet::Type.stubs(:type).returns(@type) end it "should raise an error if no type is given" do @resource.command_line.stubs(:args).returns([]) lambda { @resource.main }.should raise_error end it "should raise an error when editing a remote host" do @resource.options.stubs(:[]).with(:edit).returns(true) @resource.host = 'host' lambda { @resource.main }.should raise_error end it "should raise an error if the type is not found" do Puppet::Type.stubs(:type).returns(nil) lambda { @resource.main }.should raise_error end describe "with a host" do before :each do @resource.stubs(:puts) @resource.host = 'host' Puppet::Resource.indirection.stubs(:find ).never Puppet::Resource.indirection.stubs(:search).never Puppet::Resource.indirection.stubs(:save ).never end it "should search for resources" do @resource.command_line.stubs(:args).returns(['type']) Puppet::Resource.indirection.expects(:search).with('https://host:8139/production/resources/type/', {}).returns([]) @resource.main end it "should describe the given resource" do @resource.command_line.stubs(:args).returns(['type', 'name']) x = stub_everything 'resource' Puppet::Resource.indirection.expects(:find).with('https://host:8139/production/resources/type/name').returns(x) @resource.main end it "should add given parameters to the object" do @resource.command_line.stubs(:args).returns(['type','name','param=temp']) res = stub "resource" Puppet::Resource.indirection.expects(:save).with(res, 'https://host:8139/production/resources/type/name').returns(res) res.expects(:collect) res.expects(:to_manifest) Puppet::Resource.expects(:new).with('type', 'name', :parameters => {'param' => 'temp'}).returns(res) @resource.main end end describe "without a host" do before :each do @resource.stubs(:puts) @resource.host = nil Puppet::Resource.indirection.stubs(:find ).never Puppet::Resource.indirection.stubs(:search).never Puppet::Resource.indirection.stubs(:save ).never end it "should search for resources" do Puppet::Resource.indirection.expects(:search).with('type/', {}).returns([]) @resource.main end it "should describe the given resource" do @resource.command_line.stubs(:args).returns(['type','name']) x = stub_everything 'resource' Puppet::Resource.indirection.expects(:find).with('type/name').returns(x) @resource.main end it "should add given parameters to the object" do @resource.command_line.stubs(:args).returns(['type','name','param=temp']) res = stub "resource" Puppet::Resource.indirection.expects(:save).with(res, 'type/name').returns(res) res.expects(:collect) res.expects(:to_manifest) Puppet::Resource.expects(:new).with('type', 'name', :parameters => {'param' => 'temp'}).returns(res) @resource.main end end end end diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index befc4f510..f46959092 100755 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -1,570 +1,609 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/application' require 'puppet' require 'getoptlong' describe Puppet::Application do before do @app = Class.new(Puppet::Application).new @appclass = @app.class + @app.stubs(:name).returns("test_app") # avoid actually trying to parse any settings Puppet.settings.stubs(:parse) end describe "finding" do before do @klass = Puppet::Application @klass.stubs(:puts) end it "should find classes in the namespace" do @klass.find("Agent").should == @klass::Agent end it "should not find classes outside the namespace" do lambda { @klass.find("String") }.should raise_error(SystemExit) end it "should exit if it can't find a class" do lambda { @klass.find("ThisShallNeverEverEverExistAsdf") }.should raise_error(SystemExit) end end describe ".run_mode" do it "should default to user" do @appclass.run_mode.name.should == :user end it "should set and get a value" do @appclass.run_mode :agent @appclass.run_mode.name.should == :agent end end it "should sadly and frighteningly allow run_mode to change at runtime" do class TestApp < Puppet::Application run_mode :master def run_command # This is equivalent to calling these methods externally to the # instance, but since this is what "real world" code is likely to do # (and we need the class anyway) we may as well test that. --daniel 2011-02-03 set_run_mode self.class.run_mode "agent" end end Puppet[:run_mode].should == "user" expect { app = TestApp.new Puppet[:run_mode].should == "master" app.run app.class.run_mode.name.should == :agent $puppet_application_mode.name.should == :agent }.should_not raise_error Puppet[:run_mode].should == "agent" end it "it should not allow run mode to be set multiple times" do pending "great floods of tears, you can do this right now" # --daniel 2011-02-03 app = Puppet::Application.new expect { app.set_run_mode app.class.run_mode "master" $puppet_application_mode.name.should == :master app.set_run_mode app.class.run_mode "agent" $puppet_application_mode.name.should == :agent }.should raise_error end it "should explode when an invalid run mode is set at runtime, for great victory" # ...but you can, and while it will explode, that only happens too late for # us to easily test. --daniel 2011-02-03 it "should have a run entry-point" do @app.should respond_to(:run) end it "should have a read accessor to options" do @app.should respond_to(:options) end it "should include a default setup method" do @app.should respond_to(:setup) end it "should include a default preinit method" do @app.should respond_to(:preinit) end it "should include a default run_command method" do @app.should respond_to(:run_command) end it "should invoke main as the default" do @app.expects( :main ) @app.run_command end describe 'when invoking clear!' do before :each do Puppet::Application.run_status = :stop_requested Puppet::Application.clear! end it 'should have nil run_status' do Puppet::Application.run_status.should be_nil end it 'should return false for restart_requested?' do Puppet::Application.restart_requested?.should be_false end it 'should return false for stop_requested?' do Puppet::Application.stop_requested?.should be_false end it 'should return false for interrupted?' do Puppet::Application.interrupted?.should be_false end it 'should return true for clear?' do Puppet::Application.clear?.should be_true end end describe 'after invoking stop!' do before :each do Puppet::Application.run_status = nil Puppet::Application.stop! end after :each do Puppet::Application.run_status = nil end it 'should have run_status of :stop_requested' do Puppet::Application.run_status.should == :stop_requested end it 'should return true for stop_requested?' do Puppet::Application.stop_requested?.should be_true end it 'should return false for restart_requested?' do Puppet::Application.restart_requested?.should be_false end it 'should return true for interrupted?' do Puppet::Application.interrupted?.should be_true end it 'should return false for clear?' do Puppet::Application.clear?.should be_false end end describe 'when invoking restart!' do before :each do Puppet::Application.run_status = nil Puppet::Application.restart! end after :each do Puppet::Application.run_status = nil end it 'should have run_status of :restart_requested' do Puppet::Application.run_status.should == :restart_requested end it 'should return true for restart_requested?' do Puppet::Application.restart_requested?.should be_true end it 'should return false for stop_requested?' do Puppet::Application.stop_requested?.should be_false end it 'should return true for interrupted?' do Puppet::Application.interrupted?.should be_true end it 'should return false for clear?' do Puppet::Application.clear?.should be_false end end describe 'when performing a controlled_run' do it 'should not execute block if not :clear?' do Puppet::Application.run_status = :stop_requested target = mock 'target' target.expects(:some_method).never Puppet::Application.controlled_run do target.some_method end end it 'should execute block if :clear?' do Puppet::Application.run_status = nil target = mock 'target' target.expects(:some_method).once Puppet::Application.controlled_run do target.some_method end end describe 'on POSIX systems', :if => Puppet.features.posix? do it 'should signal process with HUP after block if restart requested during block execution' do Puppet::Application.run_status = nil target = mock 'target' target.expects(:some_method).once old_handler = trap('HUP') { target.some_method } begin Puppet::Application.controlled_run do Puppet::Application.run_status = :restart_requested end ensure trap('HUP', old_handler) end end end after :each do Puppet::Application.run_status = nil end end describe "when parsing command-line options" do before :each do @app.command_line.stubs(:args).returns([]) Puppet.settings.stubs(:optparse_addargs).returns([]) end it "should pass the banner to the option parser" do option_parser = stub "option parser" option_parser.stubs(:on) option_parser.stubs(:parse!) @app.class.instance_eval do banner "banner" end OptionParser.expects(:new).with("banner").returns(option_parser) @app.parse_options end it "should get options from Puppet.settings.optparse_addargs" do Puppet.settings.expects(:optparse_addargs).returns([]) @app.parse_options end it "should add Puppet.settings options to OptionParser" do Puppet.settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option"]]) Puppet.settings.expects(:handlearg).with("--option", 'true') @app.command_line.stubs(:args).returns(["--option"]) @app.parse_options end it "should ask OptionParser to parse the command-line argument" do @app.command_line.stubs(:args).returns(%w{ fake args }) OptionParser.any_instance.expects(:parse!).with(%w{ fake args }) @app.parse_options end describe "when using --help" do it "should call exit" do @app.expects(:exit) @app.stubs(:puts) @app.handle_help(nil) end end describe "when using --version" do it "should declare a version option" do @app.should respond_to(:handle_version) end it "should exit after printing the version" do @app.stubs(:puts) lambda { @app.handle_version(nil) }.should raise_error(SystemExit) end end describe "when dealing with an argument not declared directly by the application" do it "should pass it to handle_unknown if this method exists" do Puppet.settings.stubs(:optparse_addargs).returns([["--not-handled", :REQUIRED]]) @app.expects(:handle_unknown).with("--not-handled", "value").returns(true) @app.command_line.stubs(:args).returns(["--not-handled", "value"]) @app.parse_options end it "should pass it to Puppet.settings if handle_unknown says so" do Puppet.settings.stubs(:optparse_addargs).returns([["--topuppet", :REQUIRED]]) @app.stubs(:handle_unknown).with("--topuppet", "value").returns(false) Puppet.settings.expects(:handlearg).with("--topuppet", "value") @app.command_line.stubs(:args).returns(["--topuppet", "value"]) @app.parse_options end it "should pass it to Puppet.settings if there is no handle_unknown method" do Puppet.settings.stubs(:optparse_addargs).returns([["--topuppet", :REQUIRED]]) @app.stubs(:respond_to?).returns(false) Puppet.settings.expects(:handlearg).with("--topuppet", "value") @app.command_line.stubs(:args).returns(["--topuppet", "value"]) @app.parse_options end it "should transform boolean false value to string for Puppet.settings" do Puppet.settings.expects(:handlearg).with("--option", "false") @app.handlearg("--option", false) end it "should transform boolean true value to string for Puppet.settings" do Puppet.settings.expects(:handlearg).with("--option", "true") @app.handlearg("--option", true) end it "should transform boolean option to normal form for Puppet.settings" do Puppet.settings.expects(:handlearg).with("--option", "true") @app.handlearg("--[no-]option", true) end it "should transform boolean option to no- form for Puppet.settings" do Puppet.settings.expects(:handlearg).with("--no-option", "false") @app.handlearg("--[no-]option", false) end end - - it "should exit if OptionParser raises an error" do - $stderr.stubs(:puts) - OptionParser.any_instance.stubs(:parse!).raises(OptionParser::ParseError.new("blah blah")) - - @app.expects(:exit) - - lambda { @app.parse_options }.should_not raise_error - end - end describe "when calling default setup" do before :each do @app.stubs(:should_parse_config?).returns(false) @app.options.stubs(:[]) end [ :debug, :verbose ].each do |level| it "should honor option #{level}" do @app.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.expects(:level=).with(level == :verbose ? :info : :debug) @app.setup end end it "should honor setdest option" do @app.options.stubs(:[]).with(:setdest).returns(false) Puppet::Util::Log.expects(:newdestination).with(:syslog) @app.setup end end + describe "when configuring routes" do + include PuppetSpec::Files + + before :each do + Puppet::Node.indirection.reset_terminus_class + end + + after :each do + Puppet::Node.indirection.reset_terminus_class + end + + it "should use the routes specified for only the active application" do + Puppet[:route_file] = tmpfile('routes') + File.open(Puppet[:route_file], 'w') do |f| + f.print <<-ROUTES + test_app: + node: + terminus: exec + other_app: + node: + terminus: plain + catalog: + terminus: invalid + ROUTES + end + + @app.configure_indirector_routes + + Puppet::Node.indirection.terminus_class.should == 'exec' + end + + it "should not fail if the route file doesn't exist" do + Puppet[:route_file] = "/dev/null/non-existent" + + expect { @app.configure_indirector_routes }.should_not raise_error + end + + it "should raise an error if the routes file is invalid" do + Puppet[:route_file] = tmpfile('routes') + File.open(Puppet[:route_file], 'w') do |f| + f.print <<-ROUTES + invalid : : yaml + ROUTES + end + + expect { @app.configure_indirector_routes }.should raise_error + end + end + describe "when running" do before :each do @app.stubs(:preinit) @app.stubs(:setup) @app.stubs(:parse_options) end it "should call preinit" do @app.stubs(:run_command) @app.expects(:preinit) @app.run end it "should call parse_options" do @app.stubs(:run_command) @app.expects(:parse_options) @app.run end it "should call run_command" do @app.expects(:run_command) @app.run end it "should parse Puppet configuration if should_parse_config is called" do @app.stubs(:run_command) @app.class.should_parse_config Puppet.settings.expects(:parse) @app.run end it "should not parse_option if should_not_parse_config is called" do @app.stubs(:run_command) @app.class.should_not_parse_config Puppet.settings.expects(:parse).never @app.run end it "should parse Puppet configuration if needed" do @app.stubs(:run_command) @app.stubs(:should_parse_config?).returns(true) Puppet.settings.expects(:parse) @app.run end it "should call run_command" do @app.expects(:run_command) @app.run end it "should call main as the default command" do @app.expects(:main) @app.run end it "should warn and exit if no command can be called" do $stderr.expects(:puts) @app.expects(:exit).with(1) @app.run end it "should raise an error if dispatch returns no command" do @app.stubs(:get_command).returns(nil) $stderr.expects(:puts) @app.expects(:exit).with(1) @app.run end it "should raise an error if dispatch returns an invalid command" do @app.stubs(:get_command).returns(:this_function_doesnt_exist) $stderr.expects(:puts) @app.expects(:exit).with(1) @app.run end end describe "when metaprogramming" do describe "when calling option" do it "should create a new method named after the option" do @app.class.option("--test1","-t") do end @app.should respond_to(:handle_test1) end it "should transpose in option name any '-' into '_'" do @app.class.option("--test-dashes-again","-t") do end @app.should respond_to(:handle_test_dashes_again) end it "should create a new method called handle_test2 with option(\"--[no-]test2\")" do @app.class.option("--[no-]test2","-t") do end @app.should respond_to(:handle_test2) end describe "when a block is passed" do it "should create a new method with it" do @app.class.option("--[no-]test2","-t") do raise "I can't believe it, it works!" end lambda { @app.handle_test2 }.should raise_error end it "should declare the option to OptionParser" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.expects(:on).with { |*arg| arg[0] == "--[no-]test3" } @app.class.option("--[no-]test3","-t") do end @app.parse_options end it "should pass a block that calls our defined method" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.stubs(:on).with('--test4','-t').yields(nil) @app.expects(:send).with(:handle_test4, nil) @app.class.option("--test4","-t") do end @app.parse_options end end describe "when no block is given" do it "should declare the option to OptionParser" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.expects(:on).with("--test4","-t") @app.class.option("--test4","-t") @app.parse_options end it "should give to OptionParser a block that adds the the value to the options array" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.stubs(:on).with("--test4","-t").yields(nil) @app.options.expects(:[]=).with(:test4,nil) @app.class.option("--test4","-t") @app.parse_options end end end end end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index 266a96b03..17b285d53 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -1,200 +1,199 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/configurer/downloader' describe Puppet::Configurer::Downloader do require 'puppet_spec/files' include PuppetSpec::Files it "should require a name" do lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) end it "should require a path and a source at initialization" do lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) end it "should set the name, path and source appropriately" do dler = Puppet::Configurer::Downloader.new("facts", "path", "source") dler.name.should == "facts" dler.path.should == "path" dler.source.should == "source" end it "should be able to provide a timeout value" do Puppet::Configurer::Downloader.should respond_to(:timeout) end it "should use the configtimeout, converted to an integer, as its timeout" do Puppet.settings.expects(:value).with(:configtimeout).returns "50" Puppet::Configurer::Downloader.timeout.should == 50 end describe "when creating the file that does the downloading" do before do @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") end it "should create a file instance with the right path and source" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:path] == "path" and opts[:source] == "source" } @dler.file end it "should tag the file with the downloader name" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" } @dler.file end it "should always recurse" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true } @dler.file end it "should always purge" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true } @dler.file end it "should never be in noop" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false } @dler.file end it "should always set the owner to the current UID" do Process.expects(:uid).returns 51 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } @dler.file end it "should always set the group to the current GID" do Process.expects(:gid).returns 61 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } @dler.file end it "should always force the download" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true } @dler.file end it "should never back up when downloading" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false } @dler.file end it "should support providing an 'ignore' parameter" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == [".svn"] } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn") @dler.file end it "should split the 'ignore' parameter on whitespace" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == %w{.svn CVS} } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn CVS") @dler.file end end describe "when creating the catalog to do the downloading" do before do @dler = Puppet::Configurer::Downloader.new("foo", "/download/path", "source") end it "should create a catalog and add the file to it" do catalog = @dler.catalog catalog.resources.size.should == 1 catalog.resources.first.class.should == Puppet::Type::File catalog.resources.first.name.should == "/download/path" end it "should specify that it is not managing a host catalog" do @dler.catalog.host_config.should == false end end describe "when downloading" do before do @dl_name = tmpfile("downloadpath") source_name = tmpfile("source") File.open(source_name, 'w') {|f| f.write('hola mundo') } @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name) end it "should not skip downloaded resources when filtering on tags" do Puppet[:tags] = 'maytag' @dler.evaluate File.exists?(@dl_name).should be_true end it "should log that it is downloading" do Puppet.expects(:info) Timeout.stubs(:timeout) @dler.evaluate end it "should set a timeout for the download" do Puppet::Configurer::Downloader.expects(:timeout).returns 50 Timeout.expects(:timeout).with(50) @dler.evaluate end it "should apply the catalog within the timeout block" do catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) Timeout.expects(:timeout).yields catalog.expects(:apply) @dler.evaluate end it "should return all changed file paths" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) @dler.evaluate.should == %w{/changed/file} end it "should yield the resources if a block is given" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) yielded = nil @dler.evaluate { |r| yielded = r } yielded.should == resource end it "should catch and log exceptions" do Puppet.expects(:err) Timeout.stubs(:timeout).raises(Puppet::Error, "testing") lambda { @dler.evaluate }.should_not raise_error end end end diff --git a/spec/unit/configurer/fact_handler_spec.rb b/spec/unit/configurer/fact_handler_spec.rb index 71645225f..70d9b17c0 100755 --- a/spec/unit/configurer/fact_handler_spec.rb +++ b/spec/unit/configurer/fact_handler_spec.rb @@ -1,164 +1,163 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/configurer' require 'puppet/configurer/fact_handler' class FactHandlerTester include Puppet::Configurer::FactHandler end describe Puppet::Configurer::FactHandler do before do @facthandler = FactHandlerTester.new end it "should have a method for downloading fact plugins" do @facthandler.should respond_to(:download_fact_plugins) end it "should have a boolean method for determining whether fact plugins should be downloaded" do @facthandler.should respond_to(:download_fact_plugins?) end it "should download fact plugins when :factsync is true" do Puppet.settings.expects(:value).with(:factsync).returns true @facthandler.should be_download_fact_plugins end it "should not download fact plugins when :factsync is false" do Puppet.settings.expects(:value).with(:factsync).returns false @facthandler.should_not be_download_fact_plugins end it "should not download fact plugins when downloading is disabled" do Puppet::Configurer::Downloader.expects(:new).never @facthandler.expects(:download_fact_plugins?).returns false @facthandler.download_fact_plugins end it "should use an Agent Downloader, with the name, source, destination, and ignore set correctly, to download fact plugins when downloading is enabled" do downloader = mock 'downloader' Puppet.settings.expects(:value).with(:factsource).returns "fsource" Puppet.settings.expects(:value).with(:factdest).returns "fdest" Puppet.settings.expects(:value).with(:factsignore).returns "fignore" Puppet::Configurer::Downloader.expects(:new).with("fact", "fdest", "fsource", "fignore").returns downloader downloader.expects(:evaluate) @facthandler.expects(:download_fact_plugins?).returns true @facthandler.download_fact_plugins end it "should warn about factsync deprecation when factsync is enabled" do Puppet::Configurer::Downloader.stubs(:new).returns mock("downloader", :evaluate => nil) @facthandler.expects(:download_fact_plugins?).returns true Puppet.expects(:warning) @facthandler.download_fact_plugins end it "should have a method for retrieving facts" do @facthandler.should respond_to(:find_facts) end it "should use the Facts class with the :certname to find the facts" do Puppet.settings.expects(:value).with(:certname).returns "foo" Puppet::Node::Facts.indirection.expects(:find).with("foo").returns "myfacts" @facthandler.stubs(:reload_facter) @facthandler.find_facts.should == "myfacts" end it "should reload Facter and find local facts when asked to find facts" do @facthandler.expects(:reload_facter) Puppet.settings.expects(:value).with(:certname).returns "myhost" Puppet::Node::Facts.indirection.expects(:find).with("myhost") @facthandler.find_facts end it "should fail if finding facts fails" do @facthandler.stubs(:reload_facter) Puppet.settings.stubs(:value).with(:trace).returns false Puppet.settings.stubs(:value).with(:certname).returns "myhost" Puppet::Node::Facts.indirection.expects(:find).raises RuntimeError lambda { @facthandler.find_facts }.should raise_error(Puppet::Error) end it "should have a method to prepare the facts for uploading" do @facthandler.should respond_to(:facts_for_uploading) end # I couldn't get marshal to work for this, only yaml, so we hard-code yaml. it "should serialize and CGI escape the fact values for uploading" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns true facts.expects(:render).returns "my text" text = CGI.escape("my text") @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} end it "should properly accept facts containing a '+'" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns true facts.expects(:render).returns "my+text" text = "my%2Btext" @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} end it "use compressed yaml as the serialization if zlib is supported" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns true facts.expects(:render).with(:b64_zlib_yaml).returns "my text" text = CGI.escape("my text") @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading end it "should use yaml as the serialization if zlib is not supported" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns false facts.expects(:render).with(:yaml).returns "my text" text = CGI.escape("my text") @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading end describe "when reloading Facter" do before do Facter.stubs(:clear) Facter.stubs(:load) Facter.stubs(:loadfacts) end it "should clear Facter" do Facter.expects(:clear) @facthandler.reload_facter end it "should load all Facter facts" do Facter.expects(:loadfacts) @facthandler.reload_facter end it "should use the Facter terminus load all Puppet Fact plugins" do Puppet::Node::Facts::Facter.expects(:load_fact_plugins) @facthandler.reload_facter end end end diff --git a/spec/unit/configurer/plugin_handler_spec.rb b/spec/unit/configurer/plugin_handler_spec.rb index 04a479665..7d99960df 100755 --- a/spec/unit/configurer/plugin_handler_spec.rb +++ b/spec/unit/configurer/plugin_handler_spec.rb @@ -1,116 +1,115 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/configurer' require 'puppet/configurer/plugin_handler' class PluginHandlerTester include Puppet::Configurer::PluginHandler end describe Puppet::Configurer::PluginHandler do before do @pluginhandler = PluginHandlerTester.new # PluginHandler#load_plugin has an extra-strong rescue clause # this mock is to make sure that we don't silently ignore errors Puppet.expects(:err).never end it "should have a method for downloading plugins" do @pluginhandler.should respond_to(:download_plugins) end it "should have a boolean method for determining whether plugins should be downloaded" do @pluginhandler.should respond_to(:download_plugins?) end it "should download plugins when :pluginsync is true" do Puppet.settings.expects(:value).with(:pluginsync).returns true @pluginhandler.should be_download_plugins end it "should not download plugins when :pluginsync is false" do Puppet.settings.expects(:value).with(:pluginsync).returns false @pluginhandler.should_not be_download_plugins end it "should not download plugins when downloading is disabled" do Puppet::Configurer::Downloader.expects(:new).never @pluginhandler.expects(:download_plugins?).returns false @pluginhandler.download_plugins end it "should use an Agent Downloader, with the name, source, destination, and ignore set correctly, to download plugins when downloading is enabled" do downloader = mock 'downloader' Puppet.settings.expects(:value).with(:pluginsource).returns "psource" Puppet.settings.expects(:value).with(:plugindest).returns "pdest" Puppet.settings.expects(:value).with(:pluginsignore).returns "pignore" Puppet::Configurer::Downloader.expects(:new).with("plugin", "pdest", "psource", "pignore").returns downloader downloader.expects(:evaluate).returns [] @pluginhandler.expects(:download_plugins?).returns true @pluginhandler.download_plugins end it "should be able to load plugins" do @pluginhandler.should respond_to(:load_plugin) end it "should load each downloaded file" do FileTest.stubs(:exist?).returns true downloader = mock 'downloader' Puppet::Configurer::Downloader.expects(:new).returns downloader downloader.expects(:evaluate).returns %w{one two} @pluginhandler.expects(:download_plugins?).returns true @pluginhandler.expects(:load_plugin).with("one") @pluginhandler.expects(:load_plugin).with("two") @pluginhandler.download_plugins end it "should load plugins when asked to do so" do FileTest.stubs(:exist?).returns true @pluginhandler.expects(:load).with("foo") @pluginhandler.load_plugin("foo") end it "should not try to load files that don't exist" do FileTest.expects(:exist?).with("foo").returns false @pluginhandler.expects(:load).never @pluginhandler.load_plugin("foo") end it "should not try to load directories" do FileTest.stubs(:exist?).returns true FileTest.expects(:directory?).with("foo").returns true @pluginhandler.expects(:load).never @pluginhandler.load_plugin("foo") end it "should warn but not fail if loading a file raises an exception" do FileTest.stubs(:exist?).returns true @pluginhandler.expects(:load).with("foo").raises "eh" Puppet.expects(:err) @pluginhandler.load_plugin("foo") end it "should warn but not fail if loading a file raises a LoadError" do FileTest.stubs(:exist?).returns true @pluginhandler.expects(:load).with("foo").raises LoadError.new("eh") Puppet.expects(:err) @pluginhandler.load_plugin("foo") end end diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb index d21d86ecf..7b76c3221 100755 --- a/spec/unit/configurer_spec.rb +++ b/spec/unit/configurer_spec.rb @@ -1,526 +1,526 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-12. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet/configurer' describe Puppet::Configurer do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new end it "should include the Plugin Handler module" do Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::PluginHandler) end it "should include the Fact Handler module" do Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::FactHandler) end it "should use the puppetdlockfile as its lockfile path" do Puppet.settings.expects(:value).with(:puppetdlockfile).returns("/my/lock") Puppet::Configurer.lockfile_path.should == "/my/lock" end describe "when executing a pre-run hook" do it "should do nothing if the hook is set to an empty string" do Puppet.settings[:prerun_command] = "" Puppet::Util.expects(:exec).never @agent.execute_prerun_command end it "should execute any pre-run command provided via the 'prerun_command' setting" do Puppet.settings[:prerun_command] = "/my/command" Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } @agent.execute_prerun_command end it "should fail if the command fails" do Puppet.settings[:prerun_command] = "/my/command" Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure lambda { @agent.execute_prerun_command }.should raise_error(Puppet::Configurer::CommandHookError) end end describe "when executing a post-run hook" do it "should do nothing if the hook is set to an empty string" do Puppet.settings[:postrun_command] = "" Puppet::Util.expects(:exec).never @agent.execute_postrun_command end it "should execute any post-run command provided via the 'postrun_command' setting" do Puppet.settings[:postrun_command] = "/my/command" Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } @agent.execute_postrun_command end it "should fail if the command fails" do Puppet.settings[:postrun_command] = "/my/command" Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure lambda { @agent.execute_postrun_command }.should raise_error(Puppet::Configurer::CommandHookError) end end end describe Puppet::Configurer, "when executing a catalog run" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:prepare) @agent.stubs(:facts_for_uploading).returns({}) @catalog = Puppet::Resource::Catalog.new @catalog.stubs(:apply) @agent.stubs(:retrieve_catalog).returns @catalog @agent.stubs(:save_last_run_summary) Puppet::Transaction::Report.indirection.stubs(:save) end it "should prepare for the run" do @agent.expects(:prepare) @agent.run end it "should initialize a transaction report if one is not provided" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).at_least_once.returns report @agent.run end it "should pass the new report to the catalog" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.stubs(:new).returns report @catalog.expects(:apply).with{|options| options[:report] == report} @agent.run end it "should use the provided report if it was passed one" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).never @catalog.expects(:apply).with{|options| options[:report] == report} @agent.run(:report => report) end it "should set the report as a log destination" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns report @agent.stubs(:send_report) Puppet::Util::Log.expects(:newdestination).with(report) @agent.run end it "should retrieve the catalog" do @agent.expects(:retrieve_catalog) @agent.run end it "should log a failure and do nothing if no catalog can be retrieved" do @agent.expects(:retrieve_catalog).returns nil Puppet.expects(:err).with "Could not retrieve catalog; skipping run" @agent.run end it "should apply the catalog with all options to :run" do @agent.expects(:retrieve_catalog).returns @catalog @catalog.expects(:apply).with { |args| args[:one] == true } @agent.run :one => true end it "should accept a catalog and use it instead of retrieving a different one" do @agent.expects(:retrieve_catalog).never @catalog.expects(:apply) @agent.run :one => true, :catalog => @catalog end it "should benchmark how long it takes to apply the catalog" do @agent.expects(:benchmark).with(:notice, "Finished catalog run") @agent.expects(:retrieve_catalog).returns @catalog @catalog.expects(:apply).never # because we're not yielding @agent.run end it "should execute post-run hooks after the run" do @agent.expects(:execute_postrun_command) @agent.run end it "should send the report" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report).with { |r, trans| r == report } @agent.run end it "should send the transaction report with a reference to the transaction if a run was actually made" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) trans = stub 'transaction' @catalog.expects(:apply).returns trans @agent.expects(:send_report).with { |r, t| t == trans } @agent.run :catalog => @catalog end it "should send the transaction report even if the catalog could not be retrieved" do @agent.expects(:retrieve_catalog).returns nil report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report) @agent.run end it "should send the transaction report even if there is a failure" do @agent.expects(:retrieve_catalog).raises "whatever" report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report) lambda { @agent.run }.should raise_error end it "should remove the report as a log destination when the run is finished" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) report.expects(:<<).at_least_once @agent.run Puppet::Util::Log.destinations.should_not include(report) end it "should return the report as the result of the run" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.run.should equal(report) end end describe Puppet::Configurer, "when sending a report" do include PuppetSpec::Files before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new Puppet[:lastrunfile] = tmpfile('last_run_file') @report = Puppet::Transaction::Report.new("apply") @trans = stub 'transaction' end it "should finalize the report" do @report.expects(:finalize_report) @configurer.send_report(@report, @trans) end it "should print a report summary if configured to do so" do Puppet.settings[:summarize] = true @report.expects(:summary).returns "stuff" @configurer.expects(:puts).with("stuff") @configurer.send_report(@report, nil) end it "should not print a report summary if not configured to do so" do Puppet.settings[:summarize] = false @configurer.expects(:puts).never @configurer.send_report(@report, nil) end it "should save the report if reporting is enabled" do Puppet.settings[:report] = true Puppet::Transaction::Report.indirection.expects(:save).with(@report) @configurer.send_report(@report, nil) end it "should not save the report if reporting is disabled" do Puppet.settings[:report] = false Puppet::Transaction::Report.indirection.expects(:save).never @configurer.send_report(@report, nil) end it "should save the last run summary if reporting is enabled" do Puppet.settings[:report] = true @configurer.expects(:save_last_run_summary).with(@report) @configurer.send_report(@report, nil) end it "should save the last run summary if reporting is disabled" do Puppet.settings[:report] = false @configurer.expects(:save_last_run_summary).with(@report) @configurer.send_report(@report, nil) end it "should log but not fail if saving the report fails" do Puppet.settings[:report] = true Puppet::Transaction::Report.indirection.expects(:save).with(@report).raises "whatever" Puppet.expects(:err) lambda { @configurer.send_report(@report, nil) }.should_not raise_error end end describe Puppet::Configurer, "when saving the summary report file" do before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new @report = stub 'report' @trans = stub 'transaction' @lastrunfd = stub 'lastrunfd' Puppet::Util::FileLocking.stubs(:writelock).yields(@lastrunfd) end it "should write the raw summary to the lastrunfile setting value" do Puppet::Util::FileLocking.expects(:writelock).with(Puppet[:lastrunfile], 0660) @configurer.save_last_run_summary(@report) end it "should write the raw summary as yaml" do @report.expects(:raw_summary).returns("summary") @lastrunfd.expects(:print).with(YAML.dump("summary")) @configurer.save_last_run_summary(@report) end it "should log but not fail if saving the last run summary fails" do Puppet::Util::FileLocking.expects(:writelock).raises "exception" Puppet.expects(:err) lambda { @configurer.save_last_run_summary(@report) }.should_not raise_error end end describe Puppet::Configurer, "when retrieving a catalog" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:facts_for_uploading).returns({}) @catalog = Puppet::Resource::Catalog.new # this is the default when using a Configurer instance Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :rest @agent.stubs(:convert_catalog).returns @catalog end describe "and configured to only retrieve a catalog from the cache" do before do Puppet.settings[:use_cached_catalog] = true end it "should first look in the cache for a catalog" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.never @agent.retrieve_catalog.should == @catalog end it "should compile a new catalog if none is found in the cache" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog @agent.retrieve_catalog.should == @catalog end end describe "when not using a REST terminus for catalogs" do it "should not pass any facts when retrieving the catalog" do @agent.expects(:facts_for_uploading).never Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:facts].nil? }.returns @catalog @agent.retrieve_catalog end end describe "when using a REST terminus for catalogs" do it "should pass the prepared facts and the facts format as arguments when retrieving the catalog" do @agent.expects(:facts_for_uploading).returns(:facts => "myfacts", :facts_format => :foo) Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:facts] == "myfacts" and options[:facts_format] == :foo }.returns @catalog @agent.retrieve_catalog end end it "should use the Catalog class to get its catalog" do Puppet::Resource::Catalog.indirection.expects(:find).returns @catalog @agent.retrieve_catalog end it "should use its certname to retrieve the catalog" do Facter.stubs(:value).returns "eh" Puppet.settings[:certname] = "myhost.domain.com" Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog @agent.retrieve_catalog end it "should default to returning a catalog retrieved directly from the server, skipping the cache" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog @agent.retrieve_catalog.should == @catalog end it "should log and return the cached catalog when no catalog can be retrieved from the server" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog Puppet.expects(:notice) @agent.retrieve_catalog.should == @catalog end it "should not look in the cache for a catalog if one is returned from the server" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never @agent.retrieve_catalog.should == @catalog end it "should return the cached catalog when retrieving the remote catalog throws an exception" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh" Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog @agent.retrieve_catalog.should == @catalog end it "should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled" do Puppet.stubs(:[]) Puppet.expects(:[]).with(:usecacheonfailure).returns false Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet.expects(:warning) @agent.retrieve_catalog.should be_nil end it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil @agent.retrieve_catalog.should be_nil end it "should convert the catalog before returning" do Puppet::Resource::Catalog.indirection.stubs(:find).returns @catalog @agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog" @agent.retrieve_catalog.should == "converted catalog" end it "should return nil if there is an error while retrieving the catalog" do Puppet::Resource::Catalog.indirection.expects(:find).at_least_once.raises "eh" @agent.retrieve_catalog.should be_nil end end describe Puppet::Configurer, "when converting the catalog" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @catalog = Puppet::Resource::Catalog.new @oldcatalog = stub 'old_catalog', :to_ral => @catalog end it "should convert the catalog to a RAL-formed catalog" do @oldcatalog.expects(:to_ral).returns @catalog @agent.convert_catalog(@oldcatalog, 10).should equal(@catalog) end it "should finalize the catalog" do @catalog.expects(:finalize) @agent.convert_catalog(@oldcatalog, 10) end it "should record the passed retrieval time with the RAL catalog" do @catalog.expects(:retrieval_duration=).with 10 @agent.convert_catalog(@oldcatalog, 10) end it "should write the RAL catalog's class file" do @catalog.expects(:write_class_file) @agent.convert_catalog(@oldcatalog, 10) end end describe Puppet::Configurer, "when preparing for a run" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:dostorage) @agent.stubs(:download_fact_plugins) @agent.stubs(:download_plugins) @agent.stubs(:execute_prerun_command) @facts = {"one" => "two", "three" => "four"} end it "should initialize the metadata store" do @agent.class.stubs(:facts).returns(@facts) @agent.expects(:dostorage) @agent.prepare({}) end it "should download fact plugins" do @agent.expects(:download_fact_plugins) @agent.prepare({}) end it "should download plugins" do @agent.expects(:download_plugins) @agent.prepare({}) end it "should perform the pre-run commands" do @agent.expects(:execute_prerun_command) @agent.prepare({}) end end diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb index 4de76b728..ed8dec2a3 100755 --- a/spec/unit/daemon_spec.rb +++ b/spec/unit/daemon_spec.rb @@ -1,296 +1,295 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/daemon' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Daemon do before do @daemon = Puppet::Daemon.new end it "should be able to manage an agent" do @daemon.should respond_to(:agent) end it "should be able to manage a network server" do @daemon.should respond_to(:server) end it "should reopen the Log logs when told to reopen logs" do Puppet::Util::Log.expects(:reopen) @daemon.reopen_logs end describe "when setting signal traps" do {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| it "should log and call #{method} when it receives #{signal}" do Signal.expects(:trap).with(signal).yields Puppet.expects(:notice) @daemon.expects(method) @daemon.set_signal_traps end end end describe "when starting" do before do @daemon.stubs(:create_pidfile) @daemon.stubs(:set_signal_traps) EventLoop.current.stubs(:run) end it "should fail if it has neither agent nor server" do lambda { @daemon.start }.should raise_error(Puppet::DevError) end it "should create its pidfile" do @daemon.stubs(:agent).returns stub('agent', :start => nil) @daemon.expects(:create_pidfile) @daemon.start end it "should start the agent if the agent is configured" do agent = mock 'agent' agent.expects(:start) @daemon.stubs(:agent).returns agent @daemon.start end it "should start its server if one is configured" do server = mock 'server' server.expects(:start) @daemon.stubs(:server).returns server @daemon.start end it "should let the current EventLoop run" do @daemon.stubs(:agent).returns stub('agent', :start => nil) EventLoop.current.expects(:run) @daemon.start end end describe "when stopping" do before do @daemon.stubs(:remove_pidfile) @daemon.stubs(:exit) Puppet::Util::Log.stubs(:close_all) # to make the global safe to mock, set it to a subclass of itself, # then restore it in an after pass without_warnings { Puppet::Application = Class.new(Puppet::Application) } end after do # restore from the superclass so we lose the stub garbage without_warnings { Puppet::Application = Puppet::Application.superclass } end it "should stop its server if one is configured" do server = mock 'server' server.expects(:stop) @daemon.stubs(:server).returns server @daemon.stop end it 'should request a stop from Puppet::Application' do Puppet::Application.expects(:stop!) @daemon.stop end it "should remove its pidfile" do @daemon.expects(:remove_pidfile) @daemon.stop end it "should close all logs" do Puppet::Util::Log.expects(:close_all) @daemon.stop end it "should exit unless called with ':exit => false'" do @daemon.expects(:exit) @daemon.stop end it "should not exit if called with ':exit => false'" do @daemon.expects(:exit).never @daemon.stop :exit => false end end describe "when creating its pidfile" do it "should use an exclusive mutex" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @daemon.create_pidfile end it "should lock the pidfile using the Pidlock class" do pidfile = mock 'pidfile' Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns true @daemon.create_pidfile end it "should fail if it cannot lock" do pidfile = mock 'pidfile' Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns false lambda { @daemon.create_pidfile }.should raise_error end end describe "when removing its pidfile" do it "should use an exclusive mutex" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @daemon.remove_pidfile end it "should do nothing if the pidfile is not present" do pidfile = mock 'pidfile', :locked? => false Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" pidfile.expects(:unlock).never @daemon.remove_pidfile end it "should unlock the pidfile using the Pidlock class" do pidfile = mock 'pidfile', :locked? => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:unlock).returns true Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" @daemon.remove_pidfile end it "should warn if it cannot remove the pidfile" do pidfile = mock 'pidfile', :locked? => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:unlock).returns false Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet.expects :err @daemon.remove_pidfile end end describe "when reloading" do it "should do nothing if no agent is configured" do @daemon.reload end it "should do nothing if the agent is running" do agent = mock 'agent' agent.expects(:running?).returns true @daemon.stubs(:agent).returns agent @daemon.reload end it "should run the agent if one is available and it is not running" do agent = mock 'agent' agent.expects(:running?).returns false agent.expects :run @daemon.stubs(:agent).returns agent @daemon.reload end end describe "when restarting" do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should set Puppet::Application.restart!' do Puppet::Application.expects(:restart!) @daemon.stubs(:reexec) @daemon.restart end it "should reexec itself if no agent is available" do @daemon.expects(:reexec) @daemon.restart end it "should reexec itself if the agent is not running" do agent = mock 'agent' agent.expects(:running?).returns false @daemon.stubs(:agent).returns agent @daemon.expects(:reexec) @daemon.restart end end describe "when reexecing it self" do before do @daemon.stubs(:exec) @daemon.stubs(:stop) end it "should fail if no argv values are available" do @daemon.expects(:argv).returns nil lambda { @daemon.reexec }.should raise_error(Puppet::DevError) end it "should shut down without exiting" do @daemon.argv = %w{foo} @daemon.expects(:stop).with(:exit => false) @daemon.reexec end it "should call 'exec' with the original executable and arguments" do @daemon.argv = %w{foo} @daemon.expects(:exec).with($0 + " foo") @daemon.reexec end end end diff --git a/spec/unit/dsl/resource_api_spec.rb b/spec/unit/dsl/resource_api_spec.rb index 13aa6a437..559a43333 100755 --- a/spec/unit/dsl/resource_api_spec.rb +++ b/spec/unit/dsl/resource_api_spec.rb @@ -1,181 +1,180 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/dsl/resource_api' describe Puppet::DSL::ResourceAPI do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => "foo") @resource = Puppet::Parser::Resource.new(:mytype, "myresource", :scope => @scope) @api = Puppet::DSL::ResourceAPI.new(@resource, @scope, proc { }) end it "should include the resource type collection helper" do Puppet::DSL::ResourceAPI.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should use the scope's environment as its environment" do @scope.expects(:environment).returns "myenv" @api.environment.should == "myenv" end it "should be able to set all of its parameters as instance variables" do @resource["foo"] = "myval" @api.set_instance_variables @api.instance_variable_get("@foo").should == "myval" end describe "when calling a function" do it "should return false if the function does not exist" do Puppet::Parser::Functions.expects(:function).with("myfunc").returns nil @api.call_function("myfunc", "foo").should be_false end it "should use the scope the call the provided function with the provided arguments and return the results" do scope = stub 'scope' @api.stubs(:scope).returns scope Puppet::Parser::Functions.expects(:function).with("myfunc").returns "myfunc_method" scope.expects(:myfunc_method).with("one", "two") @api.call_function("myfunc", ["one", "two"]) end it "should call 'include' when asked to call 'acquire'" do scope = stub 'scope' @api.stubs(:scope).returns scope @api.stubs(:valid_type?).returns false scope.expects(:function_include).with("one", "two") @api.acquire("one", "two") end end describe "when determining if a provided name is a valid type" do it "should be valid if it's :class" do @api.should be_valid_type(:class) end it "should be valid if it's :node" do @api.should be_valid_type(:node) end it "should be valid if it's a builtin type" do Puppet::Type.expects(:type).with(:mytype).returns "whatever" @api.should be_valid_type(:mytype) end it "should be valid if it's a defined resource type in the environment's known resource types" do collection = stub 'collection' @api.stubs(:known_resource_types).returns collection collection.expects(:definition).with(:mytype).returns "whatever" @api.should be_valid_type(:mytype) end it "should not be valid unless it's a node, class, builtin type, or defined resource" do collection = stub 'collection' @api.stubs(:known_resource_types).returns collection collection.expects(:definition).returns nil Puppet::Type.expects(:type).returns nil @api.should_not be_valid_type(:mytype) end end describe "when creating a resource" do before do @api.scope.stubs(:source).returns stub("source") @api.scope.compiler.stubs(:add_resource) @created_resource = Puppet::Parser::Resource.new("yay", "eh", :scope => @api.scope) end it "should create and return a resource of the type specified" do Puppet::Parser::Resource.expects(:new).with { |type, title, args| type == "mytype" }.returns @created_resource @api.create_resource("mytype", "myname", {:foo => "bar"}).should == [@created_resource] end it "should use the name from the first element of the provided argument array" do Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "myname" }.returns @created_resource @api.create_resource("mytype", "myname", {:foo => "bar"}) end it "should create multiple resources if the first element of the argument array is an array" do second_resource = Puppet::Parser::Resource.new('yay', "eh", :scope => @api.scope) Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "first" }.returns @created_resource Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "second" }.returns @created_resource @api.create_resource("mytype", ["first", "second"], {:foo => "bar"}) end it "should provide its scope as the scope" do Puppet::Parser::Resource.expects(:new).with { |type, title, args| args[:scope] == @api.scope }.returns @created_resource @api.create_resource("mytype", "myname", {:foo => "bar"}) end it "should set each provided argument as a parameter on the created resource" do result = @api.create_resource("mytype", "myname", {"foo" => "bar", "biz" => "baz"}).shift result["foo"].should == "bar" result["biz"].should == "baz" end it "should add the resource to the scope's copmiler" do Puppet::Parser::Resource.expects(:new).returns @created_resource @api.scope.compiler.expects(:add_resource).with(@api.scope, @created_resource) @api.create_resource("mytype", "myname", {:foo => "bar"}) end it "should fail if the resource parameters are not a hash" do lambda { @api.create_resource("mytype", "myname", %w{foo bar}) }.should raise_error(ArgumentError) end end describe "when an unknown method is called" do it "should create a resource if the method name is a valid type" do @api.expects(:valid_type?).with(:mytype).returns true @api.expects(:create_resource).with(:mytype, "myname", {:foo => "bar"}).returns true @api.mytype("myname", :foo => "bar") end it "should call any function whose name matches the undefined method if the name is not a valid type" do @api.expects(:valid_type?).with(:myfunc).returns false @api.expects(:create_resource).never Puppet::Parser::Functions.expects(:function).with(:myfunc).returns true @api.expects(:call_function).with(:myfunc, %w{foo bar}) @api.myfunc("foo", "bar") end it "should raise a method missing error if the method is neither a type nor a function" do @api.expects(:valid_type?).with(:myfunc).returns false @api.expects(:create_resource).never Puppet::Parser::Functions.expects(:function).with(:myfunc).returns false @api.expects(:call_function).never lambda { @api.myfunc("foo", "bar") }.should raise_error(NoMethodError) end end it "should mark the specified resource as exported when creating a single exported resource" do resources = @api.export @api.file("/my/file", :ensure => :present) resources[0].should be_exported end it "should mark all created resources as exported when creating exported resources using a block" do @compiler.expects(:add_resource).with { |s, res| res.exported == true } @api.export { file "/my/file", :ensure => :present } end it "should mark the specified resource as virtual when creating a single virtual resource" do resources = @api.virtual @api.file("/my/file", :ensure => :present) resources[0].should be_virtual end it "should mark all created resources as virtual when creating virtual resources using a block" do @compiler.expects(:add_resource).with { |s, res| res.virtual == true } @api.virtual { file "/my/file", :ensure => :present } end end diff --git a/spec/unit/dsl/resource_type_api_spec.rb b/spec/unit/dsl/resource_type_api_spec.rb index 795ce2868..ea81f7da4 100755 --- a/spec/unit/dsl/resource_type_api_spec.rb +++ b/spec/unit/dsl/resource_type_api_spec.rb @@ -1,54 +1,53 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/dsl/resource_type_api' describe Puppet::DSL::ResourceTypeAPI do # Verify that the block creates a single AST node through the API, # instantiate that AST node into a types, and return that type. def test_api_call(&block) main_object = Puppet::DSL::ResourceTypeAPI.new main_object.instance_eval(&block) created_ast_objects = main_object.instance_eval { @__created_ast_objects__ } created_ast_objects.length.should == 1 new_types = created_ast_objects[0].instantiate('') new_types.length.should == 1 new_types[0] ensure Thread.current[:ruby_file_parse_result] = nil end [:definition, :node, :hostclass].each do |type| method = type == :definition ? "define" : type it "should be able to create a #{type}" do newtype = test_api_call { send(method, "myname").should == nil } newtype.should be_a(Puppet::Resource::Type) newtype.type.should == type end it "should use the provided name when creating a #{type}" do newtype = test_api_call { send(method, "myname") } newtype.name.should == "myname" end unless type == :definition it "should pass in any provided options when creating a #{type}" do newtype = test_api_call { send(method, "myname", :line => 200) } newtype.line.should == 200 end end it "should set any provided block as the type's ruby code" do newtype = test_api_call { send(method, "myname") { 'method_result' } } newtype.ruby_code.call.should == 'method_result' end end describe "when creating a definition" do it "should use the provided options to define valid arguments for the resource type" do newtype = test_api_call { define("myname", :arg1, :arg2) } newtype.arguments.should == { 'arg1' => nil, 'arg2' => nil } end end end diff --git a/spec/unit/face/catalog_spec.rb b/spec/unit/face/catalog_spec.rb new file mode 100755 index 000000000..28c2aa9be --- /dev/null +++ b/spec/unit/face/catalog_spec.rb @@ -0,0 +1,4 @@ +require 'puppet/face' +describe Puppet::Face[:catalog, '0.0.1'] do + it "should actually have some testing..." +end diff --git a/spec/unit/face/certificate_request_spec.rb b/spec/unit/face/certificate_request_spec.rb new file mode 100755 index 000000000..a83a92df8 --- /dev/null +++ b/spec/unit/face/certificate_request_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:certificate_request, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face/certificate_revocation_list_spec.rb b/spec/unit/face/certificate_revocation_list_spec.rb new file mode 100755 index 000000000..22c0fa2bf --- /dev/null +++ b/spec/unit/face/certificate_revocation_list_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:certificate_revocation_list, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face/certificate_spec.rb b/spec/unit/face/certificate_spec.rb new file mode 100755 index 000000000..dbcc888ad --- /dev/null +++ b/spec/unit/face/certificate_spec.rb @@ -0,0 +1,14 @@ +require 'puppet/ssl/host' + +describe Puppet::Face[:certificate, '0.0.1'] do + it "should have a ca-location option" do + subject.should be_option :ca_location + end + + it "should set the ca location when invoked" do + pending "#6983: This is broken in the actual face..." + Puppet::SSL::Host.expects(:ca_location=).with(:foo) + Puppet::SSL::Host.indirection.expects(:save) + subject.sign :ca_location => :foo + end +end diff --git a/spec/unit/face/config_spec.rb b/spec/unit/face/config_spec.rb new file mode 100755 index 000000000..6004d700f --- /dev/null +++ b/spec/unit/face/config_spec.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe Puppet::Face[:config, '0.0.1'] do + it "should use Settings#print_config_options when asked to print" do + Puppet.settings.stubs(:puts) + Puppet.settings.expects(:print_config_options) + subject.print + end + + it "should set 'configprint' to all desired values and call print_config_options when a specific value is provided" do + Puppet.settings.stubs(:puts) + Puppet.settings.expects(:print_config_options) + subject.print("libdir", "ssldir") + Puppet.settings[:configprint].should == "libdir,ssldir" + end + + it "should always return nil" do + Puppet.settings.stubs(:puts) + Puppet.settings.expects(:print_config_options) + subject.print("libdir").should be_nil + end +end diff --git a/spec/unit/face/configurer_spec.rb b/spec/unit/face/configurer_spec.rb new file mode 100755 index 000000000..56b45031f --- /dev/null +++ b/spec/unit/face/configurer_spec.rb @@ -0,0 +1,25 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/indirector/catalog/rest' +require 'tempfile' + +describe Puppet::Face[:configurer, '0.0.1'] do + describe "#synchronize" do + it "should retrieve and apply a catalog and return a report" do + pending "REVISIT: 2.7 changes broke this, and we want the merge published" + + dirname = Dir.mktmpdir("puppetdir") + Puppet[:vardir] = dirname + Puppet[:confdir] = dirname + @catalog = Puppet::Resource::Catalog.new + @file = Puppet::Resource.new(:file, File.join(dirname, "tmp_dir_resource"), :parameters => {:ensure => :present}) + @catalog.add_resource(@file) + Puppet::Resource::Catalog::Rest.any_instance.stubs(:find).returns(@catalog) + + report = subject.synchronize("foo") + + report.kind.should == "apply" + report.status.should == "changed" + end + end +end diff --git a/spec/unit/face/facts_spec.rb b/spec/unit/face/facts_spec.rb new file mode 100755 index 000000000..e6411f836 --- /dev/null +++ b/spec/unit/face/facts_spec.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe Puppet::Face[:facts, '0.0.1'] do + it "should define an 'upload' fact" do + subject.should be_action(:upload) + end + + it "should set its default format to :yaml" do + subject.default_format.should == :yaml + end + + describe "when uploading" do + it "should set the terminus_class to :facter" + + it "should set the cach_eclass to :rest" + + it "should find the current certname" + end +end diff --git a/spec/unit/face/file_spec.rb b/spec/unit/face/file_spec.rb new file mode 100755 index 000000000..97e8bcc08 --- /dev/null +++ b/spec/unit/face/file_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:file, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face/help_spec.rb b/spec/unit/face/help_spec.rb new file mode 100755 index 000000000..e67f29e07 --- /dev/null +++ b/spec/unit/face/help_spec.rb @@ -0,0 +1,112 @@ +require 'spec_helper' +require 'puppet/face/help' + +describe Puppet::Face[:help, '0.0.1'] do + it "should have a help action" do + subject.should be_action :help + end + + it "should have a default action of help" do + pending "REVISIT: we don't support default actions yet" + end + + it "should accept a call with no arguments" do + expect { subject.help() }.should_not raise_error + end + + it "should accept a face name" do + expect { subject.help(:help) }.should_not raise_error + end + + it "should accept a face and action name" do + expect { subject.help(:help, :help) }.should_not raise_error + end + + it "should fail if more than a face and action are given" do + expect { subject.help(:help, :help, :for_the_love_of_god) }. + should raise_error ArgumentError + end + + it "should treat :current and 'current' identically" do + subject.help(:help, :version => :current).should == + subject.help(:help, :version => 'current') + end + + it "should complain when the request version of a face is missing" do + expect { subject.help(:huzzah, :bar, :version => '17.0.0') }. + should raise_error Puppet::Error + end + + it "should find a face by version" do + face = Puppet::Face[:huzzah, :current] + subject.help(:huzzah, :version => face.version). + should == subject.help(:huzzah, :version => :current) + end + + context "when listing subcommands" do + subject { Puppet::Face[:help, :current].help } + + # Check a precondition for the next block; if this fails you have + # something odd in your set of face, and we skip testing things that + # matter. --daniel 2011-04-10 + it "should have at least one face with a summary" do + Puppet::Face.faces.should be_any do |name| + Puppet::Face[name, :current].summary + end + end + + Puppet::Face.faces.each do |name| + face = Puppet::Face[name, :current] + summary = face.summary + + it { should =~ %r{ #{name} } } + it { should =~ %r{ #{name} +#{summary}} } if summary + end + + Puppet::Face[:help, :current].legacy_applications.each do |appname| + it { should =~ %r{ #{appname} } } + + summary = Puppet::Face[:help, :current].horribly_extract_summary_from(appname) + summary and it { should =~ %r{ #{summary}\b} } + end + end + + context "#legacy_applications" do + subject { Puppet::Face[:help, :current].legacy_applications } + + # If we don't, these tests are ... less than useful, because they assume + # it. When this breaks you should consider ditching the entire feature + # and tests, but if not work out how to fake one. --daniel 2011-04-11 + it { should have_at_least(1).item } + + # Meh. This is nasty, but we can't control the other list; the specific + # bug that caused these to be listed is annoyingly subtle and has a nasty + # fix, so better to have a "fail if you do something daft" trigger in + # place here, I think. --daniel 2011-04-11 + %w{face_base indirection_base}.each do |name| + it { should_not include name } + end + end + + context "help for legacy applications" do + subject { Puppet::Face[:help, :current] } + let :appname do subject.legacy_applications.first end + + # This test is purposely generic, so that as we eliminate legacy commands + # we don't get into a loop where we either test a face-based replacement + # and fail to notice breakage, or where we have to constantly rewrite this + # test and all. --daniel 2011-04-11 + it "should return the legacy help when given the subcommand" do + help = subject.help(appname) + help.should =~ /puppet-#{appname}/ + %w{SYNOPSIS USAGE DESCRIPTION OPTIONS COPYRIGHT}.each do |heading| + help.should =~ /^#{heading}$/ + end + end + + it "should fail when asked for an action on a legacy command" do + expect { subject.help(appname, :whatever) }. + to raise_error ArgumentError, /Legacy subcommands don't take actions/ + end + end +end diff --git a/spec/unit/face/indirector_spec.rb b/spec/unit/face/indirector_spec.rb new file mode 100755 index 000000000..bb06fcfe2 --- /dev/null +++ b/spec/unit/face/indirector_spec.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/face/indirector' + +describe Puppet::Face::Indirector do + subject do + instance = Puppet::Face::Indirector.new(:test, '0.0.1') + indirection = stub('indirection', + :name => :stub_indirection, + :reset_terminus_class => nil) + instance.stubs(:indirection).returns indirection + instance + end + + it "should be able to return a list of indirections" do + Puppet::Face::Indirector.indirections.should be_include("catalog") + end + + it "should be able to return a list of terminuses for a given indirection" do + Puppet::Face::Indirector.terminus_classes(:catalog).should be_include("compiler") + end + + describe "as an instance" do + it "should be able to determine its indirection" do + # Loading actions here an get, um, complicated + Puppet::Face.stubs(:load_actions) + Puppet::Face::Indirector.new(:catalog, '0.0.1').indirection.should equal(Puppet::Resource::Catalog.indirection) + end + end + + [:find, :search, :save, :destroy].each do |method| + it "should define a '#{method}' action" do + Puppet::Face::Indirector.should be_action(method) + end + + it "should call the indirection method with options when the '#{method}' action is invoked" do + subject.indirection.expects(method).with(:test, "myargs", {}) + subject.send(method, :test, "myargs") + end + it "should forward passed options" do + subject.indirection.expects(method).with(:test, "action", {'one'=>'1'}) + subject.send(method, :test, 'action', {'one'=>'1'}) + end + end + + it "should be able to override its indirection name" do + subject.set_indirection_name :foo + subject.indirection_name.should == :foo + end + + it "should be able to set its terminus class" do + subject.indirection.expects(:terminus_class=).with(:myterm) + subject.set_terminus(:myterm) + end + + it "should define a class-level 'info' action" do + Puppet::Face::Indirector.should be_action(:info) + end +end diff --git a/spec/unit/face/key_spec.rb b/spec/unit/face/key_spec.rb new file mode 100755 index 000000000..10d664790 --- /dev/null +++ b/spec/unit/face/key_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:key, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face/node_spec.rb b/spec/unit/face/node_spec.rb new file mode 100755 index 000000000..90d258db9 --- /dev/null +++ b/spec/unit/face/node_spec.rb @@ -0,0 +1,8 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe Puppet::Face[:node, '0.0.1'] do + it "should set its default format to :yaml" do + subject.default_format.should == :yaml + end +end diff --git a/spec/unit/face/report_spec.rb b/spec/unit/face/report_spec.rb new file mode 100755 index 000000000..b1b28167e --- /dev/null +++ b/spec/unit/face/report_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:report, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face/resource_spec.rb b/spec/unit/face/resource_spec.rb new file mode 100755 index 000000000..084e2a6a9 --- /dev/null +++ b/spec/unit/face/resource_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:resource, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face/resource_type_spec.rb b/spec/unit/face/resource_type_spec.rb new file mode 100755 index 000000000..2adaedca1 --- /dev/null +++ b/spec/unit/face/resource_type_spec.rb @@ -0,0 +1,3 @@ +describe Puppet::Face[:resource_type, '0.0.1'] do + it "should actually have some tests..." +end diff --git a/spec/unit/face_spec.rb b/spec/unit/face_spec.rb new file mode 100755 index 000000000..b6c49d917 --- /dev/null +++ b/spec/unit/face_spec.rb @@ -0,0 +1 @@ +# You should look at interface_spec.rb diff --git a/spec/unit/file_bucket/dipper_spec.rb b/spec/unit/file_bucket/dipper_spec.rb index 4dabb722b..910b2808d 100755 --- a/spec/unit/file_bucket/dipper_spec.rb +++ b/spec/unit/file_bucket/dipper_spec.rb @@ -1,114 +1,113 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'pathname' require 'puppet/file_bucket/dipper' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::FileBucket::Dipper do include PuppetSpec::Files def make_tmp_file(contents) file = tmpfile("file_bucket_file") File.open(file, 'w') { |f| f.write(contents) } file end it "should fail in an informative way when there are failures checking for the file on the server" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") file = make_tmp_file('contents') Puppet::FileBucket::File.indirection.expects(:head).raises ArgumentError lambda { @dipper.backup(file) }.should raise_error(Puppet::Error) end it "should fail in an informative way when there are failures backing up to the server" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") file = make_tmp_file('contents') Puppet::FileBucket::File.indirection.expects(:head).returns false Puppet::FileBucket::File.indirection.expects(:save).raises ArgumentError lambda { @dipper.backup(file) }.should raise_error(Puppet::Error) end it "should backup files to a local bucket" do Puppet[:bucketdir] = "/non/existent/directory" file_bucket = tmpdir("bucket") @dipper = Puppet::FileBucket::Dipper.new(:Path => file_bucket) file = make_tmp_file('my contents') checksum = "2975f560750e71c478b8e3b39a956adb" Digest::MD5.hexdigest('my contents').should == checksum @dipper.backup(file).should == checksum File.exists?("#{file_bucket}/2/9/7/5/f/5/6/0/2975f560750e71c478b8e3b39a956adb/contents").should == true end it "should not backup a file that is already in the bucket" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") file = make_tmp_file('my contents') checksum = Digest::MD5.hexdigest('my contents') Puppet::FileBucket::File.indirection.expects(:head).returns true Puppet::FileBucket::File.indirection.expects(:save).never @dipper.backup(file).should == checksum end it "should retrieve files from a local bucket" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") checksum = Digest::MD5.hexdigest('my contents') request = nil Puppet::FileBucketFile::File.any_instance.expects(:find).with{ |r| request = r }.once.returns(Puppet::FileBucket::File.new('my contents')) @dipper.getfile(checksum).should == 'my contents' request.key.should == "md5/#{checksum}" end it "should backup files to a remote server" do @dipper = Puppet::FileBucket::Dipper.new(:Server => "puppetmaster", :Port => "31337") file = make_tmp_file('my contents') checksum = Digest::MD5.hexdigest('my contents') real_path = Pathname.new(file).realpath request1 = nil request2 = nil Puppet::FileBucketFile::Rest.any_instance.expects(:head).with { |r| request1 = r }.once.returns(nil) Puppet::FileBucketFile::Rest.any_instance.expects(:save).with { |r| request2 = r }.once @dipper.backup(file).should == checksum [request1, request2].each do |r| r.server.should == 'puppetmaster' r.port.should == 31337 r.key.should == "md5/#{checksum}#{real_path}" end end it "should retrieve files from a remote server" do @dipper = Puppet::FileBucket::Dipper.new(:Server => "puppetmaster", :Port => "31337") checksum = Digest::MD5.hexdigest('my contents') request = nil Puppet::FileBucketFile::Rest.any_instance.expects(:find).with { |r| request = r }.returns(Puppet::FileBucket::File.new('my contents')) @dipper.getfile(checksum).should == "my contents" request.server.should == 'puppetmaster' request.port.should == 31337 request.key.should == "md5/#{checksum}" end end diff --git a/spec/unit/file_bucket/file_spec.rb b/spec/unit/file_bucket/file_spec.rb old mode 100644 new mode 100755 index d79345736..c4444ae77 --- a/spec/unit/file_bucket/file_spec.rb +++ b/spec/unit/file_bucket/file_spec.rb @@ -1,107 +1,106 @@ -#!/usr/bin/env ruby - -require ::File.dirname(__FILE__) + '/../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_bucket/file' require 'digest/md5' require 'digest/sha1' describe Puppet::FileBucket::File do include PuppetSpec::Files before do # this is the default from spec_helper, but it keeps getting reset at odd times @bucketdir = tmpdir('bucket') Puppet[:bucketdir] = @bucketdir @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @dir = File.join(@bucketdir, '4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0') @contents = "file contents" end it "should have a to_s method to return the contents" do Puppet::FileBucket::File.new(@contents).to_s.should == @contents end it "should raise an error if changing content" do x = Puppet::FileBucket::File.new("first") proc { x.contents = "new" }.should raise_error end it "should require contents to be a string" do proc { Puppet::FileBucket::File.new(5) }.should raise_error(ArgumentError) end it "should set the contents appropriately" do Puppet::FileBucket::File.new(@contents).contents.should == @contents end it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do Puppet::FileBucket::File.new(@contents).checksum_type.should == "md5" end it "should calculate the checksum" do Puppet::FileBucket::File.new(@contents).checksum.should == @checksum end describe "when using back-ends" do it "should redirect using Puppet::Indirector" do Puppet::Indirector::Indirection.instance(:file_bucket_file).model.should equal(Puppet::FileBucket::File) end it "should have a :save instance method" do Puppet::FileBucket::File.indirection.should respond_to(:save) end end it "should return a url-ish name" do Puppet::FileBucket::File.new(@contents).name.should == "md5/4a8ec4fa5f01b4ab1a0ab8cbccb709f0" end it "should reject a url-ish name with an invalid checksum" do bucket = Puppet::FileBucket::File.new(@contents) lambda { bucket.name = "sha1/4a8ec4fa5f01b4ab1a0ab8cbccb709f0/new/path" }.should raise_error end it "should convert the contents to PSON" do Puppet::FileBucket::File.new(@contents).to_pson.should == '{"contents":"file contents"}' end it "should load from PSON" do Puppet::FileBucket::File.from_pson({"contents"=>"file contents"}).contents.should == "file contents" end def make_bucketed_file FileUtils.mkdir_p(@dir) File.open("#{@dir}/contents", 'w') { |f| f.write @contents } end describe "using the indirector's find method" do it "should return nil if a file doesn't exist" do bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{@digest}") bucketfile.should == nil end it "should find a filebucket if the file exists" do make_bucketed_file bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{@digest}") bucketfile.should_not == nil end describe "using RESTish digest notation" do it "should return nil if a file doesn't exist" do bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{@digest}") bucketfile.should == nil end it "should find a filebucket if the file exists" do make_bucketed_file bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{@digest}") bucketfile.should_not == nil end end end end diff --git a/spec/unit/file_collection/lookup_spec.rb b/spec/unit/file_collection/lookup_spec.rb index 138949a96..2b0f8bfab 100755 --- a/spec/unit/file_collection/lookup_spec.rb +++ b/spec/unit/file_collection/lookup_spec.rb @@ -1,46 +1,45 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_collection/lookup' class LookupTester include Puppet::FileCollection::Lookup end describe Puppet::FileCollection::Lookup do before do @tester = LookupTester.new @file_collection = mock 'file_collection' Puppet::FileCollection.stubs(:collection).returns @file_collection end it "should use the file collection to determine the index of the file name" do @file_collection.expects(:index).with("/my/file").returns 50 @tester.file = "/my/file" @tester.file_index.should == 50 end it "should return nil as the file name if no index is set" do @tester.file.should be_nil end it "should use the file collection to convert the index to a file name" do @file_collection.expects(:path).with(25).returns "/path/to/file" @tester.file_index = 25 @tester.file.should == "/path/to/file" end it "should support a line attribute" do @tester.line = 50 @tester.line.should == 50 end it "should default to the global file collection" do Puppet::FileCollection.expects(:collection).returns "collection" @tester.file_collection.should == "collection" end end diff --git a/spec/unit/file_collection_spec.rb b/spec/unit/file_collection_spec.rb index 09888f425..518763629 100755 --- a/spec/unit/file_collection_spec.rb +++ b/spec/unit/file_collection_spec.rb @@ -1,53 +1,52 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_collection' describe Puppet::FileCollection do before do @collection = Puppet::FileCollection.new end it "should be able to convert a file name into an index" do @collection.index("/my/file").should be_instance_of(Fixnum) end it "should be able to convert an index into a file name" do index = @collection.index("/path/to/file") @collection.path(index).should == "/path/to/file" end it "should always give the same file name for a given index" do index = @collection.index("/path/to/file") @collection.path(index).should == @collection.path(index) end it "should always give the same index for a given file name" do @collection.index("/my/file").should == @collection.index("/my/file") end it "should always correctly relate a file name and its index even when multiple files are in the collection" do indexes = %w{a b c d e f}.inject({}) do |hash, letter| hash[letter] = @collection.index("/path/to/file/#{letter}") hash end indexes.each do |letter, index| @collection.index("/path/to/file/#{letter}").should == indexes[letter] @collection.path(index).should == @collection.path(index) end end it "should return nil as the file name when an unknown index is provided" do @collection.path(50).should be_nil end it "should provide a global collection" do Puppet::FileCollection.collection.should be_instance_of(Puppet::FileCollection) end it "should reuse the global collection" do Puppet::FileCollection.collection.should equal(Puppet::FileCollection.collection) end end diff --git a/spec/unit/file_serving/base_spec.rb b/spec/unit/file_serving/base_spec.rb index 3449c8d0e..17d59460e 100755 --- a/spec/unit/file_serving/base_spec.rb +++ b/spec/unit/file_serving/base_spec.rb @@ -1,132 +1,131 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/base' describe Puppet::FileServing::Base do it "should accept a path" do Puppet::FileServing::Base.new("/module/dir/file").path.should == "/module/dir/file" end it "should require that paths be fully qualified" do lambda { Puppet::FileServing::Base.new("module/dir/file") }.should raise_error(ArgumentError) end it "should allow specification of whether links should be managed" do Puppet::FileServing::Base.new("/module/dir/file", :links => :manage).links.should == :manage end it "should have a :source attribute" do file = Puppet::FileServing::Base.new("/module/dir/file") file.should respond_to(:source) file.should respond_to(:source=) end it "should consider :ignore links equivalent to :manage links" do Puppet::FileServing::Base.new("/module/dir/file", :links => :ignore).links.should == :manage end it "should fail if :links is set to anything other than :manage, :follow, or :ignore" do proc { Puppet::FileServing::Base.new("/module/dir/file", :links => :else) }.should raise_error(ArgumentError) end it "should allow links values to be set as strings" do Puppet::FileServing::Base.new("/module/dir/file", :links => "follow").links.should == :follow end it "should default to :manage for :links" do Puppet::FileServing::Base.new("/module/dir/file").links.should == :manage end it "should allow specification of a path" do FileTest.stubs(:exists?).returns(true) Puppet::FileServing::Base.new("/module/dir/file", :path => "/my/file").path.should == "/my/file" end it "should allow specification of a relative path" do FileTest.stubs(:exists?).returns(true) Puppet::FileServing::Base.new("/module/dir/file", :relative_path => "my/file").relative_path.should == "my/file" end it "should have a means of determining if the file exists" do Puppet::FileServing::Base.new("/blah").should respond_to(:exist?) end it "should correctly indicate if the file is present" do File.expects(:lstat).with("/my/file").returns(mock("stat")) Puppet::FileServing::Base.new("/my/file").exist?.should be_true end it "should correctly indicate if the file is absent" do File.expects(:lstat).with("/my/file").raises RuntimeError Puppet::FileServing::Base.new("/my/file").exist?.should be_false end describe "when setting the relative path" do it "should require that the relative path be unqualified" do @file = Puppet::FileServing::Base.new("/module/dir/file") FileTest.stubs(:exists?).returns(true) proc { @file.relative_path = "/qualified/file" }.should raise_error(ArgumentError) end end describe "when determining the full file path" do before do @file = Puppet::FileServing::Base.new("/this/file") end it "should return the path if there is no relative path" do @file.full_path.should == "/this/file" end it "should return the path if the relative_path is set to ''" do @file.relative_path = "" @file.full_path.should == "/this/file" end it "should return the path if the relative_path is set to '.'" do @file.relative_path = "." @file.full_path.should == "/this/file" end it "should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''" do @file.relative_path = "not/qualified" @file.full_path.should == "/this/file/not/qualified" end it "should strip extra slashes" do file = Puppet::FileServing::Base.new("//this//file") file.full_path.should == "/this/file" end end describe "when stat'ing files" do before do @file = Puppet::FileServing::Base.new("/this/file") end it "should stat the file's full path" do @file.stubs(:full_path).returns("/this/file") File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") @file.stat end it "should fail if the file does not exist" do @file.stubs(:full_path).returns("/this/file") File.expects(:lstat).with("/this/file").raises(Errno::ENOENT) proc { @file.stat }.should raise_error(Errno::ENOENT) end it "should use :lstat if :links is set to :manage" do File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") @file.stat end it "should use :stat if :links is set to :follow" do File.expects(:stat).with("/this/file").returns stub("stat", :ftype => "file") @file.links = :follow @file.stat end end end diff --git a/spec/unit/file_serving/configuration/parser_spec.rb b/spec/unit/file_serving/configuration/parser_spec.rb index 46cdc1c45..3d6b3e234 100755 --- a/spec/unit/file_serving/configuration/parser_spec.rb +++ b/spec/unit/file_serving/configuration/parser_spec.rb @@ -1,181 +1,180 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/configuration/parser' describe Puppet::FileServing::Configuration::Parser do it "should subclass the LoadedFile class" do Puppet::FileServing::Configuration::Parser.superclass.should equal(Puppet::Util::LoadedFile) end end module FSConfigurationParserTesting def mock_file_content(content) # We want an array, but we actually want our carriage returns on all of it. lines = content.split("\n").collect { |l| l + "\n" } @filehandle.stubs(:each).multiple_yields(*lines) end end describe Puppet::FileServing::Configuration::Parser do before :each do @path = "/my/config.conf" FileTest.stubs(:exists?).with(@path).returns(true) FileTest.stubs(:readable?).with(@path).returns(true) @filehandle = mock 'filehandle' File.expects(:open).with(@path).yields(@filehandle) @parser = Puppet::FileServing::Configuration::Parser.new(@path) end describe Puppet::FileServing::Configuration::Parser, " when parsing" do include FSConfigurationParserTesting it "should allow comments" do @filehandle.expects(:each).yields("# this is a comment\n") proc { @parser.parse }.should_not raise_error end it "should allow blank lines" do @filehandle.expects(:each).yields("\n") proc { @parser.parse }.should_not raise_error end it "should create a new mount for each section in the configuration" do mount1 = mock 'one', :validate => true mount2 = mock 'two', :validate => true Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) mock_file_content "[one]\n[two]\n" @parser.parse end # This test is almost the exact same as the previous one. it "should return a hash of the created mounts" do mount1 = mock 'one', :validate => true mount2 = mock 'two', :validate => true Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) mock_file_content "[one]\n[two]\n" result = @parser.parse result["one"].should equal(mount1) result["two"].should equal(mount2) end it "should only allow mount names that are alphanumeric plus dashes" do mock_file_content "[a*b]\n" proc { @parser.parse }.should raise_error(ArgumentError) end it "should fail if the value for path/allow/deny starts with an equals sign" do mock_file_content "[one]\npath = /testing" proc { @parser.parse }.should raise_error(ArgumentError) end it "should validate each created mount" do mount1 = mock 'one' Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) mock_file_content "[one]\n" mount1.expects(:validate) @parser.parse end it "should fail if any mount does not pass validation" do mount1 = mock 'one' Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) mock_file_content "[one]\n" mount1.expects(:validate).raises RuntimeError lambda { @parser.parse }.should raise_error(RuntimeError) end end describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do include FSConfigurationParserTesting before do @mount = stub 'testmount', :name => "one", :validate => true Puppet::FileServing::Mount::File.expects(:new).with("one").returns(@mount) @parser.stubs(:add_modules_mount) end it "should set the mount path to the path attribute from that section" do mock_file_content "[one]\npath /some/path\n" @mount.expects(:path=).with("/some/path") @parser.parse end it "should tell the mount to allow any allow values from the section" do mock_file_content "[one]\nallow something\n" @mount.expects(:info) @mount.expects(:allow).with("something") @parser.parse end it "should tell the mount to deny any deny values from the section" do mock_file_content "[one]\ndeny something\n" @mount.expects(:info) @mount.expects(:deny).with("something") @parser.parse end it "should fail on any attributes other than path, allow, and deny" do mock_file_content "[one]\ndo something\n" proc { @parser.parse }.should raise_error(ArgumentError) end end describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do include FSConfigurationParserTesting before do @mount = stub 'modulesmount', :name => "modules", :validate => true end it "should create an instance of the Modules Mount class" do mock_file_content "[modules]\n" Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns @mount @parser.parse end it "should warn if a path is set" do mock_file_content "[modules]\npath /some/path\n" Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns(@mount) Puppet.expects(:warning) @parser.parse end end describe Puppet::FileServing::Configuration::Parser, " when parsing the plugins mount" do include FSConfigurationParserTesting before do @mount = stub 'pluginsmount', :name => "plugins", :validate => true end it "should create an instance of the Plugins Mount class" do mock_file_content "[plugins]\n" Puppet::FileServing::Mount::Plugins.expects(:new).with("plugins").returns @mount @parser.parse end it "should warn if a path is set" do mock_file_content "[plugins]\npath /some/path\n" Puppet.expects(:warning) @parser.parse end end end diff --git a/spec/unit/file_serving/configuration_spec.rb b/spec/unit/file_serving/configuration_spec.rb index 3c6113165..6ee1a4f38 100755 --- a/spec/unit/file_serving/configuration_spec.rb +++ b/spec/unit/file_serving/configuration_spec.rb @@ -1,249 +1,248 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/configuration' describe Puppet::FileServing::Configuration do it "should make :new a private method" do proc { Puppet::FileServing::Configuration.new }.should raise_error end it "should return the same configuration each time :create is called" do Puppet::FileServing::Configuration.create.should equal(Puppet::FileServing::Configuration.create) end it "should have a method for removing the current configuration instance" do old = Puppet::FileServing::Configuration.create Puppet::Util::Cacher.expire Puppet::FileServing::Configuration.create.should_not equal(old) end after do Puppet::Util::Cacher.expire end end describe Puppet::FileServing::Configuration do before :each do @path = "/path/to/configuration/file.conf" Puppet.settings.stubs(:value).with(:trace).returns(false) Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) end after :each do Puppet::Util::Cacher.expire end describe "when initializing" do it "should work without a configuration file" do FileTest.stubs(:exists?).with(@path).returns(false) proc { Puppet::FileServing::Configuration.create }.should_not raise_error end it "should parse the configuration file if present" do FileTest.stubs(:exists?).with(@path).returns(true) @parser = mock 'parser' @parser.expects(:parse).returns({}) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) Puppet::FileServing::Configuration.create end it "should determine the path to the configuration file from the Puppet settings" do Puppet::FileServing::Configuration.create end end describe "when parsing the configuration file" do before do FileTest.stubs(:exists?).with(@path).returns(true) @parser = mock 'parser' Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) end it "should set the mount list to the results of parsing" do @parser.expects(:parse).returns("one" => mock("mount")) config = Puppet::FileServing::Configuration.create config.mounted?("one").should be_true end it "should not raise exceptions" do @parser.expects(:parse).raises(ArgumentError) proc { Puppet::FileServing::Configuration.create }.should_not raise_error end it "should replace the existing mount list with the results of reparsing" do @parser.expects(:parse).returns("one" => mock("mount")) config = Puppet::FileServing::Configuration.create config.mounted?("one").should be_true # Now parse again @parser.expects(:parse).returns("two" => mock('other')) config.send(:readconfig, false) config.mounted?("one").should be_false config.mounted?("two").should be_true end it "should not replace the mount list until the file is entirely parsed successfully" do @parser.expects(:parse).returns("one" => mock("mount")) @parser.expects(:parse).raises(ArgumentError) config = Puppet::FileServing::Configuration.create # Now parse again, so the exception gets thrown config.send(:readconfig, false) config.mounted?("one").should be_true end it "should add modules and plugins mounts even if the file does not exist" do FileTest.expects(:exists?).returns false # the file doesn't exist config = Puppet::FileServing::Configuration.create config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end it "should allow all access to modules and plugins if no fileserver.conf exists" do FileTest.expects(:exists?).returns false # the file doesn't exist modules = stub 'modules', :empty? => true Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*') plugins = stub 'plugins', :empty? => true Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) plugins.expects(:allow).with('*') Puppet::FileServing::Configuration.create end it "should not allow access from all to modules and plugins if the fileserver.conf provided some rules" do FileTest.expects(:exists?).returns false # the file doesn't exist modules = stub 'modules', :empty? => false Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*').never plugins = stub 'plugins', :empty? => false Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) plugins.expects(:allow).with('*').never Puppet::FileServing::Configuration.create end it "should add modules and plugins mounts even if they are not returned by the parser" do @parser.expects(:parse).returns("one" => mock("mount")) FileTest.expects(:exists?).returns true # the file doesn't exist config = Puppet::FileServing::Configuration.create config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end end describe "when finding the specified mount" do it "should choose the named mount if one exists" do config = Puppet::FileServing::Configuration.create config.expects(:mounts).returns("one" => "foo") config.find_mount("one", mock('env')).should == "foo" end it "should use the provided environment to find a matching module if the named module cannot be found" do config = Puppet::FileServing::Configuration.create mod = mock 'module' env = mock 'environment' env.expects(:module).with("foo").returns mod mount = mock 'mount' config.stubs(:mounts).returns("modules" => mount) Puppet::Util::Warnings.expects(:notice_once) config.find_mount("foo", env).should equal(mount) end it "should return nil if there is no such named mount and no module with the same name exists" do config = Puppet::FileServing::Configuration.create env = mock 'environment' env.expects(:module).with("foo").returns nil mount = mock 'mount' config.stubs(:mounts).returns("modules" => mount) config.find_mount("foo", env).should be_nil end end describe "when finding the mount name and relative path in a request key" do before do @config = Puppet::FileServing::Configuration.create @config.stubs(:find_mount) @request = stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env") end it "should reread the configuration" do @config.expects(:readconfig) @config.split_path(@request) end it "should treat the first field of the URI path as the mount name" do @config.expects(:find_mount).with { |name, node| name == "foo" } @config.split_path(@request) end it "should fail if the mount name is not alpha-numeric" do @request.expects(:key).returns "foo&bar/asdf" lambda { @config.split_path(@request) }.should raise_error(ArgumentError) end it "should support dashes in the mount name" do @request.expects(:key).returns "foo-bar/asdf" lambda { @config.split_path(@request) }.should_not raise_error(ArgumentError) end it "should use the mount name and environment to find the mount" do @config.expects(:find_mount).with { |name, env| name == "foo" and env == @request.environment } @request.stubs(:node).returns("mynode") @config.split_path(@request) end it "should return nil if the mount cannot be found" do @config.expects(:find_mount).returns nil @config.split_path(@request).should be_nil end it "should return the mount and the relative path if the mount is found" do mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "bar/baz"] end it "should remove any double slashes" do @request.stubs(:key).returns "foo/bar//baz" mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "bar/baz"] end it "should return the relative path as nil if it is an empty string" do @request.expects(:key).returns "foo" mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, nil] end it "should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility" do @request.expects(:key).returns "foo/bar" mount = stub 'mount', :name => "modules" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "foo/bar"] end end end diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb index 576cbde1f..2637ba6ce 100755 --- a/spec/unit/file_serving/content_spec.rb +++ b/spec/unit/file_serving/content_spec.rb @@ -1,118 +1,117 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/content' describe Puppet::FileServing::Content do it "should should be a subclass of Base" do Puppet::FileServing::Content.superclass.should equal(Puppet::FileServing::Base) end it "should indirect file_content" do Puppet::FileServing::Content.indirection.name.should == :file_content end it "should should include the IndirectionHooks module in its indirection" do Puppet::FileServing::Content.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) end it "should only support the raw format" do Puppet::FileServing::Content.supported_formats.should == [:raw] end it "should have a method for collecting its attributes" do Puppet::FileServing::Content.new("/path").should respond_to(:collect) end it "should not retrieve and store its contents when its attributes are collected if the file is a normal file" do content = Puppet::FileServing::Content.new("/path") result = "foo" File.stubs(:lstat).returns(stub("stat", :ftype => "file")) File.expects(:read).with("/path").never content.collect content.instance_variable_get("@content").should be_nil end it "should not attempt to retrieve its contents if the file is a directory" do content = Puppet::FileServing::Content.new("/path") result = "foo" File.stubs(:lstat).returns(stub("stat", :ftype => "directory")) File.expects(:read).with("/path").never content.collect content.instance_variable_get("@content").should be_nil end it "should have a method for setting its content" do content = Puppet::FileServing::Content.new("/path") content.should respond_to(:content=) end it "should make content available when set externally" do content = Puppet::FileServing::Content.new("/path") content.content = "foo/bar" content.content.should == "foo/bar" end it "should be able to create a content instance from raw file contents" do Puppet::FileServing::Content.should respond_to(:from_raw) end it "should create an instance with a fake file name and correct content when converting from raw" do instance = mock 'instance' Puppet::FileServing::Content.expects(:new).with("/this/is/a/fake/path").returns instance instance.expects(:content=).with "foo/bar" Puppet::FileServing::Content.from_raw("foo/bar").should equal(instance) end it "should return an opened File when converted to raw" do content = Puppet::FileServing::Content.new("/path") File.expects(:new).with("/path","r").returns :file content.to_raw.should == :file end end describe Puppet::FileServing::Content, "when returning the contents" do before do @path = "/my/path" @content = Puppet::FileServing::Content.new(@path, :links => :follow) end it "should fail if the file is a symlink and links are set to :manage" do @content.links = :manage File.expects(:lstat).with(@path).returns stub("stat", :ftype => "symlink") proc { @content.content }.should raise_error(ArgumentError) end it "should fail if a path is not set" do proc { @content.content }.should raise_error(Errno::ENOENT) end it "should raise Errno::ENOENT if the file is absent" do @content.path = "/there/is/absolutely/no/chance/that/this/path/exists" proc { @content.content }.should raise_error(Errno::ENOENT) end it "should return the contents of the path if the file exists" do File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") File.expects(:read).with(@path).returns(:mycontent) @content.content.should == :mycontent end it "should cache the returned contents" do File.expects(:stat).with(@path).returns stub("stat", :ftype => "file") File.expects(:read).with(@path).returns(:mycontent) @content.content # The second run would throw a failure if the content weren't being cached. @content.content end end diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 3b12c953e..a369ad39c 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -1,356 +1,355 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/fileset' describe Puppet::FileServing::Fileset, " when initializing" do it "should require a path" do proc { Puppet::FileServing::Fileset.new }.should raise_error(ArgumentError) end it "should fail if its path is not fully qualified" do proc { Puppet::FileServing::Fileset.new("some/file") }.should raise_error(ArgumentError) end it "should not fail if the path is fully qualified, with a trailing separator" do path = "/some/path/with/trailing/separator" path_with_separator = "#{path}#{File::SEPARATOR}" File.stubs(:lstat).with(path).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path_with_separator) fileset.path.should == path end it "should fail if its path does not exist" do File.expects(:lstat).with("/some/file").returns nil proc { Puppet::FileServing::Fileset.new("/some/file") }.should raise_error(ArgumentError) end it "should accept a 'recurse' option" do File.expects(:lstat).with("/some/file").returns stub("stat") set = Puppet::FileServing::Fileset.new("/some/file", :recurse => true) set.recurse.should be_true end it "should accept a 'recurselimit' option" do File.expects(:lstat).with("/some/file").returns stub("stat") set = Puppet::FileServing::Fileset.new("/some/file", :recurselimit => 3) set.recurselimit.should == 3 end it "should accept an 'ignore' option" do File.expects(:lstat).with("/some/file").returns stub("stat") set = Puppet::FileServing::Fileset.new("/some/file", :ignore => ".svn") set.ignore.should == [".svn"] end it "should accept a 'links' option" do File.expects(:lstat).with("/some/file").returns stub("stat") set = Puppet::FileServing::Fileset.new("/some/file", :links => :manage) set.links.should == :manage end it "should accept a 'checksum_type' option" do File.expects(:lstat).with("/some/file").returns stub("stat") set = Puppet::FileServing::Fileset.new("/some/file", :checksum_type => :test) set.checksum_type.should == :test end it "should fail if 'links' is set to anything other than :manage or :follow" do proc { Puppet::FileServing::Fileset.new("/some/file", :links => :whatever) }.should raise_error(ArgumentError) end it "should default to 'false' for recurse" do File.expects(:lstat).with("/some/file").returns stub("stat") Puppet::FileServing::Fileset.new("/some/file").recurse.should == false end it "should default to :infinite for recurselimit" do File.expects(:lstat).with("/some/file").returns stub("stat") Puppet::FileServing::Fileset.new("/some/file").recurselimit.should == :infinite end it "should default to an empty ignore list" do File.expects(:lstat).with("/some/file").returns stub("stat") Puppet::FileServing::Fileset.new("/some/file").ignore.should == [] end it "should default to :manage for links" do File.expects(:lstat).with("/some/file").returns stub("stat") Puppet::FileServing::Fileset.new("/some/file").links.should == :manage end it "should support using an Indirector Request for its options" do File.expects(:lstat).with("/some/file").returns stub("stat") request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") lambda { Puppet::FileServing::Fileset.new("/some/file", request) }.should_not raise_error end describe "using an indirector request" do before do File.stubs(:lstat).returns stub("stat") @values = {:links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234} @request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") end [:recurse, :recurselimit, :ignore, :links].each do |option| it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present" do @request.stubs(:options).returns(option => @values[option]) Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] end it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present with the keys stored as strings" do @request.stubs(:options).returns(option.to_s => @values[option]) Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] end end it "should convert the integer as a string to their integer counterpart when setting options" do @request.stubs(:options).returns(:recurselimit => "1234") Puppet::FileServing::Fileset.new("/my/file", @request).recurselimit.should == 1234 end it "should convert the string 'true' to the boolean true when setting options" do @request.stubs(:options).returns(:recurse => "true") Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == true end it "should convert the string 'false' to the boolean false when setting options" do @request.stubs(:options).returns(:recurse => "false") Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == false end end end describe Puppet::FileServing::Fileset, " when determining whether to recurse" do before do @path = "/my/path" File.expects(:lstat).with(@path).returns stub("stat") @fileset = Puppet::FileServing::Fileset.new(@path) end it "should always recurse if :recurse is set to 'true' and with infinite recursion" do @fileset.recurse = true @fileset.recurselimit = :infinite @fileset.recurse?(0).should be_true end it "should never recurse if :recurse is set to 'false'" do @fileset.recurse = false @fileset.recurse?(-1).should be_false end it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is less than that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(0).should be_true end it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is equal to that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(1).should be_true end it "should not recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is greater than that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(2).should be_false end end describe Puppet::FileServing::Fileset, " when recursing" do before do @path = "/my/path" File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @dirstat = stub 'dirstat', :directory? => true @filestat = stub 'filestat', :directory? => false end def mock_dir_structure(path, stat_method = :lstat) File.stubs(stat_method).with(path).returns(@dirstat) Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) # Keep track of the files we're stubbing. @files = %w{.} %w{one two .svn CVS}.each do |subdir| @files << subdir # relative path subpath = File.join(path, subdir) File.stubs(stat_method).with(subpath).returns(@dirstat) Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) %w{file1 file2 .svn CVS}.each do |file| @files << File.join(subdir, file) # relative path File.stubs(stat_method).with(File.join(subpath, file)).returns(@filestat) end end end it "should recurse through the whole file tree if :recurse is set to 'true'" do mock_dir_structure(@path) @fileset.stubs(:recurse?).returns(true) @fileset.files.sort.should == @files.sort end it "should not recurse if :recurse is set to 'false'" do mock_dir_structure(@path) @fileset.stubs(:recurse?).returns(false) @fileset.files.should == %w{.} end # It seems like I should stub :recurse? here, or that I shouldn't stub the # examples above, but... it "should recurse to the level set if :recurselimit is set to an integer" do mock_dir_structure(@path) @fileset.recurse = true @fileset.recurselimit = 1 @fileset.files.should == %w{. one two .svn CVS} end it "should ignore the '.' and '..' directories in subdirectories" do mock_dir_structure(@path) @fileset.recurse = true @fileset.files.sort.should == @files.sort end it "should function if the :ignore value provided is nil" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = nil lambda { @fileset.files }.should_not raise_error end it "should ignore files that match a single pattern in the ignore list" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = ".svn" @fileset.files.find { |file| file.include?(".svn") }.should be_nil end it "should ignore files that match any of multiple patterns in the ignore list" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = %w{.svn CVS} @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil end it "should use File.stat if :links is set to :follow" do mock_dir_structure(@path, :stat) @fileset.recurse = true @fileset.links = :follow @fileset.files.sort.should == @files.sort end it "should use File.lstat if :links is set to :manage" do mock_dir_structure(@path, :lstat) @fileset.recurse = true @fileset.links = :manage @fileset.files.sort.should == @files.sort end it "should succeed when paths have regexp significant characters" do @path = "/my/path/rV1x2DafFr0R6tGG+1bbk++++TM" File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) mock_dir_structure(@path) @fileset.recurse = true @fileset.files.sort.should == @files.sort end end describe Puppet::FileServing::Fileset, " when following links that point to missing files" do before do @path = "/my/path" File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @fileset.links = :follow @fileset.recurse = true @stat = stub 'stat', :directory? => true File.expects(:stat).with(@path).returns(@stat) File.expects(:stat).with(File.join(@path, "mylink")).raises(Errno::ENOENT) Dir.stubs(:entries).with(@path).returns(["mylink"]) end it "should not fail" do proc { @fileset.files }.should_not raise_error end it "should still manage the link" do @fileset.files.sort.should == %w{. mylink}.sort end end describe Puppet::FileServing::Fileset, " when ignoring" do before do @path = "/my/path" File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) end it "should use ruby's globbing to determine what files should be ignored" do @fileset.ignore = ".svn" File.expects(:fnmatch?).with(".svn", "my_file") @fileset.ignore?("my_file") end it "should ignore files whose paths match a single provided ignore value" do @fileset.ignore = ".svn" File.stubs(:fnmatch?).with(".svn", "my_file").returns true @fileset.ignore?("my_file").should be_true end it "should ignore files whose paths match any of multiple provided ignore values" do @fileset.ignore = [".svn", "CVS"] File.stubs(:fnmatch?).with(".svn", "my_file").returns false File.stubs(:fnmatch?).with("CVS", "my_file").returns true @fileset.ignore?("my_file").should be_true end end describe Puppet::FileServing::Fileset, "when merging other filesets" do before do @paths = %w{/first/path /second/path /third/path} File.stubs(:lstat).returns stub("stat", :directory? => false) @filesets = @paths.collect do |path| File.stubs(:lstat).with(path).returns stub("stat", :directory? => true) Puppet::FileServing::Fileset.new(path, :recurse => true) end Dir.stubs(:entries).returns [] end it "should return a hash of all files in each fileset with the value being the base path" do Dir.expects(:entries).with("/first/path").returns(%w{one uno}) Dir.expects(:entries).with("/second/path").returns(%w{two dos}) Dir.expects(:entries).with("/third/path").returns(%w{three tres}) Puppet::FileServing::Fileset.merge(*@filesets).should == { "." => "/first/path", "one" => "/first/path", "uno" => "/first/path", "two" => "/second/path", "dos" => "/second/path", "three" => "/third/path", "tres" => "/third/path", } end it "should include the base directory from the first fileset" do Dir.expects(:entries).with("/first/path").returns(%w{one}) Dir.expects(:entries).with("/second/path").returns(%w{two}) Puppet::FileServing::Fileset.merge(*@filesets)["."].should == "/first/path" end it "should use the base path of the first found file when relative file paths conflict" do Dir.expects(:entries).with("/first/path").returns(%w{one}) Dir.expects(:entries).with("/second/path").returns(%w{one}) Puppet::FileServing::Fileset.merge(*@filesets)["one"].should == "/first/path" end end diff --git a/spec/unit/file_serving/indirection_hooks_spec.rb b/spec/unit/file_serving/indirection_hooks_spec.rb index 1c7f55da8..4890505ab 100755 --- a/spec/unit/file_serving/indirection_hooks_spec.rb +++ b/spec/unit/file_serving/indirection_hooks_spec.rb @@ -1,63 +1,63 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/file_serving/indirection_hooks' describe Puppet::FileServing::IndirectionHooks do before do @object = Object.new @object.extend(Puppet::FileServing::IndirectionHooks) @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil end describe "when being used to select termini" do it "should return :file if the request key is fully qualified" do @request.expects(:key).returns "#{File::SEPARATOR}foo" @object.select_terminus(@request).should == :file end it "should return :file if the URI protocol is set to 'file'" do @request.expects(:protocol).returns "file" @object.select_terminus(@request).should == :file end it "should fail when a protocol other than :puppet or :file is used" do @request.stubs(:protocol).returns "http" proc { @object.select_terminus(@request) }.should raise_error(ArgumentError) end describe "and the protocol is 'puppet'" do before do @request.stubs(:protocol).returns "puppet" end it "should choose :rest when a server is specified" do @request.stubs(:protocol).returns "puppet" @request.expects(:server).returns "foo" @object.select_terminus(@request).should == :rest end # This is so a given file location works when bootstrapping with no server. it "should choose :rest when the Settings name isn't 'puppet'" do @request.stubs(:protocol).returns "puppet" @request.stubs(:server).returns "foo" Puppet.settings.stubs(:value).with(:name).returns "foo" @object.select_terminus(@request).should == :rest end it "should choose :file_server when the settings name is 'puppet' and no server is specified" do modules = mock 'modules' @request.expects(:protocol).returns "puppet" @request.expects(:server).returns nil Puppet.settings.expects(:value).with(:name).returns "puppet" @object.select_terminus(@request).should == :file_server end end end end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 34794cca9..39f2a9548 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -1,286 +1,285 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/metadata' describe Puppet::FileServing::Metadata do it "should should be a subclass of Base" do Puppet::FileServing::Metadata.superclass.should equal(Puppet::FileServing::Base) end it "should indirect file_metadata" do Puppet::FileServing::Metadata.indirection.name.should == :file_metadata end it "should should include the IndirectionHooks module in its indirection" do Puppet::FileServing::Metadata.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) end it "should have a method that triggers attribute collection" do Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:collect) end it "should support pson serialization" do Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:to_pson) end it "should support to_pson_data_hash" do Puppet::FileServing::Metadata.new("/foo/bar").should respond_to(:to_pson_data_hash) end it "should support pson deserialization" do Puppet::FileServing::Metadata.should respond_to(:from_pson) end describe "when serializing" do before do @metadata = Puppet::FileServing::Metadata.new("/foo/bar") end it "should perform pson serialization by calling to_pson on it's pson_data_hash" do pdh = mock "data hash" pdh_as_pson = mock "data as pson" @metadata.expects(:to_pson_data_hash).returns pdh pdh.expects(:to_pson).returns pdh_as_pson @metadata.to_pson.should == pdh_as_pson end it "should serialize as FileMetadata" do @metadata.to_pson_data_hash['document_type'].should == "FileMetadata" end it "the data should include the path, relative_path, links, owner, group, mode, checksum, type, and destination" do @metadata.to_pson_data_hash['data'].keys.sort.should == %w{ path relative_path links owner group mode checksum type destination }.sort end it "should pass the path in the hash verbatum" do @metadata.to_pson_data_hash['data']['path'] == @metadata.path end it "should pass the relative_path in the hash verbatum" do @metadata.to_pson_data_hash['data']['relative_path'] == @metadata.relative_path end it "should pass the links in the hash verbatum" do @metadata.to_pson_data_hash['data']['links'] == @metadata.links end it "should pass the path owner in the hash verbatum" do @metadata.to_pson_data_hash['data']['owner'] == @metadata.owner end it "should pass the group in the hash verbatum" do @metadata.to_pson_data_hash['data']['group'] == @metadata.group end it "should pass the mode in the hash verbatum" do @metadata.to_pson_data_hash['data']['mode'] == @metadata.mode end it "should pass the ftype in the hash verbatum as the 'type'" do @metadata.to_pson_data_hash['data']['type'] == @metadata.ftype end it "should pass the destination verbatum" do @metadata.to_pson_data_hash['data']['destination'] == @metadata.destination end it "should pass the checksum in the hash as a nested hash" do @metadata.to_pson_data_hash['data']['checksum'].should be_is_a(Hash) end it "should pass the checksum_type in the hash verbatum as the checksum's type" do @metadata.to_pson_data_hash['data']['checksum']['type'] == @metadata.checksum_type end it "should pass the checksum in the hash verbatum as the checksum's value" do @metadata.to_pson_data_hash['data']['checksum']['value'] == @metadata.checksum end end end describe Puppet::FileServing::Metadata, " when finding the file to use for setting attributes" do before do @path = "/my/path" @metadata = Puppet::FileServing::Metadata.new(@path) # Use a link because it's easier to test -- no checksumming @stat = stub "stat", :uid => 10, :gid => 20, :mode => 0755, :ftype => "link" # Not quite. We don't want to checksum links, but we must because they might be being followed. @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. @metadata.stubs(:md5_file).returns(@checksum) # end it "should accept a base path path to which the file should be relative" do File.expects(:lstat).with(@path).returns @stat File.expects(:readlink).with(@path).returns "/what/ever" @metadata.collect end it "should use the set base path if one is not provided" do File.expects(:lstat).with(@path).returns @stat File.expects(:readlink).with(@path).returns "/what/ever" @metadata.collect end it "should raise an exception if the file does not exist" do File.expects(:lstat).with(@path).raises(Errno::ENOENT) proc { @metadata.collect}.should raise_error(Errno::ENOENT) end end describe Puppet::FileServing::Metadata, " when collecting attributes" do before do @path = "/my/file" # Use a real file mode, so we can validate the masking is done. @stat = stub 'stat', :uid => 10, :gid => 20, :mode => 33261, :ftype => "file" File.stubs(:lstat).returns(@stat) @checksum = Digest::MD5.hexdigest("some content\n") @metadata = Puppet::FileServing::Metadata.new("/my/file") @metadata.stubs(:md5_file).returns(@checksum) @metadata.collect end it "should be able to produce xmlrpc-style attribute information" do @metadata.should respond_to(:attributes_with_tabs) end # LAK:FIXME This should actually change at some point it "should set the owner by id" do @metadata.owner.should be_instance_of(Fixnum) end # LAK:FIXME This should actually change at some point it "should set the group by id" do @metadata.group.should be_instance_of(Fixnum) end it "should set the owner to the file's current owner" do @metadata.owner.should == 10 end it "should set the group to the file's current group" do @metadata.group.should == 20 end it "should set the mode to the file's masked mode" do @metadata.mode.should == 0755 end it "should set the checksum to the file's current checksum" do @metadata.checksum.should == "{md5}#{@checksum}" end describe "when managing files" do it "should default to a checksum of type MD5" do @metadata.checksum.should == "{md5}#{@checksum}" end it "should give a mtime checksum when checksum_type is set" do time = Time.now @metadata.checksum_type = "mtime" @metadata.expects(:mtime_file).returns(@time) @metadata.collect @metadata.checksum.should == "{mtime}#{@time}" end it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do @metadata.attributes_with_tabs.should == "#{0755.to_s}\tfile\t10\t20\t{md5}#{@checksum}" end end describe "when managing directories" do before do @stat.stubs(:ftype).returns("directory") @time = Time.now @metadata.expects(:ctime_file).returns(@time) end it "should only use checksums of type 'ctime' for directories" do @metadata.collect @metadata.checksum.should == "{ctime}#{@time}" end it "should only use checksums of type 'ctime' for directories even if checksum_type set" do @metadata.checksum_type = "mtime" @metadata.expects(:mtime_file).never @metadata.collect @metadata.checksum.should == "{ctime}#{@time}" end it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do @metadata.collect @metadata.attributes_with_tabs.should == "#{0755.to_s}\tdirectory\t10\t20\t{ctime}#{@time.to_s}" end end describe "when managing links" do before do @stat.stubs(:ftype).returns("link") File.expects(:readlink).with("/my/file").returns("/path/to/link") @metadata.collect @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. @file.stubs(:md5_file).returns(@checksum) # end it "should read links instead of returning their checksums" do @metadata.destination.should == "/path/to/link" end pending "should produce tab-separated mode, type, owner, group, and destination for xmlrpc" do # "We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow." @metadata.attributes_with_tabs.should == "#{0755}\tlink\t10\t20\t/path/to/link" end it "should produce tab-separated mode, type, owner, group, checksum, and destination for xmlrpc" do @metadata.attributes_with_tabs.should == "#{0755}\tlink\t10\t20\t{md5}eb9c2bf0eb63f3a7bc0ea37ef18aeba5\t/path/to/link" end end end describe Puppet::FileServing::Metadata, " when pointing to a link" do describe "when links are managed" do before do @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage) File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path" @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. @file.stubs(:md5_file).returns(@checksum) # end it "should store the destination of the link in :destination if links are :manage" do @file.collect @file.destination.should == "/some/other/path" end pending "should not collect the checksum if links are :manage" do # We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow. @file.collect @file.checksum.should be_nil end it "should collect the checksum if links are :manage" do # see pending note above @file.collect @file.checksum.should == "{md5}#{@checksum}" end end describe "when links are followed" do before do @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :follow) File.expects(:stat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755) File.expects(:readlink).with("/base/path/my/file").never @checksum = Digest::MD5.hexdigest("some content\n") @file.stubs(:md5_file).returns(@checksum) end it "should not store the destination of the link in :destination if links are :follow" do @file.collect @file.destination.should be_nil end it "should collect the checksum if links are :follow" do @file.collect @file.checksum.should == "{md5}#{@checksum}" end end end diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb index 792ce951c..70c804abd 100755 --- a/spec/unit/file_serving/mount/file_spec.rb +++ b/spec/unit/file_serving/mount/file_spec.rb @@ -1,196 +1,195 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/mount/file' module FileServingMountTesting def stub_facter(hostname) Facter.stubs(:value).with("hostname").returns(hostname.sub(/\..+/, '')) Facter.stubs(:value).with("domain").returns(hostname.sub(/^[^.]+\./, '')) end end describe Puppet::FileServing::Mount::File do it "should provide a method for clearing its cached host information" do old = Puppet::FileServing::Mount::File.localmap Puppet::Util::Cacher.expire Puppet::FileServing::Mount::File.localmap.should_not equal(old) end it "should be invalid if it does not have a path" do lambda { Puppet::FileServing::Mount::File.new("foo").validate }.should raise_error(ArgumentError) end it "should be valid if it has a path" do FileTest.stubs(:directory?).returns true FileTest.stubs(:readable?).returns true mount = Puppet::FileServing::Mount::File.new("foo") mount.path = "/foo" lambda { mount.validate }.should_not raise_error(ArgumentError) end end describe Puppet::FileServing::Mount::File, " when setting the path" do before do @mount = Puppet::FileServing::Mount::File.new("test") @dir = "/this/path/does/not/exist" end it "should fail if the path is not a directory" do FileTest.expects(:directory?).returns(false) proc { @mount.path = @dir }.should raise_error(ArgumentError) end it "should fail if the path is not readable" do FileTest.expects(:directory?).returns(true) FileTest.expects(:readable?).returns(false) proc { @mount.path = @dir }.should raise_error(ArgumentError) end end describe Puppet::FileServing::Mount::File, " when substituting hostnames and ip addresses into file paths" do include FileServingMountTesting before do FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @host = "host.domain.com" end it "should replace incidences of %h in the path with the client's short name" do @mount.path = "/dir/%h/yay" @mount.path(@host).should == "/dir/host/yay" end it "should replace incidences of %H in the path with the client's fully qualified name" do @mount.path = "/dir/%H/yay" @mount.path(@host).should == "/dir/host.domain.com/yay" end it "should replace incidences of %d in the path with the client's domain name" do @mount.path = "/dir/%d/yay" @mount.path(@host).should == "/dir/domain.com/yay" end it "should perform all necessary replacements" do @mount.path = "/%h/%d/%H" @mount.path(@host).should == "/host/domain.com/host.domain.com" end it "should use local host information if no client data is provided" do stub_facter("myhost.mydomain.com") @mount.path = "/%h/%d/%H" @mount.path.should == "/myhost/mydomain.com/myhost.mydomain.com" end after do Puppet::Util::Cacher.expire end end describe Puppet::FileServing::Mount::File, "when determining the complete file path" do include FileServingMountTesting before do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @mount.path = "/mount" stub_facter("myhost.mydomain.com") @host = "host.domain.com" end it "should return nil if the file is absent" do FileTest.stubs(:exist?).returns(false) @mount.complete_path("/my/path", nil).should be_nil end it "should write a log message if the file is absent" do FileTest.stubs(:exist?).returns(false) Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") @mount.complete_path("/my/path", nil) end it "should return the file path if the file is present" do FileTest.stubs(:exist?).with("/my/path").returns(true) @mount.complete_path("/my/path", nil).should == "/mount/my/path" end it "should treat a nil file name as the path to the mount itself" do FileTest.stubs(:exist?).returns(true) @mount.complete_path(nil, nil).should == "/mount" end it "should use the client host name if provided in the options" do @mount.path = "/mount/%h" @mount.complete_path("/my/path", @host).should == "/mount/host/my/path" end it "should perform replacements on the base path" do @mount.path = "/blah/%h" @mount.complete_path("/my/stuff", @host).should == "/blah/host/my/stuff" end it "should not perform replacements on the per-file path" do @mount.path = "/blah" @mount.complete_path("/%h/stuff", @host).should == "/blah/%h/stuff" end it "should look for files relative to its base directory" do @mount.complete_path("/my/stuff", @host).should == "/mount/my/stuff" end end describe Puppet::FileServing::Mount::File, "when finding files" do include FileServingMountTesting before do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @mount.path = "/mount" stub_facter("myhost.mydomain.com") @host = "host.domain.com" @request = stub 'request', :node => "foo" end it "should return the results of the complete file path" do FileTest.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" @mount.find("/my/path", @request).should == "eh" end end describe Puppet::FileServing::Mount::File, "when searching for files" do include FileServingMountTesting before do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @mount.path = "/mount" stub_facter("myhost.mydomain.com") @host = "host.domain.com" @request = stub 'request', :node => "foo" end it "should return the results of the complete file path as an array" do FileTest.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" @mount.search("/my/path", @request).should == ["eh"] end it "should return nil if the complete path is nil" do FileTest.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns nil @mount.search("/my/path", @request).should be_nil end end diff --git a/spec/unit/file_serving/mount/modules_spec.rb b/spec/unit/file_serving/mount/modules_spec.rb index 1213f4fa5..2d582daa2 100755 --- a/spec/unit/file_serving/mount/modules_spec.rb +++ b/spec/unit/file_serving/mount/modules_spec.rb @@ -1,63 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/mount/modules' describe Puppet::FileServing::Mount::Modules do before do @mount = Puppet::FileServing::Mount::Modules.new("modules") @environment = stub 'environment', :module => nil @request = stub 'request', :environment => @environment end describe "when finding files" do it "should use the provided environment to find the module" do @environment.expects(:module) @mount.find("foo", @request) end it "should treat the first field of the relative path as the module name" do @environment.expects(:module).with("foo") @mount.find("foo/bar/baz", @request) end it "should return nil if the specified module does not exist" do @environment.expects(:module).with("foo").returns nil @mount.find("foo/bar/baz", @request) end it "should return the file path from the module" do mod = mock 'module' mod.expects(:file).with("bar/baz").returns "eh" @environment.expects(:module).with("foo").returns mod @mount.find("foo/bar/baz", @request).should == "eh" end end describe "when searching for files" do it "should use the node's environment to search the module" do @environment.expects(:module) @mount.search("foo", @request) end it "should treat the first field of the relative path as the module name" do @environment.expects(:module).with("foo") @mount.search("foo/bar/baz", @request) end it "should return nil if the specified module does not exist" do @environment.expects(:module).with("foo").returns nil @mount.search("foo/bar/baz", @request) end it "should return the file path as an array from the module" do mod = mock 'module' mod.expects(:file).with("bar/baz").returns "eh" @environment.expects(:module).with("foo").returns mod @mount.search("foo/bar/baz", @request).should == ["eh"] end end end diff --git a/spec/unit/file_serving/mount/plugins_spec.rb b/spec/unit/file_serving/mount/plugins_spec.rb index 6aa9ae927..b6bed72a0 100755 --- a/spec/unit/file_serving/mount/plugins_spec.rb +++ b/spec/unit/file_serving/mount/plugins_spec.rb @@ -1,61 +1,60 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/mount/plugins' describe Puppet::FileServing::Mount::Plugins do before do @mount = Puppet::FileServing::Mount::Plugins.new("plugins") @environment = stub 'environment', :module => nil @request = stub 'request', :environment => @environment end describe "when finding files" do it "should use the provided environment to find the modules" do @environment.expects(:modules).returns [] @mount.find("foo", @request) end it "should return nil if no module can be found with a matching plugin" do mod = mock 'module' mod.stubs(:plugin).with("foo/bar").returns nil @environment.stubs(:modules).returns [mod] @mount.find("foo/bar", @request).should be_nil end it "should return the file path from the module" do mod = mock 'module' mod.stubs(:plugin).with("foo/bar").returns "eh" @environment.stubs(:modules).returns [mod] @mount.find("foo/bar", @request).should == "eh" end end describe "when searching for files" do it "should use the node's environment to find the modules" do @environment.expects(:modules).returns [] @mount.search("foo", @request) end it "should return nil if no modules can be found that have plugins" do mod = mock 'module' mod.stubs(:plugins?).returns false @environment.stubs(:modules).returns [] @mount.search("foo/bar", @request).should be_nil end it "should return the plugin paths for each module that has plugins" do one = stub 'module', :plugins? => true, :plugin_directory => "/one" two = stub 'module', :plugins? => true, :plugin_directory => "/two" @environment.stubs(:modules).returns [one, two] @mount.search("foo/bar", @request).should == %w{/one /two} end end end diff --git a/spec/unit/file_serving/mount_spec.rb b/spec/unit/file_serving/mount_spec.rb index 2bfe643f6..5d8e64f82 100755 --- a/spec/unit/file_serving/mount_spec.rb +++ b/spec/unit/file_serving/mount_spec.rb @@ -1,32 +1,31 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_serving/mount' describe Puppet::FileServing::Mount do it "should use 'mount[$name]' as its string form" do Puppet::FileServing::Mount.new("foo").to_s.should == "mount[foo]" end end describe Puppet::FileServing::Mount, " when initializing" do it "should fail on non-alphanumeric name" do proc { Puppet::FileServing::Mount.new("non alpha") }.should raise_error(ArgumentError) end it "should allow dashes in its name" do Puppet::FileServing::Mount.new("non-alpha").name.should == "non-alpha" end end describe Puppet::FileServing::Mount, " when finding files" do it "should fail" do lambda { Puppet::FileServing::Mount.new("test").find("foo", :one => "two") }.should raise_error(NotImplementedError) end end describe Puppet::FileServing::Mount, " when searching for files" do it "should fail" do lambda { Puppet::FileServing::Mount.new("test").search("foo", :one => "two") }.should raise_error(NotImplementedError) end end diff --git a/spec/unit/file_serving/terminus_helper_spec.rb b/spec/unit/file_serving/terminus_helper_spec.rb index 36cf0daf3..7efe3fb98 100755 --- a/spec/unit/file_serving/terminus_helper_spec.rb +++ b/spec/unit/file_serving/terminus_helper_spec.rb @@ -1,98 +1,98 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-22. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/file_serving/terminus_helper' describe Puppet::FileServing::TerminusHelper do before do @helper = Object.new @helper.extend(Puppet::FileServing::TerminusHelper) @model = mock 'model' @helper.stubs(:model).returns(@model) @request = stub 'request', :key => "url", :options => {} @fileset = stub 'fileset', :files => [], :path => "/my/file" Puppet::FileServing::Fileset.stubs(:new).with("/my/file", {}).returns(@fileset) end it "should use a fileset to find paths" do @fileset = stub 'fileset', :files => [], :path => "/my/files" Puppet::FileServing::Fileset.expects(:new).with { |key, options| key == "/my/file" }.returns(@fileset) @helper.path2instances(@request, "/my/file") end it "should support finding across multiple paths by merging the filesets" do first = stub 'fileset', :files => [], :path => "/first/file" Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/first/file" }.returns(first) second = stub 'fileset', :files => [], :path => "/second/file" Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/second/file" }.returns(second) Puppet::FileServing::Fileset.expects(:merge).with(first, second).returns({}) @helper.path2instances(@request, "/first/file", "/second/file") end it "should pass the indirection request to the Fileset at initialization" do Puppet::FileServing::Fileset.expects(:new).with { |path, options| options == @request }.returns @fileset @helper.path2instances(@request, "/my/file") end describe "when creating instances" do before do @request.stubs(:key).returns "puppet://host/mount/dir" @one = stub 'one', :links= => nil, :collect => nil @two = stub 'two', :links= => nil, :collect => nil @fileset = stub 'fileset', :files => %w{one two}, :path => "/my/file" Puppet::FileServing::Fileset.stubs(:new).returns(@fileset) end it "should set each returned instance's path to the original path" do @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@one) @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@two) @helper.path2instances(@request, "/my/file") end it "should set each returned instance's relative path to the file-specific path" do @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(@one) @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(@two) @helper.path2instances(@request, "/my/file") end it "should set the links value on each instance if one is provided" do @one.expects(:links=).with :manage @two.expects(:links=).with :manage @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @request.options[:links] = :manage @helper.path2instances(@request, "/my/file") end it "should set the request checksum_type if one is provided" do @one.expects(:checksum_type=).with :test @two.expects(:checksum_type=).with :test @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @request.options[:checksum_type] = :test @helper.path2instances(@request, "/my/file") end it "should collect the instance's attributes" do @one.expects(:collect) @two.expects(:collect) @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @helper.path2instances(@request, "/my/file") end end end diff --git a/spec/unit/indirector/active_record_spec.rb b/spec/unit/indirector/active_record_spec.rb index 4fab17da2..2baef33fe 100755 --- a/spec/unit/indirector/active_record_spec.rb +++ b/spec/unit/indirector/active_record_spec.rb @@ -1,76 +1,75 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/rails' require 'puppet/indirector/active_record' describe Puppet::Indirector::ActiveRecord do before do Puppet::Rails.stubs(:init) Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @active_record_class = class Testing::MyActiveRecord < Puppet::Indirector::ActiveRecord self end @ar_model = mock 'ar_model' @active_record_class.use_ar_model @ar_model @terminus = @active_record_class.new @name = "me" @instance = stub 'instance', :name => @name @request = stub 'request', :key => @name, :instance => @instance end it "should allow declaration of an ActiveRecord model to use" do @active_record_class.use_ar_model "foo" @active_record_class.ar_model.should == "foo" end describe "when initializing" do it "should init Rails" do Puppet::Rails.expects(:init) @active_record_class.new end end describe "when finding an instance" do it "should use the ActiveRecord model to find the instance" do @ar_model.expects(:find_by_name).with(@name) @terminus.find(@request) end it "should return nil if no instance is found" do @ar_model.expects(:find_by_name).with(@name).returns nil @terminus.find(@request).should be_nil end it "should convert the instance to a Puppet object if it is found" do instance = mock 'rails_instance' instance.expects(:to_puppet).returns "mypuppet" @ar_model.expects(:find_by_name).with(@name).returns instance @terminus.find(@request).should == "mypuppet" end end describe "when saving an instance" do it "should use the ActiveRecord model to convert the instance into a Rails object and then save that rails object" do rails_object = mock 'rails_object' @ar_model.expects(:from_puppet).with(@instance).returns rails_object rails_object.expects(:save) @terminus.save(@request) end end end diff --git a/spec/unit/indirector/catalog/active_record_spec.rb b/spec/unit/indirector/catalog/active_record_spec.rb index da1d77582..35d01179b 100755 --- a/spec/unit/indirector/catalog/active_record_spec.rb +++ b/spec/unit/indirector/catalog/active_record_spec.rb @@ -1,157 +1,156 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "Puppet::Resource::Catalog::ActiveRecord", :if => Puppet.features.rails? do require 'puppet/rails' before :all do class Tableless < ActiveRecord::Base def self.columns @columns ||= [] end def self.column(name, sql_type=nil, default=nil, null=true) columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null) end end class Host < Tableless column :name, :string, :null => false column :ip, :string column :environment, :string column :last_compile, :datetime end end before do require 'puppet/indirector/catalog/active_record' Puppet.features.stubs(:rails?).returns true Puppet::Rails.stubs(:init) @terminus = Puppet::Resource::Catalog::ActiveRecord.new end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Resource::Catalog::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Resource::Catalog::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end describe "when finding an instance" do before do @request = stub 'request', :key => "foo", :options => {:cache_integration_hack => true} end # This hack is here because we don't want to look in the db unless we actually want # to look in the db, but our indirection architecture in 0.24.x isn't flexible # enough to tune that via configuration. it "should return nil unless ':cache_integration_hack' is set to true" do @request.options[:cache_integration_hack] = false Puppet::Rails::Host.expects(:find_by_name).never @terminus.find(@request).should be_nil end it "should use the Hosts ActiveRecord class to find the host" do Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } @terminus.find(@request) end it "should return nil if no host instance can be found" do Puppet::Rails::Host.expects(:find_by_name).returns nil @terminus.find(@request).should be_nil end it "should return a catalog with the same name as the host if the host can be found" do host = stub 'host', :name => "foo", :resources => [] Puppet::Rails::Host.expects(:find_by_name).returns host result = @terminus.find(@request) result.should be_instance_of(Puppet::Resource::Catalog) result.name.should == "foo" end it "should set each of the host's resources as a transportable resource within the catalog" do host = stub 'host', :name => "foo" Puppet::Rails::Host.expects(:find_by_name).returns host res1 = mock 'res1', :to_transportable => "trans_res1" res2 = mock 'res2', :to_transportable => "trans_res2" host.expects(:resources).returns [res1, res2] catalog = stub 'catalog' Puppet::Resource::Catalog.expects(:new).returns catalog catalog.expects(:add_resource).with "trans_res1" catalog.expects(:add_resource).with "trans_res2" @terminus.find(@request) end end describe "when saving an instance" do before do @host = Host.new(:name => "foo") @host.stubs(:merge_resources) @host.stubs(:save) @host.stubs(:railsmark).yields @node = Puppet::Node.new("foo", :environment => "environment") Puppet::Node.indirection.stubs(:find).with("foo").returns(@node) Puppet::Rails::Host.stubs(:find_by_name).returns @host @catalog = Puppet::Resource::Catalog.new("foo") @request = Puppet::Indirector::Request.new(:active_record, :save, @catalog) end it "should find the Rails host with the same name" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @terminus.save(@request) end it "should create a new Rails host if none can be found" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host @terminus.save(@request) end it "should set the catalog vertices as resources on the Rails host instance" do @catalog.expects(:vertices).returns "foo" @host.expects(:merge_resources).with("foo") @terminus.save(@request) end it "should set host ip if we could find a matching node" do @node.stubs(:parameters).returns({"ipaddress" => "192.168.0.1"}) @terminus.save(@request) @host.ip.should == '192.168.0.1' end it "should set host environment if we could find a matching node" do @terminus.save(@request) @host.environment.should == "environment" end it "should set the last compile time on the host" do now = Time.now Time.expects(:now).returns now @terminus.save(@request) @host.last_compile.should == now end it "should save the Rails host instance" do @host.expects(:save) @terminus.save(@request) end end end diff --git a/spec/unit/indirector/catalog/compiler_spec.rb b/spec/unit/indirector/catalog/compiler_spec.rb index a2ab0c6f4..cd84031e5 100755 --- a/spec/unit/indirector/catalog/compiler_spec.rb +++ b/spec/unit/indirector/catalog/compiler_spec.rb @@ -1,283 +1,283 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-23. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/catalog/compiler' require 'puppet/rails' describe Puppet::Resource::Catalog::Compiler do before do require 'puppet/rails' Puppet::Rails.stubs(:init) Facter.stubs(:to_hash).returns({}) Facter.stubs(:value).returns(Facter::Util::Fact.new("something")) end describe "when initializing" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") end it "should gather data about itself" do Puppet::Resource::Catalog::Compiler.new end it "should cache the server metadata and reuse it" do compiler = Puppet::Resource::Catalog::Compiler.new node1 = stub 'node1', :merge => nil node2 = stub 'node2', :merge => nil compiler.stubs(:compile) Puppet::Node.indirection.stubs(:find).with('node1').returns(node1) Puppet::Node.indirection.stubs(:find).with('node2').returns(node2) compiler.find(stub('request', :key => 'node1', :node => 'node1', :options => {})) compiler.find(stub('node2request', :key => 'node2', :node => 'node2', :options => {})) end it "should provide a method for determining if the catalog is networked" do compiler = Puppet::Resource::Catalog::Compiler.new compiler.should respond_to(:networked?) end describe "and storeconfigs is enabled" do before do Puppet.settings.expects(:value).with(:storeconfigs).returns true end it "should initialize Rails if it is available" do Puppet.features.expects(:rails?).returns true Puppet::Rails.expects(:init) Puppet::Resource::Catalog::Compiler.new end it "should fail if Rails is unavailable" do Puppet.features.expects(:rails?).returns false Puppet::Rails.expects(:init).never lambda { Puppet::Resource::Catalog::Compiler.new }.should raise_error(Puppet::Error) end end end describe "when finding catalogs" do before do Facter.stubs(:value).returns("whatever") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = Puppet::Node.new @name @node.stubs(:merge) Puppet::Node.indirection.stubs(:find).returns @node @request = stub 'request', :key => @name, :node => @name, :options => {} end it "should directly use provided nodes" do Puppet::Node.indirection.expects(:find).never @compiler.expects(:compile).with(@node) @request.stubs(:options).returns(:use_node => @node) @compiler.find(@request) end it "should use the authenticated node name if no request key is provided" do @request.stubs(:key).returns(nil) Puppet::Node.indirection.expects(:find).with(@name).returns(@node) @compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should use the provided node name by default" do @request.expects(:key).returns "my_node" Puppet::Node.indirection.expects(:find).with("my_node").returns @node @compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should fail if no node is passed and none can be found" do Puppet::Node.indirection.stubs(:find).with(@name).returns(nil) proc { @compiler.find(@request) }.should raise_error(ArgumentError) end it "should fail intelligently when searching for a node raises an exception" do Puppet::Node.indirection.stubs(:find).with(@name).raises "eh" proc { @compiler.find(@request) }.should raise_error(Puppet::Error) end it "should pass the found node to the compiler for compiling" do Puppet::Node.indirection.expects(:find).with(@name).returns(@node) config = mock 'config' Puppet::Parser::Compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should extract and save any facts from the request" do Puppet::Node.indirection.expects(:find).with(@name).returns @node @compiler.expects(:extract_facts_from_request).with(@request) Puppet::Parser::Compiler.stubs(:compile) @compiler.find(@request) end it "should return the results of compiling as the catalog" do Puppet::Node.indirection.stubs(:find).returns(@node) config = mock 'config' result = mock 'result' Puppet::Parser::Compiler.expects(:compile).returns result @compiler.find(@request).should equal(result) end it "should benchmark the compile process" do Puppet::Node.indirection.stubs(:find).returns(@node) @compiler.stubs(:networked?).returns(true) @compiler.expects(:benchmark).with do |level, message| level == :notice and message =~ /^Compiled catalog/ end Puppet::Parser::Compiler.stubs(:compile) @compiler.find(@request) end it "should log the benchmark result" do Puppet::Node.indirection.stubs(:find).returns(@node) @compiler.stubs(:networked?).returns(true) Puppet::Parser::Compiler.stubs(:compile) Puppet.expects(:notice).with { |msg| msg =~ /Compiled catalog/ } @compiler.find(@request) end end describe "when extracting facts from the request" do before do Facter.stubs(:value).returns "something" @compiler = Puppet::Resource::Catalog::Compiler.new @request = stub 'request', :options => {} @facts = Puppet::Node::Facts.new('hostname', "fact" => "value", "architecture" => "i386") Puppet::Node::Facts.indirection.stubs(:save).returns(nil) end it "should do nothing if no facts are provided" do Puppet::Node::Facts.indirection.expects(:convert_from).never @request.options[:facts] = nil @compiler.extract_facts_from_request(@request) end it "should use the Facts class to deserialize the provided facts and update the timestamp" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts @facts.timestamp = Time.parse('2010-11-01') @now = Time.parse('2010-11-02') Time.expects(:now).returns(@now) @compiler.extract_facts_from_request(@request) @facts.timestamp.should == @now end it "should use the provided fact format" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).with { |format, text| format == "foo" }.returns @facts @compiler.extract_facts_from_request(@request) end it "should convert the facts into a fact instance and save it" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts Puppet::Node::Facts.indirection.expects(:save).with(@facts) @compiler.extract_facts_from_request(@request) end end describe "when finding nodes" do before do Facter.stubs(:value).returns("whatever") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = stub 'request', :key => @name, :options => {} @compiler.stubs(:compile) end it "should look node information up via the Node class with the provided key" do @node.stubs :merge Puppet::Node.indirection.expects(:find).with(@name).returns(@node) @compiler.find(@request) end end describe "after finding nodes" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = stub 'request', :key => @name, :options => {} @compiler.stubs(:compile) Puppet::Node.indirection.stubs(:find).with(@name).returns(@node) end it "should add the server's Puppet version to the node's parameters as 'serverversion'" do @node.expects(:merge).with { |args| args["serverversion"] == "1" } @compiler.find(@request) end it "should add the server's fqdn to the node's parameters as 'servername'" do @node.expects(:merge).with { |args| args["servername"] == "my.server.com" } @compiler.find(@request) end it "should add the server's IP address to the node's parameters as 'serverip'" do @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" } @compiler.find(@request) end end describe "when filtering resources" do before :each do Facter.stubs(:value) @compiler = Puppet::Resource::Catalog::Compiler.new @catalog = stub_everything 'catalog' @catalog.stubs(:respond_to?).with(:filter).returns(true) end it "should delegate to the catalog instance filtering" do @catalog.expects(:filter) @compiler.filter(@catalog) end it "should filter out virtual resources" do resource = mock 'resource', :virtual? => true @catalog.stubs(:filter).yields(resource) @compiler.filter(@catalog) end it "should return the same catalog if it doesn't support filtering" do @catalog.stubs(:respond_to?).with(:filter).returns(false) @compiler.filter(@catalog).should == @catalog end it "should return the filtered catalog" do catalog = stub 'filtered catalog' @catalog.stubs(:filter).returns(catalog) @compiler.filter(@catalog).should == catalog end end end diff --git a/spec/unit/indirector/catalog/queue_spec.rb b/spec/unit/indirector/catalog/queue_spec.rb index 866113631..d396ad897 100755 --- a/spec/unit/indirector/catalog/queue_spec.rb +++ b/spec/unit/indirector/catalog/queue_spec.rb @@ -1,20 +1,19 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/catalog/queue' describe Puppet::Resource::Catalog::Queue do it 'should be a subclass of the Queue terminus' do Puppet::Resource::Catalog::Queue.superclass.should equal(Puppet::Indirector::Queue) end it 'should be registered with the catalog store indirection' do indirection = Puppet::Indirector::Indirection.instance(:catalog) Puppet::Resource::Catalog::Queue.indirection.should equal(indirection) end it 'shall be dubbed ":queue"' do Puppet::Resource::Catalog::Queue.name.should == :queue end end diff --git a/spec/unit/indirector/catalog/rest_spec.rb b/spec/unit/indirector/catalog/rest_spec.rb index c887504a2..3e674dde9 100755 --- a/spec/unit/indirector/catalog/rest_spec.rb +++ b/spec/unit/indirector/catalog/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/catalog/rest' describe Puppet::Resource::Catalog::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Resource::Catalog::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/catalog/yaml_spec.rb b/spec/unit/indirector/catalog/yaml_spec.rb index 3bf5b4640..ddaa173c6 100755 --- a/spec/unit/indirector/catalog/yaml_spec.rb +++ b/spec/unit/indirector/catalog/yaml_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/catalog' require 'puppet/indirector/catalog/yaml' describe Puppet::Resource::Catalog::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Resource::Catalog::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Resource::Catalog::Yaml.doc.should_not be_nil end it "should be registered with the catalog store indirection" do indirection = Puppet::Indirector::Indirection.instance(:catalog) Puppet::Resource::Catalog::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do Puppet::Resource::Catalog::Yaml.name.should == :yaml end end diff --git a/spec/unit/indirector/certificate/ca_spec.rb b/spec/unit/indirector/certificate/ca_spec.rb index 77384ddb8..277d2209d 100755 --- a/spec/unit/indirector/certificate/ca_spec.rb +++ b/spec/unit/indirector/certificate/ca_spec.rb @@ -1,28 +1,28 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/certificate/ca' describe Puppet::SSL::Certificate::Ca do it "should have documentation" do Puppet::SSL::Certificate::Ca.doc.should be_instance_of(String) end it "should use the :signeddir as the collection directory" do Puppet.settings.expects(:value).with(:signeddir).returns "/cert/dir" Puppet::SSL::Certificate::Ca.collection_directory.should == "/cert/dir" end it "should store the ca certificate at the :cacert location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" file = Puppet::SSL::Certificate::Ca.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/cert" end end diff --git a/spec/unit/indirector/certificate/file_spec.rb b/spec/unit/indirector/certificate/file_spec.rb index 45dd95cb5..f398e1115 100755 --- a/spec/unit/indirector/certificate/file_spec.rb +++ b/spec/unit/indirector/certificate/file_spec.rb @@ -1,28 +1,28 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/certificate/file' describe Puppet::SSL::Certificate::File do it "should have documentation" do Puppet::SSL::Certificate::File.doc.should be_instance_of(String) end it "should use the :certdir as the collection directory" do Puppet.settings.expects(:value).with(:certdir).returns "/cert/dir" Puppet::SSL::Certificate::File.collection_directory.should == "/cert/dir" end it "should store the ca certificate at the :localcacert location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert" file = Puppet::SSL::Certificate::File.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/cert" end end diff --git a/spec/unit/indirector/certificate/rest_spec.rb b/spec/unit/indirector/certificate/rest_spec.rb index 8fd6bff9a..21e10e316 100755 --- a/spec/unit/indirector/certificate/rest_spec.rb +++ b/spec/unit/indirector/certificate/rest_spec.rb @@ -1,58 +1,57 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/certificate/rest' describe Puppet::SSL::Certificate::Rest do before do @searcher = Puppet::SSL::Certificate::Rest.new end it "should be a sublcass of Puppet::Indirector::REST" do Puppet::SSL::Certificate::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should set server_setting to :ca_server" do Puppet::SSL::Certificate::Rest.server_setting.should == :ca_server end it "should set port_setting to :ca_port" do Puppet::SSL::Certificate::Rest.port_setting.should == :ca_port end it "should make sure found certificates have their names set to the search string" do terminus = Puppet::SSL::Certificate::Rest.new # This has 'boo.com' in the CN cert_string = "-----BEGIN CERTIFICATE----- MIICPzCCAaigAwIBAgIBBDANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtidWNr eS5sb2NhbDAeFw0wOTA5MTcxNzI1MzJaFw0xNDA5MTYxNzI1MzJaMBIxEDAOBgNV BAMMB2Jvby5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKG9B+DkTCNh F5xHchNDfnbC9NzWKM600oxrr84pgUVAG6B2wAZcdfoEtXszhsY9Jzpwqkvxk4Mx AbYqo9+TCi4UoiH6e+vAKOOJD3DHrlf+/RW4hGtyaI41DBhf4+B4/oFz5PH9mvKe NSfHFI/yPW+1IXYjxKLQNwF9E7q3JbnzAgMBAAGjgaAwgZ0wOAYJYIZIAYb4QgEN BCsWKVB1cHBldCBSdWJ5L09wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMAwG A1UdEwEB/wQCMAAwHQYDVR0OBBYEFJOxEUeyf4cNOBmf9zIaE1JTuNdLMAsGA1Ud DwQEAwIFoDAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwME MA0GCSqGSIb3DQEBBQUAA4GBAFTJxKprMg6tfhGnvEvURPmlJrINn9c2b5Y4AGYp tO86PFFkWw/EIJvvJzbj3s+Butr+eUo//+f1xxX7UCwwGqGxKqjtVS219oU/wkx8 h7rW4Xk7MrLl0auSS1p4wLcAMm+ZImf94+j8Cj+tkr8eGozZceRV13b8+EkdaE3S rn/G -----END CERTIFICATE----- " network = stub 'network' terminus.stubs(:network).returns network response = stub 'response', :code => "200", :body => cert_string response.stubs(:[]).with('content-type').returns "text/plain" response.stubs(:[]).with('content-encoding') network.expects(:get).returns response request = Puppet::Indirector::Request.new(:certificate, :find, "foo.com") result = terminus.find(request) result.should_not be_nil result.name.should == "foo.com" end end diff --git a/spec/unit/indirector/certificate_request/ca_spec.rb b/spec/unit/indirector/certificate_request/ca_spec.rb index 8c25e40f7..ebd64a2fb 100755 --- a/spec/unit/indirector/certificate_request/ca_spec.rb +++ b/spec/unit/indirector/certificate_request/ca_spec.rb @@ -1,19 +1,64 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' +require 'puppet/ssl/host' +require 'puppet/sslcertificates' +require 'puppet/sslcertificates/ca' require 'puppet/indirector/certificate_request/ca' describe Puppet::SSL::CertificateRequest::Ca do + include PuppetSpec::Files + + before :each do + Puppet[:ssldir] = tmpdir('ssl') + + Puppet::SSL::Host.ca_location = :local + Puppet[:localcacert] = Puppet[:cacert] + Puppet::SSLCertificates::CA.new.mkrootcert + + @ca = Puppet::SSL::CertificateAuthority.new + end + + after :all do + Puppet::SSL::Host.ca_location = :none + end + it "should have documentation" do Puppet::SSL::CertificateRequest::Ca.doc.should be_instance_of(String) end it "should use the :csrdir as the collection directory" do Puppet.settings.expects(:value).with(:csrdir).returns "/request/dir" Puppet::SSL::CertificateRequest::Ca.collection_directory.should == "/request/dir" end + + it "should overwrite the previous certificate request if allow_duplicate_certs is true" do + Puppet[:allow_duplicate_certs] = true + host = Puppet::SSL::Host.new("foo") + host.generate_certificate_request + @ca.sign(host.name) + + Puppet::SSL::Host.indirection.find("foo").generate_certificate_request + + Puppet::SSL::Certificate.indirection.find("foo").name.should == "foo" + Puppet::SSL::CertificateRequest.indirection.find("foo").name.should == "foo" + Puppet::SSL::Host.indirection.find("foo").state.should == "requested" + end + + it "should reject a new certificate request if allow_duplicate_certs is false" do + Puppet[:allow_duplicate_certs] = false + host = Puppet::SSL::Host.new("bar") + host.generate_certificate_request + @ca.sign(host.name) + + expect { Puppet::SSL::Host.indirection.find("bar").generate_certificate_request }.should raise_error(/ignoring certificate request/) + + Puppet::SSL::Certificate.indirection.find("bar").name.should == "bar" + Puppet::SSL::CertificateRequest.indirection.find("bar").should be_nil + Puppet::SSL::Host.indirection.find("bar").state.should == "signed" + end end diff --git a/spec/unit/indirector/certificate_request/file_spec.rb b/spec/unit/indirector/certificate_request/file_spec.rb index 465d55d3c..69dc5eb9c 100755 --- a/spec/unit/indirector/certificate_request/file_spec.rb +++ b/spec/unit/indirector/certificate_request/file_spec.rb @@ -1,19 +1,19 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/certificate_request/file' describe Puppet::SSL::CertificateRequest::File do it "should have documentation" do Puppet::SSL::CertificateRequest::File.doc.should be_instance_of(String) end it "should use the :requestdir as the collection directory" do Puppet.settings.expects(:value).with(:requestdir).returns "/request/dir" Puppet::SSL::CertificateRequest::File.collection_directory.should == "/request/dir" end end diff --git a/spec/unit/indirector/certificate_request/rest_spec.rb b/spec/unit/indirector/certificate_request/rest_spec.rb index 9a8b87439..398b91b84 100755 --- a/spec/unit/indirector/certificate_request/rest_spec.rb +++ b/spec/unit/indirector/certificate_request/rest_spec.rb @@ -1,23 +1,22 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/certificate_request/rest' describe Puppet::SSL::CertificateRequest::Rest do before do @searcher = Puppet::SSL::CertificateRequest::Rest.new end it "should be a sublcass of Puppet::Indirector::REST" do Puppet::SSL::CertificateRequest::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should set server_setting to :ca_server" do Puppet::SSL::CertificateRequest::Rest.server_setting.should == :ca_server end it "should set port_setting to :ca_port" do Puppet::SSL::CertificateRequest::Rest.port_setting.should == :ca_port end end diff --git a/spec/unit/indirector/certificate_revocation_list/ca_spec.rb b/spec/unit/indirector/certificate_revocation_list/ca_spec.rb index d72a3bb10..d76373b97 100755 --- a/spec/unit/indirector/certificate_revocation_list/ca_spec.rb +++ b/spec/unit/indirector/certificate_revocation_list/ca_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/certificate_revocation_list/ca' describe Puppet::SSL::CertificateRevocationList::Ca do it "should have documentation" do Puppet::SSL::CertificateRevocationList::Ca.doc.should be_instance_of(String) end it "should use the :cacrl setting as the crl location" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).with(:cacrl).returns "/request/dir" Puppet::SSL::CertificateRevocationList::Ca.new.path("whatever").should == "/request/dir" end end diff --git a/spec/unit/indirector/certificate_revocation_list/file_spec.rb b/spec/unit/indirector/certificate_revocation_list/file_spec.rb index 3284bee35..f4b8c36d3 100755 --- a/spec/unit/indirector/certificate_revocation_list/file_spec.rb +++ b/spec/unit/indirector/certificate_revocation_list/file_spec.rb @@ -1,20 +1,20 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/certificate_revocation_list/file' describe Puppet::SSL::CertificateRevocationList::File do it "should have documentation" do Puppet::SSL::CertificateRevocationList::File.doc.should be_instance_of(String) end it "should always store the file to :hostcrl location" do Puppet.settings.expects(:value).with(:hostcrl).returns "/host/crl" Puppet.settings.stubs(:use) Puppet::SSL::CertificateRevocationList::File.file_location.should == "/host/crl" end end diff --git a/spec/unit/indirector/certificate_revocation_list/rest_spec.rb b/spec/unit/indirector/certificate_revocation_list/rest_spec.rb index e47a0848c..238ba75ff 100755 --- a/spec/unit/indirector/certificate_revocation_list/rest_spec.rb +++ b/spec/unit/indirector/certificate_revocation_list/rest_spec.rb @@ -1,23 +1,22 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/certificate_revocation_list/rest' describe Puppet::SSL::CertificateRevocationList::Rest do before do @searcher = Puppet::SSL::CertificateRevocationList::Rest.new end it "should be a sublcass of Puppet::Indirector::REST" do Puppet::SSL::CertificateRevocationList::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should set server_setting to :ca_server" do Puppet::SSL::CertificateRevocationList::Rest.server_setting.should == :ca_server end it "should set port_setting to :ca_port" do Puppet::SSL::CertificateRevocationList::Rest.port_setting.should == :ca_port end end diff --git a/spec/unit/indirector/certificate_status/file_spec.rb b/spec/unit/indirector/certificate_status/file_spec.rb old mode 100644 new mode 100755 index 6cc0bb547..ae03aa9cb --- a/spec/unit/indirector/certificate_status/file_spec.rb +++ b/spec/unit/indirector/certificate_status/file_spec.rb @@ -1,188 +1,187 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/host' require 'puppet/indirector/certificate_status' require 'tempfile' describe "Puppet::Indirector::CertificateStatus::File" do include PuppetSpec::Files before do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true @terminus = Puppet::SSL::Host.indirection.terminus(:file) @tmpdir = tmpdir("certificate_status_ca_testing") Puppet[:confdir] = @tmpdir Puppet[:vardir] = @tmpdir # localcacert is where each client stores the CA certificate # cacert is where the master stores the CA certificate # Since we need to play the role of both for testing we need them to be the same and exist Puppet[:cacert] = Puppet[:localcacert] end def generate_csr(host) host.generate_key csr = Puppet::SSL::CertificateRequest.new(host.name) csr.generate(host.key.content) Puppet::SSL::CertificateRequest.indirection.save(csr) end def sign_csr(host) host.desired_state = "signed" @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host)) end def generate_signed_cert(host) generate_csr(host) sign_csr(host) @terminus.find(Puppet::Indirector::Request.new(:certificate_status, :find, host.name, host)) end def generate_revoked_cert(host) generate_signed_cert(host) host.desired_state = "revoked" @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host)) end it "should be a terminus on SSL::Host" do @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::File) end it "should create a CA instance if none is present" do @terminus.ca.should be_instance_of(Puppet::SSL::CertificateAuthority) end describe "when creating the CA" do it "should fail if it is not a valid CA" do Puppet::SSL::CertificateAuthority.expects(:ca?).returns false lambda { @terminus.ca }.should raise_error(ArgumentError, "This process is not configured as a certificate authority") end end it "should be indirected with the name 'certificate_status'" do Puppet::SSL::Host.indirection.name.should == :certificate_status end describe "when finding" do before do @host = Puppet::SSL::Host.new("foo") Puppet.settings.use(:main) end it "should return the Puppet::SSL::Host when a CSR exists for the host" do generate_csr(@host) request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) retrieved_host = @terminus.find(request) retrieved_host.name.should == @host.name retrieved_host.certificate_request.content.to_s.chomp.should == @host.certificate_request.content.to_s.chomp end it "should return the Puppet::SSL::Host when a public key exist for the host" do generate_signed_cert(@host) request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) retrieved_host = @terminus.find(request) retrieved_host.name.should == @host.name retrieved_host.certificate.content.to_s.chomp.should == @host.certificate.content.to_s.chomp end it "should return nil when neither a CSR nor public key exist for the host" do request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) @terminus.find(request).should == nil end end describe "when saving" do before do @host = Puppet::SSL::Host.new("foobar") Puppet.settings.use(:main) end describe "when signing a cert" do before do @host.desired_state = "signed" @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host) end it "should fail if no CSR is on disk" do lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /certificate request/) end it "should sign the on-disk CSR when it is present" do signed_host = generate_signed_cert(@host) signed_host.state.should == "signed" Puppet::SSL::Certificate.indirection.find("foobar").should be_instance_of(Puppet::SSL::Certificate) end end describe "when revoking a cert" do before do @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host) end it "should fail if no certificate is on disk" do @host.desired_state = "revoked" lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /Cannot revoke/) end it "should revoke the certificate when it is present" do generate_revoked_cert(@host) @host.state.should == 'revoked' end end end describe "when deleting" do before do Puppet.settings.use(:main) end it "should not delete anything if no certificate, request, or key is on disk" do host = Puppet::SSL::Host.new("clean_me") request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_me", host) @terminus.destroy(request).should == "Nothing was deleted" end it "should clean certs, cert requests, keys" do signed_host = Puppet::SSL::Host.new("clean_signed_cert") generate_signed_cert(signed_host) signed_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_signed_cert", signed_host) @terminus.destroy(signed_request).should == "Deleted for clean_signed_cert: Puppet::SSL::Certificate, Puppet::SSL::Key" requested_host = Puppet::SSL::Host.new("clean_csr") generate_csr(requested_host) csr_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_csr", requested_host) @terminus.destroy(csr_request).should == "Deleted for clean_csr: Puppet::SSL::CertificateRequest, Puppet::SSL::Key" end end describe "when searching" do it "should return a list of all hosts with certificate requests, signed certs, or revoked certs" do Puppet.settings.use(:main) signed_host = Puppet::SSL::Host.new("signed_host") generate_signed_cert(signed_host) requested_host = Puppet::SSL::Host.new("requested_host") generate_csr(requested_host) revoked_host = Puppet::SSL::Host.new("revoked_host") generate_revoked_cert(revoked_host) retrieved_hosts = @terminus.search(Puppet::Indirector::Request.new(:certificate_status, :search, "all", signed_host)) results = retrieved_hosts.map {|h| [h.name, h.state]}.sort{ |h,i| h[0] <=> i[0] } results.should == [["ca","signed"],["requested_host","requested"],["revoked_host","revoked"],["signed_host","signed"]] end end end diff --git a/spec/unit/indirector/certificate_status/rest_spec.rb b/spec/unit/indirector/certificate_status/rest_spec.rb old mode 100644 new mode 100755 index f44eac671..39fbb7024 --- a/spec/unit/indirector/certificate_status/rest_spec.rb +++ b/spec/unit/indirector/certificate_status/rest_spec.rb @@ -1,15 +1,14 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/host' require 'puppet/indirector/certificate_status' describe "Puppet::CertificateStatus::Rest" do before do @terminus = Puppet::SSL::Host.indirection.terminus(:rest) end it "should be a terminus on Puppet::SSL::Host" do @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::Rest) end end diff --git a/spec/unit/indirector/code_spec.rb b/spec/unit/indirector/code_spec.rb index 1c9e4d2f1..29369bf5e 100755 --- a/spec/unit/indirector/code_spec.rb +++ b/spec/unit/indirector/code_spec.rb @@ -1,32 +1,31 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/code' describe Puppet::Indirector::Code do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @code_class = class Testing::MyCode < Puppet::Indirector::Code self end @searcher = @code_class.new end it "should not have a find() method defined" do @searcher.should_not respond_to(:find) end it "should not have a save() method defined" do @searcher.should_not respond_to(:save) end it "should not have a destroy() method defined" do @searcher.should_not respond_to(:destroy) end end diff --git a/spec/unit/indirector/direct_file_server_spec.rb b/spec/unit/indirector/direct_file_server_spec.rb index 5d9af626f..abd7172b7 100755 --- a/spec/unit/indirector/direct_file_server_spec.rb +++ b/spec/unit/indirector/direct_file_server_spec.rb @@ -1,83 +1,83 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-24. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/indirector/direct_file_server' describe Puppet::Indirector::DirectFileServer do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @direct_file_class = class Testing::Mytype < Puppet::Indirector::DirectFileServer self end @server = @direct_file_class.new @uri = "file:///my/local" @request = Puppet::Indirector::Request.new(:mytype, :find, @uri) end describe Puppet::Indirector::DirectFileServer, "when finding a single file" do it "should return nil if the file does not exist" do FileTest.expects(:exists?).with("/my/local").returns false @server.find(@request).should be_nil end it "should return a Content instance created with the full path to the file if the file exists" do FileTest.expects(:exists?).with("/my/local").returns true @model.expects(:new).returns(:mycontent) @server.find(@request).should == :mycontent end end describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do before do @data = mock 'content' @data.stubs(:collect) FileTest.expects(:exists?).with("/my/local").returns true end it "should pass the full path to the instance" do @model.expects(:new).with { |key, options| key == "/my/local" }.returns(@data) @server.find(@request) end it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do @model.expects(:new).returns(@data) @data.expects(:links=).with(:manage) @request.stubs(:options).returns(:links => :manage) @server.find(@request) end end describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do it "should return nil if the file does not exist" do FileTest.expects(:exists?).with("/my/local").returns false @server.find(@request).should be_nil end it "should use :path2instances from the terminus_helper to return instances if the file exists" do FileTest.expects(:exists?).with("/my/local").returns true @server.expects(:path2instances) @server.search(@request) end it "should pass the original request to :path2instances" do FileTest.expects(:exists?).with("/my/local").returns true @server.expects(:path2instances).with(@request, "/my/local") @server.search(@request) end end end diff --git a/spec/unit/indirector/envelope_spec.rb b/spec/unit/indirector/envelope_spec.rb index 2715a2671..e056b768c 100755 --- a/spec/unit/indirector/envelope_spec.rb +++ b/spec/unit/indirector/envelope_spec.rb @@ -1,47 +1,46 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/envelope' describe Puppet::Indirector::Envelope do before do @instance = Object.new @instance.extend(Puppet::Indirector::Envelope) end it "should have an expiration accessor" do @instance.expiration = "testing" @instance.expiration.should == "testing" end it "should have an expiration setter" do @instance.should respond_to(:expiration=) end it "should have a means of testing whether it is expired" do @instance.should respond_to(:expired?) end describe "when testing if it is expired" do it "should return false if there is no expiration set" do @instance.should_not be_expired end it "should return true if the current date is after the expiration date" do @instance.expiration = Time.now - 10 @instance.should be_expired end it "should return false if the current date is prior to the expiration date" do @instance.expiration = Time.now + 10 @instance.should_not be_expired end it "should return false if the current date is equal to the expiration date" do now = Time.now Time.stubs(:now).returns(now) @instance.expiration = now @instance.should_not be_expired end end end diff --git a/spec/unit/indirector/exec_spec.rb b/spec/unit/indirector/exec_spec.rb index 5abb00ae9..1050ed4cc 100755 --- a/spec/unit/indirector/exec_spec.rb +++ b/spec/unit/indirector/exec_spec.rb @@ -1,55 +1,54 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/exec' describe Puppet::Indirector::Exec do before :all do @indirection = stub 'indirection', :name => :testing Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) module Testing; end @exec_class = class Testing::MyTesting < Puppet::Indirector::Exec attr_accessor :command self end end before :each do @searcher = @exec_class.new @searcher.command = ["/echo"] @request = stub 'request', :key => "foo" end it "should throw an exception if the command is not an array" do @searcher.command = "/usr/bin/echo" proc { @searcher.find(@request) }.should raise_error(Puppet::DevError) end it "should throw an exception if the command is not fully qualified" do @searcher.command = ["mycommand"] proc { @searcher.find(@request) }.should raise_error(ArgumentError) end it "should execute the command with the object name as the only argument" do @searcher.expects(:execute).with(%w{/echo foo}) @searcher.find(@request) end it "should return the output of the script" do @searcher.expects(:execute).with(%w{/echo foo}).returns("whatever") @searcher.find(@request).should == "whatever" end it "should return nil when the command produces no output" do @searcher.expects(:execute).with(%w{/echo foo}).returns(nil) @searcher.find(@request).should be_nil end it "should raise an exception if there's an execution failure" do @searcher.expects(:execute).with(%w{/echo foo}).raises(Puppet::ExecutionFailure.new("message")) lambda {@searcher.find(@request)}.should raise_exception(Puppet::Error, 'Failed to find foo via exec: message') end end diff --git a/spec/unit/indirector/facts/active_record_spec.rb b/spec/unit/indirector/facts/active_record_spec.rb index 71e19e3ca..01a906716 100755 --- a/spec/unit/indirector/facts/active_record_spec.rb +++ b/spec/unit/indirector/facts/active_record_spec.rb @@ -1,103 +1,102 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/rails' require 'puppet/node/facts' describe "Puppet::Node::Facts::ActiveRecord", :if => Puppet.features.rails? do before do require 'puppet/indirector/facts/active_record' Puppet.features.stubs(:rails?).returns true Puppet::Rails.stubs(:init) @terminus = Puppet::Node::Facts::ActiveRecord.new end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Node::Facts::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Node::Facts::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end describe "when finding an instance" do before do @request = stub 'request', :key => "foo" end it "should use the Hosts ActiveRecord class to find the host" do Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } @terminus.find(@request) end it "should include the fact names and values when finding the host" do Puppet::Rails::Host.expects(:find_by_name).with { |key, args| args[:include] == {:fact_values => :fact_name} } @terminus.find(@request) end it "should return nil if no host instance can be found" do Puppet::Rails::Host.expects(:find_by_name).returns nil @terminus.find(@request).should be_nil end it "should convert the node's parameters into a Facts instance if a host instance is found" do host = stub 'host', :name => "foo" host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")], "three" => [mock("three_value", :value => "four")]) Puppet::Rails::Host.expects(:find_by_name).returns host result = @terminus.find(@request) result.should be_instance_of(Puppet::Node::Facts) result.name.should == "foo" result.values.should == {"one" => "two", "three" => "four"} end it "should convert all single-member arrays into non-arrays" do host = stub 'host', :name => "foo" host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")]) Puppet::Rails::Host.expects(:find_by_name).returns host @terminus.find(@request).values["one"].should == "two" end end describe "when saving an instance" do before do @host = stub 'host', :name => "foo", :save => nil, :merge_facts => nil Puppet::Rails::Host.stubs(:find_by_name).returns @host @facts = Puppet::Node::Facts.new("foo", "one" => "two", "three" => "four") @request = stub 'request', :key => "foo", :instance => @facts end it "should find the Rails host with the same name" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @terminus.save(@request) end it "should create a new Rails host if none can be found" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host @terminus.save(@request) end it "should set the facts as facts on the Rails host instance" do # There is other stuff added to the hash. @host.expects(:merge_facts).with { |args| args["one"] == "two" and args["three"] == "four" } @terminus.save(@request) end it "should save the Rails host instance" do @host.expects(:save) @terminus.save(@request) end end end diff --git a/spec/unit/indirector/facts/couch_spec.rb b/spec/unit/indirector/facts/couch_spec.rb old mode 100644 new mode 100755 index 1f86b56f2..d0862486c --- a/spec/unit/indirector/facts/couch_spec.rb +++ b/spec/unit/indirector/facts/couch_spec.rb @@ -1,97 +1,102 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node/facts' +require 'puppet/indirector/facts/couch' -describe "Puppet::Node::Facts::Couch", :if => Puppet.features.couchdb? do - require 'puppet/indirector/facts/couch' if Puppet.features.couchdb? - - before do - @mock_db = mock('couch db') - mock_document = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.values) - mock_document.stubs(:database).returns(@mock_db) - @mock_db.stubs(:get).with(fake_request.key).returns(mock_document) - Puppet::Node::Facts::Couch.stubs(:db).returns(@mock_db) +describe "Puppet::Node::Facts::Couch" do + describe "when couchdb is not available", :unless => Puppet.features.couchdb? do + it "should fail to initialize" do + lambda { Puppet::Node::Facts::Couch.new }.should raise_error + end end - subject { Puppet::Node::Facts::Couch } - - describe "#find" do - describe "when the node document exists" do - it "should find the request by key" do - @mock_db.expects(:get).with(fake_request.key).returns({'_id' => fake_request.key, 'facts' => fake_request.instance.values}) - subject.new.find(fake_request).should == fake_request.instance - end + describe "when couchdb is available", :if => Puppet.features.couchdb? do + before do + @mock_db = mock('couch db') + mock_document = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.values) + mock_document.stubs(:database).returns(@mock_db) + @mock_db.stubs(:get).with(fake_request.key).returns(mock_document) + Puppet::Node::Facts::Couch.stubs(:db).returns(@mock_db) end - describe "when the node document does not exist" do - before do - @mock_db.expects(:get). - with(fake_request.key). - raises(RestClient::ResourceNotFound) - end + subject { Puppet::Node::Facts::Couch } - it "should return nil" do - subject.new.find(fake_request).should be_nil + describe "#find" do + describe "when the node document exists" do + it "should find the request by key" do + @mock_db.expects(:get).with(fake_request.key).returns({'_id' => fake_request.key, 'facts' => fake_request.instance.values}) + subject.new.find(fake_request).should == fake_request.instance + end end - it "should send Puppet a debug message" do - Puppet.expects(:debug).with("No couchdb document with id: test.local") - subject.new.find(fake_request).should be_nil - end + describe "when the node document does not exist" do + before do + @mock_db.expects(:get). + with(fake_request.key). + raises(RestClient::ResourceNotFound) + end - end - end + it "should return nil" do + subject.new.find(fake_request).should be_nil + end - describe "#save" do - describe "with options" do - subject do - lambda { Puppet::Node::Facts::Couch.new.save(fake_request([1])) } - end + it "should send Puppet a debug message" do + Puppet.expects(:debug).with("No couchdb document with id: test.local") + subject.new.find(fake_request).should be_nil + end - it { should raise_error(ArgumentError, "PUT does not accept options") } + end end - it "should save the json to the CouchDB database" do - @mock_db.expects(:save_doc).at_least_once.returns({'ok' => true }) - subject.new.save(fake_request) - end + describe "#save" do + describe "with options" do + subject do + lambda { Puppet::Node::Facts::Couch.new.save(fake_request([1])) } + end - describe "when the document exists" do - before do - @doc = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.instance.values) - @mock_db.expects(:get).with(fake_request.key).returns(@doc) + it { should raise_error(ArgumentError, "PUT does not accept options") } end - it "saves the document" do - @doc.expects(:save) + it "should save the json to the CouchDB database" do + @mock_db.expects(:save_doc).at_least_once.returns({'ok' => true }) subject.new.save(fake_request) end - end + describe "when the document exists" do + before do + @doc = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.instance.values) + @mock_db.expects(:get).with(fake_request.key).returns(@doc) + end + + it "saves the document" do + @doc.expects(:save) + subject.new.save(fake_request) + end - describe "when the document does not exist" do - before do - @mock_db.expects(:get). - with(fake_request.key). - raises(RestClient::ResourceNotFound) end - it "saves the document" do - @mock_db.expects(:save_doc) - subject.new.save(fake_request) + describe "when the document does not exist" do + before do + @mock_db.expects(:get). + with(fake_request.key). + raises(RestClient::ResourceNotFound) + end + + it "saves the document" do + @mock_db.expects(:save_doc) + subject.new.save(fake_request) + end + end end + def fake_request(options={}) + facts = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml', 'test.local.yaml')) + Struct.new(:instance, :key, :options).new(facts, facts.name, options) + end + private :fake_request end - - def fake_request(options={}) - facts = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml', 'test.local.yaml')) - Struct.new(:instance, :key, :options).new(facts, facts.name, options) - end - private :fake_request - end diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb index 3129f5ebf..9f5a0249b 100755 --- a/spec/unit/indirector/facts/facter_spec.rb +++ b/spec/unit/indirector/facts/facter_spec.rb @@ -1,143 +1,143 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-23. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/facts/facter' describe Puppet::Node::Facts::Facter do it "should be a subclass of the Code terminus" do Puppet::Node::Facts::Facter.superclass.should equal(Puppet::Indirector::Code) end it "should have documentation" do Puppet::Node::Facts::Facter.doc.should_not be_nil end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:facts) Puppet::Node::Facts::Facter.indirection.should equal(indirection) end it "should have its name set to :facter" do Puppet::Node::Facts::Facter.name.should == :facter end it "should load facts on initialization" do Puppet::Node::Facts::Facter.expects(:load_fact_plugins) Puppet::Node::Facts::Facter.new end end describe Puppet::Node::Facts::Facter do before :each do @facter = Puppet::Node::Facts::Facter.new Facter.stubs(:to_hash).returns({}) @name = "me" @request = stub 'request', :key => @name end describe Puppet::Node::Facts::Facter, " when finding facts" do it "should return a Facts instance" do @facter.find(@request).should be_instance_of(Puppet::Node::Facts) end it "should return a Facts instance with the provided key as the name" do @facter.find(@request).name.should == @name end it "should return the Facter facts as the values in the Facts instance" do Facter.expects(:to_hash).returns("one" => "two") facts = @facter.find(@request) facts.values["one"].should == "two" end it "should add local facts" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:add_local_facts) @facter.find(@request) end it "should convert all facts into strings" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:stringify) @facter.find(@request) end it "should call the downcase hook" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:downcase_if_necessary) @facter.find(@request) end end describe Puppet::Node::Facts::Facter, " when saving facts" do it "should fail" do proc { @facter.save(@facts) }.should raise_error(Puppet::DevError) end end describe Puppet::Node::Facts::Facter, " when destroying facts" do it "should fail" do proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError) end end it "should skip files when asked to load a directory" do FileTest.expects(:directory?).with("myfile").returns false Puppet::Node::Facts::Facter.load_facts_in_dir("myfile") end it "should load each ruby file when asked to load a directory" do FileTest.expects(:directory?).with("mydir").returns true Dir.expects(:chdir).with("mydir").yields Dir.expects(:glob).with("*.rb").returns %w{a.rb b.rb} Puppet::Node::Facts::Facter.expects(:load).with("a.rb") Puppet::Node::Facts::Facter.expects(:load).with("b.rb") Puppet::Node::Facts::Facter.load_facts_in_dir("mydir") end describe Puppet::Node::Facts::Facter, "when loading fact plugins from disk" do it "should load each directory in the Fact path" do Puppet.settings.stubs(:value).returns "foo" Puppet.settings.expects(:value).with(:factpath).returns("one#{File::PATH_SEPARATOR}two") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("one") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("two") Puppet::Node::Facts::Facter.load_fact_plugins end it "should load all facts from the modules" do Puppet.settings.stubs(:value).returns "foo" Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir) Puppet.settings.expects(:value).with(:modulepath).returns("one#{File::PATH_SEPARATOR}two") Dir.stubs(:glob).returns [] Dir.expects(:glob).with("one/*/lib/facter").returns %w{oneA oneB} Dir.expects(:glob).with("two/*/lib/facter").returns %w{twoA twoB} Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneA") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneB") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoA") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoB") Puppet::Node::Facts::Facter.load_fact_plugins end end end diff --git a/spec/unit/indirector/facts/inventory_active_record_spec.rb b/spec/unit/indirector/facts/inventory_active_record_spec.rb old mode 100644 new mode 100755 index 43b9fa397..edd03d8e4 --- a/spec/unit/indirector/facts/inventory_active_record_spec.rb +++ b/spec/unit/indirector/facts/inventory_active_record_spec.rb @@ -1,164 +1,167 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' -require 'sqlite3' rescue nil +#!/usr/bin/env rspec +require 'spec_helper' +begin + require 'sqlite3' +rescue LoadError +end require 'tempfile' require 'puppet/rails' describe "Puppet::Node::Facts::InventoryActiveRecord", :if => (Puppet.features.rails? and defined? SQLite3) do let(:terminus) { Puppet::Node::Facts::InventoryActiveRecord.new } before :all do require 'puppet/indirector/facts/inventory_active_record' @dbfile = Tempfile.new("testdb") @dbfile.close end after :all do Puppet::Node::Facts.indirection.reset_terminus_class @dbfile.unlink end before :each do Puppet::Node::Facts.indirection.terminus_class = :inventory_active_record Puppet[:dbadapter] = 'sqlite3' Puppet[:dblocation] = @dbfile.path Puppet[:railslog] = "/dev/null" Puppet::Rails.init end after :each do Puppet::Rails.teardown + ActiveRecord::Base.remove_connection end describe "#save" do it "should use an existing node if possible" do node = Puppet::Rails::InventoryNode.new(:name => "foo", :timestamp => Time.now) node.save facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryNode.count.should == 1 Puppet::Rails::InventoryNode.first.should == node end it "should create a new node if one can't be found" do # This test isn't valid if there are nodes to begin with Puppet::Rails::InventoryNode.count.should == 0 facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryNode.count.should == 1 Puppet::Rails::InventoryNode.first.name.should == "foo" end it "should save the facts" do facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should =~ [["uptime_days","60"],["kernel","Darwin"]] end it "should remove the previous facts for an existing node" do facts = Puppet::Node::Facts.new("foo", "uptime_days" => "30", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) bar_facts = Puppet::Node::Facts.new("bar", "uptime_days" => "35", "kernel" => "Linux") foo_facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "is_virtual" => "false") Puppet::Node::Facts.indirection.save(bar_facts) Puppet::Node::Facts.indirection.save(foo_facts) Puppet::Node::Facts.indirection.find("bar").should == bar_facts Puppet::Node::Facts.indirection.find("foo").should == foo_facts Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should_not include(["uptime_days", "30"], ["kernel", "Darwin"]) end end describe "#find" do before do @foo_facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") @bar_facts = Puppet::Node::Facts.new("bar", "uptime_days" => "30", "kernel" => "Linux") Puppet::Node::Facts.indirection.save(@foo_facts) Puppet::Node::Facts.indirection.save(@bar_facts) end it "should identify facts by node name" do Puppet::Node::Facts.indirection.find("foo").should == @foo_facts end it "should return nil if no node instance can be found" do Puppet::Node::Facts.indirection.find("non-existent node").should == nil end end describe "#search" do def search_request(conditions) Puppet::Indirector::Request.new(:facts, :search, nil, conditions) end before :each do @now = Time.now @foo = Puppet::Node::Facts.new("foo", "fact1" => "value1", "fact2" => "value2", "uptime_days" => "30") @bar = Puppet::Node::Facts.new("bar", "fact1" => "value1", "uptime_days" => "60") @baz = Puppet::Node::Facts.new("baz", "fact1" => "value2", "fact2" => "value1", "uptime_days" => "90") @bat = Puppet::Node::Facts.new("bat") @foo.timestamp = @now - 3600*1 @bar.timestamp = @now - 3600*3 @baz.timestamp = @now - 3600*5 @bat.timestamp = @now - 3600*7 [@foo, @bar, @baz, @bat].each {|facts| Puppet::Node::Facts.indirection.save(facts)} end it "should return node names that match 'equal' constraints" do request = search_request('facts.fact1.eq' => 'value1', 'facts.fact2.eq' => 'value2') terminus.search(request).should == ["foo"] end it "should return node names that match 'not equal' constraints" do request = search_request('facts.fact1.ne' => 'value2') terminus.search(request).should == ["bar","foo"] end it "should return node names that match strict inequality constraints" do request = search_request('facts.uptime_days.gt' => '20', 'facts.uptime_days.lt' => '70') terminus.search(request).should == ["bar","foo"] end it "should return node names that match non-strict inequality constraints" do request = search_request('facts.uptime_days.ge' => '30', 'facts.uptime_days.le' => '60') terminus.search(request).should == ["bar","foo"] end it "should return node names whose facts are within a given timeframe" do request = search_request('meta.timestamp.ge' => @now - 3600*5, 'meta.timestamp.le' => @now - 3600*1) terminus.search(request).should == ["bar","baz","foo"] end it "should return node names whose facts are from a specific time" do request = search_request('meta.timestamp.eq' => @now - 3600*3) terminus.search(request).should == ["bar"] end it "should return node names whose facts are not from a specific time" do request = search_request('meta.timestamp.ne' => @now - 3600*1) terminus.search(request).should == ["bar","bat","baz"] end it "should perform strict searches on nodes by timestamp" do request = search_request('meta.timestamp.gt' => @now - 3600*5, 'meta.timestamp.lt' => @now - 3600*1) terminus.search(request).should == ["bar"] end it "should search nodes based on both facts and timestamp values" do request = search_request('facts.uptime_days.gt' => '45', 'meta.timestamp.lt' => @now - 3600*4) terminus.search(request).should == ["baz"] end end end diff --git a/spec/unit/indirector/facts/rest_spec.rb b/spec/unit/indirector/facts/rest_spec.rb index 03bef4578..6a2a23f8b 100755 --- a/spec/unit/indirector/facts/rest_spec.rb +++ b/spec/unit/indirector/facts/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/facts/rest' describe Puppet::Node::Facts::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Node::Facts::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/facts/yaml_spec.rb b/spec/unit/indirector/facts/yaml_spec.rb index c266df18f..a22d690b8 100755 --- a/spec/unit/indirector/facts/yaml_spec.rb +++ b/spec/unit/indirector/facts/yaml_spec.rb @@ -1,240 +1,239 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node/facts' require 'puppet/indirector/facts/yaml' describe Puppet::Node::Facts::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Node::Facts::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Node::Facts::Yaml.doc.should_not be_nil Puppet::Node::Facts::Yaml.doc.should_not be_empty end it "should be registered with the facts indirection" do indirection = Puppet::Indirector::Indirection.instance(:facts) Puppet::Node::Facts::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do Puppet::Node::Facts::Yaml.name.should == :yaml end describe "#search" do def assert_search_matches(matching, nonmatching, query) request = Puppet::Indirector::Request.new(:inventory, :search, nil, query) Dir.stubs(:glob).returns(matching.keys + nonmatching.keys) [matching, nonmatching].each do |examples| examples.each do |key, value| YAML.stubs(:load_file).with(key).returns value end end Puppet::Node::Facts::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name} end it "should return node names that match the search query options" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '4'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "i386", 'processor_count' => '4', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), }, {'facts.architecture' => 'i386', 'facts.processor_count' => '4'} ) end it "should return empty array when no nodes match the search query options" do assert_search_matches({}, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '10'), "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), }, {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'} ) end it "should return node names that match the search query options with the greater than operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '10', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '3'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.gt' => '4'} ) end it "should return node names that match the search query options with the less than operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '30', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '50' ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '100'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.lt' => '50'} ) end it "should return node names that match the search query options with the less than or equal to operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '100' ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5000'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.le' => '50'} ) end it "should return node names that match the search query options with the greater than or equal to operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '100'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '40'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.ge' => '50'} ) end it "should return node names that match the search query options with the not equal operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => 'arm' ), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => 'powerpc', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "i386" ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.architecture.ne' => 'i386'} ) end def apply_timestamp(facts, timestamp) facts.timestamp = timestamp facts end it "should be able to query based on meta.timestamp.gt" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), }, { '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.gt' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.le" do assert_search_matches({ '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), }, {'meta.timestamp.le' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.lt" do assert_search_matches({ '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.lt' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.ge" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp.ge' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.eq" do assert_search_matches({ '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp.eq' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp" do assert_search_matches({ '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.ne" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, { '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.ne' => '2010-10-15'} ) end end end diff --git a/spec/unit/indirector/file_bucket_file/file_spec.rb b/spec/unit/indirector/file_bucket_file/file_spec.rb index 1423cb2e3..e0612cb21 100755 --- a/spec/unit/indirector/file_bucket_file/file_spec.rb +++ b/spec/unit/indirector/file_bucket_file/file_spec.rb @@ -1,274 +1,273 @@ -#!/usr/bin/env ruby - -require ::File.dirname(__FILE__) + '/../../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/file_bucket_file/file' describe Puppet::FileBucketFile::File do include PuppetSpec::Files it "should be a subclass of the Code terminus class" do Puppet::FileBucketFile::File.superclass.should equal(Puppet::Indirector::Code) end it "should have documentation" do Puppet::FileBucketFile::File.doc.should be_instance_of(String) end describe "non-stubbing tests" do include PuppetSpec::Files before do Puppet[:bucketdir] = tmpdir('bucketdir') end def save_bucket_file(contents, path = "/who_cares") bucket_file = Puppet::FileBucket::File.new(contents) Puppet::FileBucket::File.indirection.save(bucket_file, "md5/#{Digest::MD5.hexdigest(contents)}#{path}") bucket_file.checksum_data end describe "when servicing a save request" do describe "when supplying a path" do it "should store the path if not already stored" do checksum = save_bucket_file("stuff", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\n" end it "should leave the paths file alone if the path is already stored" do checksum = save_bucket_file("stuff", "/foo/bar") checksum = save_bucket_file("stuff", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\n" end it "should store an additional path if the new path differs from those already stored" do checksum = save_bucket_file("stuff", "/foo/bar") checksum = save_bucket_file("stuff", "/foo/baz") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\nfoo/baz\n" end end describe "when not supplying a path" do it "should save the file and create an empty paths file" do checksum = save_bucket_file("stuff", "") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "" end end end describe "when servicing a head/find request" do describe "when supplying a path" do it "should return false/nil if the file isn't bucketed" do Puppet::FileBucket::File.indirection.head("md5/0ae2ec1980410229885fe72f7b44fe55/foo/bar").should == false Puppet::FileBucket::File.indirection.find("md5/0ae2ec1980410229885fe72f7b44fe55/foo/bar").should == nil end it "should return false/nil if the file is bucketed but with a different path" do checksum = save_bucket_file("I'm the contents of a file", '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}/foo/baz").should == false Puppet::FileBucket::File.indirection.find("md5/#{checksum}/foo/baz").should == nil end it "should return true/file if the file is already bucketed with the given path" do contents = "I'm the contents of a file" checksum = save_bucket_file(contents, '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}/foo/bar").should == true find_result = Puppet::FileBucket::File.indirection.find("md5/#{checksum}/foo/bar") find_result.should be_a(Puppet::FileBucket::File) find_result.checksum.should == "{md5}#{checksum}" find_result.to_s.should == contents end end describe "when not supplying a path" do [false, true].each do |trailing_slash| describe "#{trailing_slash ? 'with' : 'without'} a trailing slash" do trailing_string = trailing_slash ? '/' : '' it "should return false/nil if the file isn't bucketed" do Puppet::FileBucket::File.indirection.head("md5/0ae2ec1980410229885fe72f7b44fe55#{trailing_string}").should == false Puppet::FileBucket::File.indirection.find("md5/0ae2ec1980410229885fe72f7b44fe55#{trailing_string}").should == nil end it "should return true/file if the file is already bucketed" do contents = "I'm the contents of a file" checksum = save_bucket_file(contents, '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}#{trailing_string}").should == true find_result = Puppet::FileBucket::File.indirection.find("md5/#{checksum}#{trailing_string}") find_result.should be_a(Puppet::FileBucket::File) find_result.checksum.should == "{md5}#{checksum}" find_result.to_s.should == contents end end end end end describe "when diffing files" do it "should generate an empty string if there is no diff" do checksum = save_bucket_file("I'm the contents of a file") Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => checksum).should == '' end it "should generate a proper diff if there is a diff" do checksum1 = save_bucket_file("foo\nbar\nbaz") checksum2 = save_bucket_file("foo\nbiz\nbaz") diff = Puppet::FileBucket::File.indirection.find("md5/#{checksum1}", :diff_with => checksum2) diff.should == < biz HERE end it "should raise an exception if the hash to diff against isn't found" do checksum = save_bucket_file("whatever") bogus_checksum = "d1bf072d0e2c6e20e3fbd23f022089a1" lambda { Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => bogus_checksum) }.should raise_error "could not find diff_with #{bogus_checksum}" end it "should return nil if the hash to diff from isn't found" do checksum = save_bucket_file("whatever") bogus_checksum = "d1bf072d0e2c6e20e3fbd23f022089a1" Puppet::FileBucket::File.indirection.find("md5/#{bogus_checksum}", :diff_with => checksum).should == nil end end end describe "when initializing" do it "should use the filebucket settings section" do Puppet.settings.expects(:use).with(:filebucket) Puppet::FileBucketFile::File.new end end [true, false].each do |override_bucket_path| describe "when bucket path #{if override_bucket_path then 'is' else 'is not' end} overridden" do [true, false].each do |supply_path| describe "when #{supply_path ? 'supplying' : 'not supplying'} a path" do before :each do Puppet.settings.stubs(:use) @store = Puppet::FileBucketFile::File.new @contents = "my content" @digest = "f2bfa7fc155c4f42cb91404198dda01f" @digest.should == Digest::MD5.hexdigest(@contents) @bucket_dir = tmpdir("bucket") if override_bucket_path Puppet[:bucketdir] = "/bogus/path" # should not be used else Puppet[:bucketdir] = @bucket_dir end @dir = "#{@bucket_dir}/f/2/b/f/a/7/f/c/f2bfa7fc155c4f42cb91404198dda01f" @contents_path = "#{@dir}/contents" end describe "when retrieving files" do before :each do request_options = {} if override_bucket_path request_options[:bucket_path] = @bucket_dir end key = "md5/#{@digest}" if supply_path key += "/path/to/file" end @request = Puppet::Indirector::Request.new(:indirection_name, :find, key, request_options) end def make_bucketed_file FileUtils.mkdir_p(@dir) File.open(@contents_path, 'w') { |f| f.write @contents } end it "should return an instance of Puppet::FileBucket::File created with the content if the file exists" do make_bucketed_file if supply_path @store.find(@request).should == nil @store.head(@request).should == false # because path didn't match else bucketfile = @store.find(@request) bucketfile.should be_a(Puppet::FileBucket::File) bucketfile.contents.should == @contents @store.head(@request).should == true end end it "should return nil if no file is found" do @store.find(@request).should be_nil @store.head(@request).should == false end end describe "when saving files" do it "should save the contents to the calculated path" do options = {} if override_bucket_path options[:bucket_path] = @bucket_dir end key = "md5/#{@digest}" if supply_path key += "//path/to/file" end file_instance = Puppet::FileBucket::File.new(@contents, options) request = Puppet::Indirector::Request.new(:indirection_name, :save, key, file_instance) @store.save(request) File.read("#{@dir}/contents").should == @contents end end end end end end describe "when verifying identical files" do before do # this is the default from spec_helper, but it keeps getting reset at odd times Puppet[:bucketdir] = "/dev/null/bucket" @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' @contents = "file contents" @bucket = stub "bucket file" @bucket.stubs(:bucket_path) @bucket.stubs(:checksum).returns(@checksum) @bucket.stubs(:checksum_data).returns(@digest) @bucket.stubs(:path).returns(nil) @bucket.stubs(:contents).returns("file contents") end it "should raise an error if the files don't match" do File.expects(:read).with("#{@dir}/contents").returns("corrupt contents") lambda{ Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) }.should raise_error(Puppet::FileBucket::BucketError) end it "should do nothing if the files match" do File.expects(:read).with("#{@dir}/contents").returns("file contents") Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) end end end diff --git a/spec/unit/indirector/file_bucket_file/rest_spec.rb b/spec/unit/indirector/file_bucket_file/rest_spec.rb index d0f714751..ae2e033c6 100755 --- a/spec/unit/indirector/file_bucket_file/rest_spec.rb +++ b/spec/unit/indirector/file_bucket_file/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::FileBucketFile::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::FileBucketFile::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/file_content/file_server_spec.rb b/spec/unit/indirector/file_content/file_server_spec.rb index 5bd88132c..99a535dc3 100755 --- a/spec/unit/indirector/file_content/file_server_spec.rb +++ b/spec/unit/indirector/file_content/file_server_spec.rb @@ -1,18 +1,18 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_content/file_server' describe Puppet::Indirector::FileContent::FileServer do it "should be registered with the file_content indirection" do Puppet::Indirector::Terminus.terminus_class(:file_content, :file_server).should equal(Puppet::Indirector::FileContent::FileServer) end it "should be a subclass of the FileServer terminus" do Puppet::Indirector::FileContent::FileServer.superclass.should equal(Puppet::Indirector::FileServer) end end diff --git a/spec/unit/indirector/file_content/file_spec.rb b/spec/unit/indirector/file_content/file_spec.rb index edead1c70..b629981c5 100755 --- a/spec/unit/indirector/file_content/file_spec.rb +++ b/spec/unit/indirector/file_content/file_spec.rb @@ -1,18 +1,18 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_content/file' describe Puppet::Indirector::FileContent::File do it "should be registered with the file_content indirection" do Puppet::Indirector::Terminus.terminus_class(:file_content, :file).should equal(Puppet::Indirector::FileContent::File) end it "should be a subclass of the DirectFileServer terminus" do Puppet::Indirector::FileContent::File.superclass.should equal(Puppet::Indirector::DirectFileServer) end end diff --git a/spec/unit/indirector/file_content/rest_spec.rb b/spec/unit/indirector/file_content/rest_spec.rb index cf94d2ffd..06ad16e77 100755 --- a/spec/unit/indirector/file_content/rest_spec.rb +++ b/spec/unit/indirector/file_content/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/file_content' describe "Puppet::Indirector::Content::Rest" do it "should add the node's cert name to the arguments" it "should set the content type to text/plain" end diff --git a/spec/unit/indirector/file_metadata/file_server_spec.rb b/spec/unit/indirector/file_metadata/file_server_spec.rb index 99eb83d7f..e16829035 100755 --- a/spec/unit/indirector/file_metadata/file_server_spec.rb +++ b/spec/unit/indirector/file_metadata/file_server_spec.rb @@ -1,18 +1,18 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_metadata/file_server' describe Puppet::Indirector::FileMetadata::FileServer do it "should be registered with the file_metadata indirection" do Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file_server).should equal(Puppet::Indirector::FileMetadata::FileServer) end it "should be a subclass of the FileServer terminus" do Puppet::Indirector::FileMetadata::FileServer.superclass.should equal(Puppet::Indirector::FileServer) end end diff --git a/spec/unit/indirector/file_metadata/file_spec.rb b/spec/unit/indirector/file_metadata/file_spec.rb index 74eff5b66..28a974290 100755 --- a/spec/unit/indirector/file_metadata/file_spec.rb +++ b/spec/unit/indirector/file_metadata/file_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_metadata/file' describe Puppet::Indirector::FileMetadata::File do it "should be registered with the file_metadata indirection" do Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file).should equal(Puppet::Indirector::FileMetadata::File) end it "should be a subclass of the DirectFileServer terminus" do Puppet::Indirector::FileMetadata::File.superclass.should equal(Puppet::Indirector::DirectFileServer) end describe "when creating the instance for a single found file" do before do @metadata = Puppet::Indirector::FileMetadata::File.new @uri = "file:///my/local" @data = mock 'metadata' @data.stubs(:collect) FileTest.expects(:exists?).with("/my/local").returns true @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri) end it "should collect its attributes when a file is found" do @data.expects(:collect) Puppet::FileServing::Metadata.expects(:new).returns(@data) @metadata.find(@request).should == @data end end describe "when searching for multiple files" do before do @metadata = Puppet::Indirector::FileMetadata::File.new @uri = "file:///my/local" @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri) end it "should collect the attributes of the instances returned" do FileTest.expects(:exists?).with("/my/local").returns true @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] ) @metadata.search(@request) end end end diff --git a/spec/unit/indirector/file_metadata/rest_spec.rb b/spec/unit/indirector/file_metadata/rest_spec.rb index 132d90d4f..511f7c758 100755 --- a/spec/unit/indirector/file_metadata/rest_spec.rb +++ b/spec/unit/indirector/file_metadata/rest_spec.rb @@ -1,9 +1,8 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/file_metadata' describe "Puppet::Indirector::Metadata::Rest" do it "should add the node's cert name to the arguments" end diff --git a/spec/unit/indirector/file_server_spec.rb b/spec/unit/indirector/file_server_spec.rb index 079eba0ae..6df715fb1 100755 --- a/spec/unit/indirector/file_server_spec.rb +++ b/spec/unit/indirector/file_server_spec.rb @@ -1,267 +1,267 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-19. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/indirector/file_server' require 'puppet/file_serving/configuration' describe Puppet::Indirector::FileServer do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @file_server_class = class Testing::MyFileServer < Puppet::Indirector::FileServer self end end before :each do @file_server = @file_server_class.new @uri = "puppet://host/my/local/file" @configuration = mock 'configuration' Puppet::FileServing::Configuration.stubs(:create).returns(@configuration) @request = Puppet::Indirector::Request.new(:myind, :mymethod, @uri, :environment => "myenv") end describe "when finding files" do before do @mount = stub 'mount', :find => nil @instance = stub('instance', :links= => nil, :collect => nil) end it "should use the configuration to find the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.find(@request) end it "should return nil if it cannot find the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) @file_server.find(@request).should be_nil end it "should use the mount to find the full path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" } @file_server.find(@request) end it "should pass the request when finding a file" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| request == @request } @file_server.find(@request) end it "should return nil if it cannot find a full path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns nil @file_server.find(@request).should be_nil end it "should create an instance with the found path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" @model.expects(:new).with("/my/file").returns @instance @file_server.find(@request).should equal(@instance) end it "should set 'links' on the instance if it is set in the request options" do @request.options[:links] = true @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" @model.expects(:new).with("/my/file").returns @instance @instance.expects(:links=).with(true) @file_server.find(@request).should equal(@instance) end it "should collect the instance" do @request.options[:links] = true @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" @model.expects(:new).with("/my/file").returns @instance @instance.expects(:collect) @file_server.find(@request).should equal(@instance) end end describe "when searching for instances" do before do @mount = stub 'mount', :search => nil @instance = stub('instance', :links= => nil, :collect => nil) end it "should use the configuration to search the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.search(@request) end it "should return nil if it cannot search the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) @file_server.search(@request).should be_nil end it "should use the mount to search for the full paths" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" } @file_server.search(@request) end it "should pass the request" do @configuration.stubs(:split_path).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| request == @request } @file_server.search(@request) end it "should return nil if searching does not find any full paths" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns nil @file_server.search(@request).should be_nil end it "should create a fileset with each returned path and merge them" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two} FileTest.stubs(:exist?).returns true one = mock 'fileset_one' Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one) two = mock 'fileset_two' Puppet::FileServing::Fileset.expects(:new).with("/two", @request).returns(two) Puppet::FileServing::Fileset.expects(:merge).with(one, two).returns [] @file_server.search(@request) end it "should create an instance with each path resulting from the merger of the filesets" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] FileTest.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two") one = stub 'one', :collect => nil @model.expects(:new).with("/one", :relative_path => "one").returns one two = stub 'two', :collect => nil @model.expects(:new).with("/two", :relative_path => "two").returns two # order can't be guaranteed result = @file_server.search(@request) result.should be_include(one) result.should be_include(two) result.length.should == 2 end it "should set 'links' on the instances if it is set in the request options" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] FileTest.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") one = stub 'one', :collect => nil @model.expects(:new).with("/one", :relative_path => "one").returns one one.expects(:links=).with true @request.options[:links] = true @file_server.search(@request) end it "should collect the instances" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, options| key == "rel/path" }.returns [] FileTest.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") one = mock 'one' @model.expects(:new).with("/one", :relative_path => "one").returns one one.expects(:collect) @file_server.search(@request) end end describe "when checking authorization" do before do @request.method = :find @mount = stub 'mount' @configuration.stubs(:split_path).with(@request).returns([@mount, "rel/path"]) @request.stubs(:node).returns("mynode") @request.stubs(:ip).returns("myip") @mount.stubs(:allowed?).with("mynode", "myip").returns "something" end it "should return false when destroying" do @request.method = :destroy @file_server.should_not be_authorized(@request) end it "should return false when saving" do @request.method = :save @file_server.should_not be_authorized(@request) end it "should use the configuration to find the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.authorized?(@request) end it "should return false if it cannot find the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) @file_server.should_not be_authorized(@request) end it "should return the results of asking the mount whether the node and IP are authorized" do @file_server.authorized?(@request).should == "something" end end end diff --git a/spec/unit/indirector/file_spec.rb b/spec/unit/indirector/file_spec.rb index 96d5b2ae7..b72bf4d67 100755 --- a/spec/unit/indirector/file_spec.rb +++ b/spec/unit/indirector/file_spec.rb @@ -1,180 +1,179 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/file' describe Puppet::Indirector::File do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @file_class = class Testing::MyFile < Puppet::Indirector::File self end @searcher = @file_class.new @path = "/my/file" @dir = "/my" @request = stub 'request', :key => @path end describe "when finding files" do it "should provide a method to return file contents at a specified path" do @searcher.should respond_to(:find) end it "should use the server data directory plus the indirection name if the run_mode is master" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:server_datadir).returns "/my/dir" @searcher.data_directory.should == File.join("/my/dir", "mystuff") end it "should use the client data directory plus the indirection name if the run_mode is not master" do Puppet.run_mode.expects(:master?).returns false Puppet.settings.expects(:value).with(:client_datadir).returns "/my/dir" @searcher.data_directory.should == File.join("/my/dir", "mystuff") end it "should use the newest file in the data directory matching the indirection key without extension" do @searcher.expects(:data_directory).returns "/data/dir" @request.stubs(:key).returns "foo" Dir.expects(:glob).with("/data/dir/foo.*").returns %w{/data1.stuff /data2.stuff} stat1 = stub 'data1', :mtime => (Time.now - 5) stat2 = stub 'data2', :mtime => Time.now File.expects(:stat).with("/data1.stuff").returns stat1 File.expects(:stat).with("/data2.stuff").returns stat2 @searcher.latest_path(@request).should == "/data2.stuff" end it "should return nil when no files are found" do @searcher.stubs(:latest_path).returns nil @searcher.find(@request).should be_nil end it "should determine the file format from the file extension" do @searcher.file_format("/data2.pson").should == "pson" end it "should fail if the model does not support the file format" do @searcher.stubs(:latest_path).returns "/my/file.pson" @model.expects(:support_format?).with("pson").returns false lambda { @searcher.find(@request) }.should raise_error(ArgumentError) end end describe "when saving files" do before do @content = "my content" @file = stub 'file', :content => @content, :path => @path, :name => @path, :render => "mydata" @request.stubs(:instance).returns @file end it "should provide a method to save file contents at a specified path" do @searcher.should respond_to(:save) end it "should choose the file extension based on the default format of the model" do @model.expects(:default_format).returns "pson" @searcher.serialization_format.should == "pson" end it "should place the file in the data directory, named after the indirection, key, and format" do @searcher.stubs(:data_directory).returns "/my/dir" @searcher.stubs(:serialization_format).returns "pson" @request.stubs(:key).returns "foo" @searcher.file_path(@request).should == File.join("/my/dir", "foo.pson") end it "should fail intelligently if the file's parent directory does not exist" do @searcher.stubs(:file_path).returns "/my/dir/file.pson" @searcher.stubs(:serialization_format).returns "pson" @request.stubs(:key).returns "foo" File.expects(:directory?).with(File.join("/my/dir")).returns(false) proc { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should render the instance using the file format and print it to the file path" do @searcher.stubs(:file_path).returns "/my/file.pson" @searcher.stubs(:serialization_format).returns "pson" File.stubs(:directory?).returns true @request.instance.expects(:render).with("pson").returns "data" fh = mock 'filehandle' File.expects(:open).with("/my/file.pson", "w").yields fh fh.expects(:print).with("data") @searcher.save(@request) end it "should fail intelligently if a file cannot be written" do filehandle = mock 'file' File.stubs(:directory?).returns(true) File.stubs(:open).yields(filehandle) filehandle.expects(:print).raises(ArgumentError) @searcher.stubs(:file_path).returns "/my/file.pson" @model.stubs(:default_format).returns "pson" @instance.stubs(:render).returns "stuff" proc { @searcher.save(@request) }.should raise_error(Puppet::Error) end end describe "when removing files" do it "should provide a method to remove files" do @searcher.should respond_to(:destroy) end it "should remove files in all formats found in the data directory that match the request key" do @searcher.stubs(:data_directory).returns "/my/dir" @request.stubs(:key).returns "me" Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one /two} File.expects(:unlink).with("/one") File.expects(:unlink).with("/two") @searcher.destroy(@request) end it "should throw an exception if no file is found" do @searcher.stubs(:data_directory).returns "/my/dir" @request.stubs(:key).returns "me" Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns [] proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) end it "should fail intelligently if a file cannot be removed" do @searcher.stubs(:data_directory).returns "/my/dir" @request.stubs(:key).returns "me" Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one} File.expects(:unlink).with("/one").raises ArgumentError proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) end end end diff --git a/spec/unit/indirector/indirection_spec.rb b/spec/unit/indirector/indirection_spec.rb index 8795ae795..4bbc855b1 100755 --- a/spec/unit/indirector/indirection_spec.rb +++ b/spec/unit/indirector/indirection_spec.rb @@ -1,861 +1,860 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/indirection' shared_examples_for "Indirection Delegator" do it "should create a request object with the appropriate method name and all of the passed arguments" do request = Puppet::Indirector::Request.new(:indirection, :find, "me") @indirection.expects(:request).with(@method, "mystuff", :one => :two).returns request @terminus.stubs(@method) @indirection.send(@method, "mystuff", :one => :two) end it "should let the :select_terminus method choose the terminus using the created request if the :select_terminus method is available" do # Define the method, so our respond_to? hook matches. class << @indirection def select_terminus(request) end end request = Puppet::Indirector::Request.new(:indirection, :find, "me") @indirection.stubs(:request).returns request @indirection.expects(:select_terminus).with(request).returns :test_terminus @indirection.stubs(:check_authorization) @terminus.expects(@method) @indirection.send(@method, "me") end it "should fail if the :select_terminus hook does not return a terminus name" do # Define the method, so our respond_to? hook matches. class << @indirection def select_terminus(request) end end request = stub 'request', :key => "me", :options => {} @indirection.stubs(:request).returns request @indirection.expects(:select_terminus).with(request).returns nil lambda { @indirection.send(@method, "me") }.should raise_error(ArgumentError) end it "should choose the terminus returned by the :terminus_class method if no :select_terminus method is available" do @indirection.expects(:terminus_class).returns :test_terminus @terminus.expects(@method) @indirection.send(@method, "me") end it "should let the appropriate terminus perform the lookup" do @terminus.expects(@method).with { |r| r.is_a?(Puppet::Indirector::Request) } @indirection.send(@method, "me") end end shared_examples_for "Delegation Authorizer" do before do # So the :respond_to? turns out correctly. class << @terminus def authorized? end end end it "should not check authorization if a node name is not provided" do @terminus.expects(:authorized?).never @terminus.stubs(@method) # The quotes are necessary here, else it looks like a block. @request.stubs(:options).returns({}) @indirection.send(@method, "/my/key") end it "should pass the request to the terminus's authorization method" do @terminus.expects(:authorized?).with { |r| r.is_a?(Puppet::Indirector::Request) }.returns(true) @terminus.stubs(@method) @indirection.send(@method, "/my/key", :node => "mynode") end it "should fail if authorization returns false" do @terminus.expects(:authorized?).returns(false) @terminus.stubs(@method) proc { @indirection.send(@method, "/my/key", :node => "mynode") }.should raise_error(ArgumentError) end it "should continue if authorization returns true" do @terminus.expects(:authorized?).returns(true) @terminus.stubs(@method) @indirection.send(@method, "/my/key", :node => "mynode") end end describe Puppet::Indirector::Indirection do after do Puppet::Util::Cacher.expire end describe "when initializing" do # (LAK) I've no idea how to test this, really. it "should store a reference to itself before it consumes its options" do proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection) Puppet::Indirector::Indirection.instance(:testingness).delete end it "should keep a reference to the indirecting model" do model = mock 'model' @indirection = Puppet::Indirector::Indirection.new(model, :myind) @indirection.model.should equal(model) end it "should set the name" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) @indirection.name.should == :myind end it "should require indirections to have unique names" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError) end it "should extend itself with any specified module" do mod = Module.new @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod) @indirection.singleton_class.included_modules.should include(mod) end after do @indirection.delete if defined?(@indirection) end end describe "when an instance" do before :each do @terminus_class = mock 'terminus_class' @terminus = mock 'terminus' @terminus_class.stubs(:new).returns(@terminus) @cache = stub 'cache', :name => "mycache" @cache_class = mock 'cache_class' Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @indirection.terminus_class = :test_terminus @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" @name = :mything #@request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} @request = mock 'instance' end it "should allow setting the ttl" do @indirection.ttl = 300 @indirection.ttl.should == 300 end it "should default to the :runinterval setting, converted to an integer, for its ttl" do Puppet.settings.expects(:value).returns "1800" @indirection.ttl.should == 1800 end it "should calculate the current expiration by adding the TTL to the current time" do @indirection.stubs(:ttl).returns(100) now = Time.now Time.stubs(:now).returns now @indirection.expiration.should == (Time.now + 100) end it "should have a method for creating an indirection request instance" do @indirection.should respond_to(:request) end describe "creates a request" do it "should create it with its name as the request's indirection name" do Puppet::Indirector::Request.expects(:new).with { |name, *other| @indirection.name == name } @indirection.request(:funtest, "yayness") end it "should require a method and key" do Puppet::Indirector::Request.expects(:new).with { |name, method, key, *other| method == :funtest and key == "yayness" } @indirection.request(:funtest, "yayness") end it "should support optional arguments" do Puppet::Indirector::Request.expects(:new).with { |name, method, key, other| other == {:one => :two} } @indirection.request(:funtest, "yayness", :one => :two) end it "should not pass options if none are supplied" do Puppet::Indirector::Request.expects(:new).with { |*args| args.length < 4 } @indirection.request(:funtest, "yayness") end it "should return the request" do request = mock 'request' Puppet::Indirector::Request.expects(:new).returns request @indirection.request(:funtest, "yayness").should equal(request) end end describe "and looking for a model instance" do before { @method = :find } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should return the results of the delegation" do @terminus.expects(:find).returns(@instance) @indirection.find("me").should equal(@instance) end it "should set the expiration date on any instances without one set" do @terminus.stubs(:find).returns(@instance) @indirection.expects(:expiration).returns :yay @instance.expects(:expiration).returns(nil) @instance.expects(:expiration=).with(:yay) @indirection.find("/my/key") end it "should not override an already-set expiration date on returned instances" do @terminus.stubs(:find).returns(@instance) @indirection.expects(:expiration).never @instance.expects(:expiration).returns(:yay) @instance.expects(:expiration=).never @indirection.find("/my/key") end it "should filter the result instance if the terminus supports it" do @terminus.stubs(:find).returns(@instance) @terminus.stubs(:respond_to?).with(:filter).returns(true) @terminus.expects(:filter).with(@instance) @indirection.find("/my/key") end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.stubs(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should first look in the cache for an instance" do @terminus.stubs(:find).never @cache.expects(:find).returns @instance @indirection.find("/my/key") end it "should not look in the cache if the request specifies not to use the cache" do @terminus.expects(:find).returns @instance @cache.expects(:find).never @cache.stubs(:save) @indirection.find("/my/key", :ignore_cache => true) end it "should still save to the cache even if the cache is being ignored during readin" do @terminus.expects(:find).returns @instance @cache.expects(:save) @indirection.find("/my/key", :ignore_cache => true) end it "should only look in the cache if the request specifies not to use the terminus" do @terminus.expects(:find).never @cache.expects(:find) @indirection.find("/my/key", :ignore_terminus => true) end it "should use a request to look in the cache for cached objects" do @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns @instance @cache.stubs(:save) @indirection.find("/my/key") end it "should return the cached object if it is not expired" do @instance.stubs(:expired?).returns false @cache.stubs(:find).returns @instance @indirection.find("/my/key").should equal(@instance) end it "should not fail if the cache fails" do @terminus.stubs(:find).returns @instance @cache.expects(:find).raises ArgumentError @cache.stubs(:save) lambda { @indirection.find("/my/key") }.should_not raise_error end it "should look in the main terminus if the cache fails" do @terminus.expects(:find).returns @instance @cache.expects(:find).raises ArgumentError @cache.stubs(:save) @indirection.find("/my/key").should equal(@instance) end it "should send a debug log if it is using the cached object" do Puppet.expects(:debug) @cache.stubs(:find).returns @instance @indirection.find("/my/key") end it "should not return the cached object if it is expired" do @instance.stubs(:expired?).returns true @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil @indirection.find("/my/key").should be_nil end it "should send an info log if it is using the cached object" do Puppet.expects(:info) @instance.stubs(:expired?).returns true @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil @indirection.find("/my/key") end it "should cache any objects not retrieved from the cache" do @cache.expects(:find).returns nil @terminus.expects(:find).returns(@instance) @cache.expects(:save) @indirection.find("/my/key") end it "should use a request to look in the cache for cached objects" do @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns nil @terminus.stubs(:find).returns(@instance) @cache.stubs(:save) @indirection.find("/my/key") end it "should cache the instance using a request with the instance set to the cached object" do @cache.stubs(:find).returns nil @terminus.stubs(:find).returns(@instance) @cache.expects(:save).with { |r| r.method == :save and r.instance == @instance } @indirection.find("/my/key") end it "should send an info log that the object is being cached" do @cache.stubs(:find).returns nil @terminus.stubs(:find).returns(@instance) @cache.stubs(:save) Puppet.expects(:info) @indirection.find("/my/key") end end end describe "and doing a head operation" do before { @method = :head } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should return true if the head method returned true" do @terminus.expects(:head).returns(true) @indirection.head("me").should == true end it "should return false if the head method returned false" do @terminus.expects(:head).returns(false) @indirection.head("me").should == false end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.stubs(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should first look in the cache for an instance" do @terminus.stubs(:find).never @terminus.stubs(:head).never @cache.expects(:find).returns @instance @indirection.head("/my/key").should == true end it "should not save to the cache" do @cache.expects(:find).returns nil @cache.expects(:save).never @terminus.expects(:head).returns true @indirection.head("/my/key").should == true end it "should not fail if the cache fails" do @terminus.stubs(:head).returns true @cache.expects(:find).raises ArgumentError lambda { @indirection.head("/my/key") }.should_not raise_error end it "should look in the main terminus if the cache fails" do @terminus.expects(:head).returns true @cache.expects(:find).raises ArgumentError @indirection.head("/my/key").should == true end it "should send a debug log if it is using the cached object" do Puppet.expects(:debug) @cache.stubs(:find).returns @instance @indirection.head("/my/key") end it "should not accept the cached object if it is expired" do @instance.stubs(:expired?).returns true @cache.stubs(:find).returns @instance @terminus.stubs(:head).returns false @indirection.head("/my/key").should == false end end end describe "and storing a model instance" do before { @method = :save } it "should return the result of the save" do @terminus.stubs(:save).returns "foo" @indirection.save(@instance).should == "foo" end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.stubs(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should return the result of saving to the terminus" do request = stub 'request', :instance => @instance, :node => nil @indirection.expects(:request).returns request @cache.stubs(:save) @terminus.stubs(:save).returns @instance @indirection.save(@instance).should equal(@instance) end it "should use a request to save the object to the cache" do request = stub 'request', :instance => @instance, :node => nil @indirection.expects(:request).returns request @cache.expects(:save).with(request) @terminus.stubs(:save) @indirection.save(@instance) end it "should not save to the cache if the normal save fails" do request = stub 'request', :instance => @instance, :node => nil @indirection.expects(:request).returns request @cache.expects(:save).never @terminus.expects(:save).raises "eh" lambda { @indirection.save(@instance) }.should raise_error end end end describe "and removing a model instance" do before { @method = :destroy } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should return the result of removing the instance" do @terminus.stubs(:destroy).returns "yayness" @indirection.destroy("/my/key").should == "yayness" end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.expects(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should use a request instance to search in and remove objects from the cache" do destroy = stub 'destroy_request', :key => "/my/key", :node => nil find = stub 'destroy_request', :key => "/my/key", :node => nil @indirection.expects(:request).with(:destroy, "/my/key").returns destroy @indirection.expects(:request).with(:find, "/my/key").returns find cached = mock 'cache' @cache.expects(:find).with(find).returns cached @cache.expects(:destroy).with(destroy) @terminus.stubs(:destroy) @indirection.destroy("/my/key") end end end describe "and searching for multiple model instances" do before { @method = :search } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should set the expiration date on any instances without one set" do @terminus.stubs(:search).returns([@instance]) @indirection.expects(:expiration).returns :yay @instance.expects(:expiration).returns(nil) @instance.expects(:expiration=).with(:yay) @indirection.search("/my/key") end it "should not override an already-set expiration date on returned instances" do @terminus.stubs(:search).returns([@instance]) @indirection.expects(:expiration).never @instance.expects(:expiration).returns(:yay) @instance.expects(:expiration=).never @indirection.search("/my/key") end it "should return the results of searching in the terminus" do @terminus.expects(:search).returns([@instance]) @indirection.search("/my/key").should == [@instance] end end describe "and expiring a model instance" do describe "when caching is not enabled" do it "should do nothing" do @cache_class.expects(:new).never @indirection.expire("/my/key") end end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.expects(:new).returns(@cache) @instance.stubs(:expired?).returns false @cached = stub 'cached', :expiration= => nil, :name => "/my/key" end it "should use a request to find within the cache" do @cache.expects(:find).with { |r| r.is_a?(Puppet::Indirector::Request) and r.method == :find } @indirection.expire("/my/key") end it "should do nothing if no such instance is cached" do @cache.expects(:find).returns nil @indirection.expire("/my/key") end it "should log when expiring a found instance" do @cache.expects(:find).returns @cached @cache.stubs(:save) Puppet.expects(:info) @indirection.expire("/my/key") end it "should set the cached instance's expiration to a time in the past" do @cache.expects(:find).returns @cached @cache.stubs(:save) @cached.expects(:expiration=).with { |t| t < Time.now } @indirection.expire("/my/key") end it "should save the now expired instance back into the cache" do @cache.expects(:find).returns @cached @cached.expects(:expiration=).with { |t| t < Time.now } @cache.expects(:save) @indirection.expire("/my/key") end it "should use a request to save the expired resource to the cache" do @cache.expects(:find).returns @cached @cached.expects(:expiration=).with { |t| t < Time.now } @cache.expects(:save).with { |r| r.is_a?(Puppet::Indirector::Request) and r.instance == @cached and r.method == :save }.returns(@cached) @indirection.expire("/my/key") end end end after :each do @indirection.delete Puppet::Util::Cacher.expire end end describe "when managing indirection instances" do it "should allow an indirection to be retrieved by name" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) end it "should return nil when the named indirection has not been created" do Puppet::Indirector::Indirection.instance(:test).should be_nil end it "should allow an indirection's model to be retrieved by name" do mock_model = mock('model') @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) Puppet::Indirector::Indirection.model(:test).should equal(mock_model) end it "should return nil when no model matches the requested name" do Puppet::Indirector::Indirection.model(:test).should be_nil end after do @indirection.delete if defined?(@indirection) end end describe "when routing to the correct the terminus class" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = stub 'terminus class', :new => @terminus Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) end it "should fail if no terminus class can be picked" do proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) end it "should choose the default terminus class if one is specified" do @indirection.terminus_class = :default @indirection.terminus_class.should equal(:default) end it "should use the provided Puppet setting if told to do so" do Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2")) Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus") @indirection.terminus_setting = :my_setting @indirection.terminus_class.should equal(:my_terminus) end it "should fail if the provided terminus class is not valid" do Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil) proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError) end after do @indirection.delete if defined?(@indirection) end end describe "when specifying the terminus class to use" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = stub 'terminus class', :new => @terminus end it "should allow specification of a terminus type" do @indirection.should respond_to(:terminus_class=) end it "should fail to redirect if no terminus type has been specified" do proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) end it "should fail when the terminus class name is an empty string" do proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) end it "should fail when the terminus class name is nil" do proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) end it "should fail when the specified terminus class cannot be found" do Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError) end it "should select the specified terminus class if a terminus class name is provided" do Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class) @indirection.terminus(:foo).should equal(@terminus) end it "should use the configured terminus class if no terminus name is specified" do Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) @indirection.terminus_class = :foo @indirection.terminus.should equal(@terminus) end after do @indirection.delete if defined?(@indirection) end end describe "when managing terminus instances" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = mock 'terminus class' Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) end it "should create an instance of the chosen terminus class" do @terminus_class.stubs(:new).returns(@terminus) @indirection.terminus(:foo).should equal(@terminus) end # Make sure it caches the terminus. it "should return the same terminus instance each time for a given name" do @terminus_class.stubs(:new).returns(@terminus) @indirection.terminus(:foo).should equal(@terminus) @indirection.terminus(:foo).should equal(@terminus) end it "should not create a terminus instance until one is actually needed" do Puppet::Indirector.expects(:terminus).never indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest) end after do @indirection.delete end end describe "when deciding whether to cache" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = mock 'terminus class' @terminus_class.stubs(:new).returns(@terminus) Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) @indirection.terminus_class = :foo end it "should provide a method for setting the cache terminus class" do @indirection.should respond_to(:cache_class=) end it "should fail to cache if no cache type has been specified" do proc { @indirection.cache }.should raise_error(Puppet::DevError) end it "should fail to set the cache class when the cache class name is an empty string" do proc { @indirection.cache_class = "" }.should raise_error(ArgumentError) end it "should allow resetting the cache_class to nil" do @indirection.cache_class = nil @indirection.cache_class.should be_nil end it "should fail to set the cache class when the specified cache class cannot be found" do Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) end after do @indirection.delete end end describe "when using a cache" do before :each do Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") @terminus_class = mock 'terminus_class' @terminus = mock 'terminus' @terminus_class.stubs(:new).returns(@terminus) @cache = mock 'cache' @cache_class = mock 'cache_class' Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @indirection.terminus_class = :test_terminus end describe "and managing the cache terminus" do it "should not create a cache terminus at initialization" do # This is weird, because all of the code is in the setup. If we got # new called on the cache class, we'd get an exception here. end it "should reuse the cache terminus" do @cache_class.expects(:new).returns(@cache) Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") @indirection.cache_class = :cache_terminus @indirection.cache.should equal(@cache) @indirection.cache.should equal(@cache) end end describe "and saving" do end describe "and finding" do end after :each do @indirection.delete end end end diff --git a/spec/unit/indirector/inventory/yaml_spec.rb b/spec/unit/indirector/inventory/yaml_spec.rb old mode 100644 new mode 100755 index a4eb2ab7a..54ab9c72c --- a/spec/unit/indirector/inventory/yaml_spec.rb +++ b/spec/unit/indirector/inventory/yaml_spec.rb @@ -1,221 +1,220 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node/inventory' require 'puppet/indirector/inventory/yaml' require 'puppet/indirector/request' describe Puppet::Node::Inventory::Yaml do def assert_search_matches(matching, nonmatching, query) request = Puppet::Indirector::Request.new(:inventory, :search, nil, query) Dir.stubs(:glob).returns(matching.keys + nonmatching.keys) [matching, nonmatching].each do |examples| examples.each do |key, value| YAML.stubs(:load_file).with(key).returns value end end Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name} end it "should return node names that match the search query options" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '4'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "i386", 'processor_count' => '4', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), }, {'facts.architecture' => 'i386', 'facts.processor_count' => '4'} ) end it "should return empty array when no nodes match the search query options" do assert_search_matches({}, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '10'), "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), }, {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'} ) end it "should return node names that match the search query options with the greater than operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '10', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '3'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.gt' => '4'} ) end it "should return node names that match the search query options with the less than operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '30', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '50' ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '100'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.lt' => '50'} ) end it "should return node names that match the search query options with the less than or equal to operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '100' ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5000'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.le' => '50'} ) end it "should return node names that match the search query options with the greater than or equal to operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '100'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '40'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.ge' => '50'} ) end it "should return node names that match the search query options with the not equal operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => 'arm' ), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => 'powerpc', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "i386" ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.architecture.ne' => 'i386'} ) end def apply_timestamp(facts, timestamp) facts.timestamp = timestamp facts end it "should be able to query based on meta.timestamp.gt" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), }, { '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.gt' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.le" do assert_search_matches({ '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), }, {'meta.timestamp.le' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.lt" do assert_search_matches({ '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.lt' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.ge" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp.ge' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.eq" do assert_search_matches({ '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp.eq' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp" do assert_search_matches({ '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.ne" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, { '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.ne' => '2010-10-15'} ) end end diff --git a/spec/unit/indirector/key/ca_spec.rb b/spec/unit/indirector/key/ca_spec.rb index 53bd380a6..ba3d1aae2 100755 --- a/spec/unit/indirector/key/ca_spec.rb +++ b/spec/unit/indirector/key/ca_spec.rb @@ -1,28 +1,28 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/key/ca' describe Puppet::SSL::Key::Ca do it "should have documentation" do Puppet::SSL::Key::Ca.doc.should be_instance_of(String) end it "should use the :privatekeydir as the collection directory" do Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" Puppet::SSL::Key::Ca.collection_directory.should == "/key/dir" end it "should store the ca key at the :cakey location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" file = Puppet::SSL::Key::Ca.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/key" end end diff --git a/spec/unit/indirector/key/file_spec.rb b/spec/unit/indirector/key/file_spec.rb index 715ceacda..bf9b293d8 100755 --- a/spec/unit/indirector/key/file_spec.rb +++ b/spec/unit/indirector/key/file_spec.rb @@ -1,104 +1,104 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/key/file' describe Puppet::SSL::Key::File do it "should have documentation" do Puppet::SSL::Key::File.doc.should be_instance_of(String) end it "should use the :privatekeydir as the collection directory" do Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" Puppet::SSL::Key::File.collection_directory.should == "/key/dir" end it "should store the ca key at the :cakey location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" file = Puppet::SSL::Key::File.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/key" end describe "when choosing the path for the public key" do it "should use the :capub setting location if the key is for the certificate authority" do Puppet.settings.stubs(:value).returns "/fake/dir" Puppet.settings.stubs(:value).with(:capub).returns "/ca/pubkey" Puppet.settings.stubs(:use) @searcher = Puppet::SSL::Key::File.new @searcher.stubs(:ca?).returns true @searcher.public_key_path("whatever").should == "/ca/pubkey" end it "should use the host name plus '.pem' in :publickeydir for normal hosts" do Puppet.settings.stubs(:value).with(:privatekeydir).returns "/private/key/dir" Puppet.settings.stubs(:value).with(:publickeydir).returns "/public/key/dir" Puppet.settings.stubs(:use) @searcher = Puppet::SSL::Key::File.new @searcher.stubs(:ca?).returns false @searcher.public_key_path("whatever").should == "/public/key/dir/whatever.pem" end end describe "when managing private keys" do before do @searcher = Puppet::SSL::Key::File.new @private_key_path = File.join("/fake/key/path") @public_key_path = File.join("/other/fake/key/path") @searcher.stubs(:public_key_path).returns @public_key_path @searcher.stubs(:path).returns @private_key_path FileTest.stubs(:directory?).returns true FileTest.stubs(:writable?).returns true @public_key = stub 'public_key' @real_key = stub 'sslkey', :public_key => @public_key @key = stub 'key', :name => "myname", :content => @real_key @request = stub 'request', :key => "myname", :instance => @key end it "should save the public key when saving the private key" do Puppet.settings.stubs(:writesub) fh = mock 'filehandle' Puppet.settings.expects(:writesub).with(:publickeydir, @public_key_path).yields fh @public_key.expects(:to_pem).returns "my pem" fh.expects(:print).with "my pem" @searcher.save(@request) end it "should destroy the public key when destroying the private key" do File.stubs(:unlink).with(@private_key_path) FileTest.stubs(:exist?).with(@private_key_path).returns true FileTest.expects(:exist?).with(@public_key_path).returns true File.expects(:unlink).with(@public_key_path) @searcher.destroy(@request) end it "should not fail if the public key does not exist when deleting the private key" do File.stubs(:unlink).with(@private_key_path) FileTest.stubs(:exist?).with(@private_key_path).returns true FileTest.expects(:exist?).with(@public_key_path).returns false File.expects(:unlink).with(@public_key_path).never @searcher.destroy(@request) end end end diff --git a/spec/unit/indirector/ldap_spec.rb b/spec/unit/indirector/ldap_spec.rb index ab5dab9ce..2b40325de 100755 --- a/spec/unit/indirector/ldap_spec.rb +++ b/spec/unit/indirector/ldap_spec.rb @@ -1,138 +1,137 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/ldap' describe Puppet::Indirector::Ldap do before do @indirection = stub 'indirection', :name => :testing Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @ldap_class = class Testing::MyLdap < Puppet::Indirector::Ldap self end @connection = mock 'ldap' @searcher = @ldap_class.new end describe "when searching ldap" do before do # Stub everything, and we can selectively replace with an expect as # we need to for testing. @searcher.stubs(:connection).returns(@connection) @searcher.stubs(:search_filter).returns(:filter) @searcher.stubs(:search_base).returns(:base) @searcher.stubs(:process) @request = stub 'request', :key => "yay" end it "should call the ldapsearch method with the search filter" do @searcher.expects(:search_filter).with("yay").returns("yay's filter") @searcher.expects(:ldapsearch).with("yay's filter") @searcher.find @request end it "should fail if no block is passed to the ldapsearch method" do proc { @searcher.ldapsearch("blah") }.should raise_error(ArgumentError) end it "should use the results of the ldapbase method as the ldap search base" do @searcher.stubs(:search_base).returns("mybase") @connection.expects(:search).with do |*args| args[0].should == "mybase" true end @searcher.find @request end it "should default to the value of the :search_base setting as the result of the ldapbase method" do Puppet.expects(:[]).with(:ldapbase).returns("myldapbase") searcher = @ldap_class.new searcher.search_base.should == "myldapbase" end it "should use the results of the :search_attributes method as the list of attributes to return" do @searcher.stubs(:search_attributes).returns(:myattrs) @connection.expects(:search).with do |*args| args[3].should == :myattrs true end @searcher.find @request end it "should use depth 2 when searching" do @connection.expects(:search).with do |*args| args[1].should == 2 true end @searcher.find @request end it "should call process() on the first found entry" do @connection.expects(:search).yields("myresult") @searcher.expects(:process).with("myresult") @searcher.find @request end it "should reconnect and retry the search if there is a failure" do run = false @connection.stubs(:search).with do |*args| if run true else run = true raise "failed" end end.yields("myresult") @searcher.expects(:process).with("myresult") @searcher.find @request end it "should not reconnect on failure more than once" do count = 0 @connection.stubs(:search).with do |*args| count += 1 raise ArgumentError, "yay" end proc { @searcher.find(@request) }.should raise_error(Puppet::Error) count.should == 2 end it "should return true if an entry is found" do @connection.expects(:search).yields("result") @searcher.ldapsearch("whatever") { |r| }.should be_true end end describe "when connecting to ldap", :if => Puppet.features.ldap? do it "should create and start a Util::Ldap::Connection instance" do conn = mock 'connection', :connection => "myconn", :start => nil Puppet::Util::Ldap::Connection.expects(:instance).returns conn @searcher.connection.should == "myconn" end it "should only create the ldap connection when asked for it the first time" do conn = mock 'connection', :connection => "myconn", :start => nil Puppet::Util::Ldap::Connection.expects(:instance).returns conn @searcher.connection end it "should cache the connection" do conn = mock 'connection', :connection => "myconn", :start => nil Puppet::Util::Ldap::Connection.expects(:instance).returns conn @searcher.connection.should equal(@searcher.connection) end end describe "when reconnecting to ldap", :if => (Puppet.features.root? and Facter.value("hostname") == "culain") do it "should reconnect to ldap when connections are lost" end end diff --git a/spec/unit/indirector/memory_spec.rb b/spec/unit/indirector/memory_spec.rb index 751adb1b6..676acfcca 100755 --- a/spec/unit/indirector/memory_spec.rb +++ b/spec/unit/indirector/memory_spec.rb @@ -1,28 +1,27 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/memory' require 'shared_behaviours/memory_terminus' describe Puppet::Indirector::Memory do it_should_behave_like "A Memory Terminus" before do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @memory_class = class Testing::MyMemory < Puppet::Indirector::Memory self end @searcher = @memory_class.new @name = "me" @instance = stub 'instance', :name => @name @request = stub 'request', :key => @name, :instance => @instance end end diff --git a/spec/unit/indirector/node/active_record_spec.rb b/spec/unit/indirector/node/active_record_spec.rb index 65b0e1fc9..6fed940b9 100755 --- a/spec/unit/indirector/node/active_record_spec.rb +++ b/spec/unit/indirector/node/active_record_spec.rb @@ -1,38 +1,37 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node' describe "Puppet::Node::ActiveRecord", :if => Puppet.features.rails? && Puppet.features.sqlite? do include PuppetSpec::Files before do require 'puppet/indirector/node/active_record' end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Node::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Node::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end it "should call fact_merge when a node is found" do db_instance = stub 'db_instance' Puppet::Node::ActiveRecord.ar_model.expects(:find_by_name).returns db_instance node = Puppet::Node.new("foo") db_instance.expects(:to_puppet).returns node Puppet[:statedir] = tmpdir('active_record_tmp') Puppet[:railslog] = '$statedir/rails.log' ar = Puppet::Node::ActiveRecord.new node.expects(:fact_merge) request = Puppet::Indirector::Request.new(:node, :find, "what.ever") ar.find(request) end end diff --git a/spec/unit/indirector/node/exec_spec.rb b/spec/unit/indirector/node/exec_spec.rb index e6d6ccb93..f14990cd5 100755 --- a/spec/unit/indirector/node/exec_spec.rb +++ b/spec/unit/indirector/node/exec_spec.rb @@ -1,70 +1,69 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/node/exec' describe Puppet::Node::Exec do before do @indirection = mock 'indirection' Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo") @searcher = Puppet::Node::Exec.new end describe "when constructing the command to run" do it "should use the external_node script as the command" do Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo") @searcher.command.should == %w{/bin/echo} end it "should throw an exception if no external node command is set" do Puppet.expects(:[]).with(:external_nodes).returns("none") proc { @searcher.find(stub('request', :key => "foo")) }.should raise_error(ArgumentError) end end describe "when handling the results of the command" do before do @name = "yay" @node = Puppet::Node.new(@name) @node.stubs(:fact_merge) Puppet::Node.expects(:new).with(@name).returns(@node) @result = {} # Use a local variable so the reference is usable in the execute definition. result = @result @searcher.meta_def(:execute) do |command| return YAML.dump(result) end @request = stub 'request', :key => @name end it "should translate the YAML into a Node instance" do # Use an empty hash @searcher.find(@request).should equal(@node) end it "should set the resulting parameters as the node parameters" do @result[:parameters] = {"a" => "b", "c" => "d"} @searcher.find(@request) @node.parameters.should == {"a" => "b", "c" => "d"} end it "should set the resulting classes as the node classes" do @result[:classes] = %w{one two} @searcher.find(@request) @node.classes.should == [ 'one', 'two' ] end it "should merge the node's facts with its parameters" do @node.expects(:fact_merge) @searcher.find(@request) end it "should set the node's environment if one is provided" do @result[:environment] = "yay" @searcher.find(@request) @node.environment.to_s.should == 'yay' end end end diff --git a/spec/unit/indirector/node/ldap_spec.rb b/spec/unit/indirector/node/ldap_spec.rb index 5d4086a1a..5e57db3c4 100755 --- a/spec/unit/indirector/node/ldap_spec.rb +++ b/spec/unit/indirector/node/ldap_spec.rb @@ -1,454 +1,453 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do describe "when searching for a single node" do before :each do @searcher = Puppet::Node::Ldap.new @name = "mynode.domain.com" @node = stub 'node', :name => @name, :name= => nil @node.stub_everything Puppet::Node.stubs(:new).returns(@node) @request = stub 'request', :key => @name end it "should convert the hostname into a search filter" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry @searcher.name2hash(@name) end it "should convert any found entry into a hash" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{@name}))").yields entry myhash = {"myhash" => true} @searcher.expects(:entry2hash).with(entry).returns myhash @searcher.name2hash(@name).should == myhash end # This heavily tests our entry2hash method, so we don't have to stub out the stupid entry information any more. describe "when an ldap entry is found" do before do @entry = stub 'entry', :dn => 'cn=mynode,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} @searcher.stubs(:ldapsearch).yields @entry end it "should convert the entry to a hash" do @searcher.entry2hash(@entry).should be_instance_of(Hash) end it "should add the entry's common name to the hash if fqdn if false" do @searcher.entry2hash(@entry,fqdn = false)[:name].should == "mynode" end it "should add the entry's fqdn name to the hash if fqdn if true" do @searcher.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" end it "should add all of the entry's classes to the hash" do @entry.stubs(:vals).with("puppetclass").returns %w{one two} @searcher.entry2hash(@entry)[:classes].should == %w{one two} end it "should deduplicate class values" do @entry.stubs(:to_hash).returns({}) @searcher.stubs(:class_attributes).returns(%w{one two}) @entry.stubs(:vals).with("one").returns(%w{a b}) @entry.stubs(:vals).with("two").returns(%w{b c}) @searcher.entry2hash(@entry)[:classes].should == %w{a b c} end it "should add the entry's environment to the hash" do @entry.stubs(:to_hash).returns("environment" => %w{production}) @searcher.entry2hash(@entry)[:environment].should == "production" end it "should add all stacked parameters as parameters in the hash" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) result = @searcher.entry2hash(@entry) result[:parameters]["one"].should == "two" result[:parameters]["three"].should == "four" end it "should not add the stacked parameter as a normal parameter" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) @entry.stubs(:to_hash).returns("puppetvar" => %w{one=two three=four}) @searcher.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil end it "should add all other attributes as parameters in the hash" do @entry.stubs(:to_hash).returns("foo" => %w{one two}) @searcher.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} end it "should return single-value parameters as strings, not arrays" do @entry.stubs(:to_hash).returns("foo" => %w{one}) @searcher.entry2hash(@entry)[:parameters]["foo"].should == "one" end it "should convert 'true' values to the boolean 'true'" do @entry.stubs(:to_hash).returns({"one" => ["true"]}) @searcher.entry2hash(@entry)[:parameters]["one"].should == true end it "should convert 'false' values to the boolean 'false'" do @entry.stubs(:to_hash).returns({"one" => ["false"]}) @searcher.entry2hash(@entry)[:parameters]["one"].should == false end it "should convert 'true' values to the boolean 'true' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["true", "other"]}) @searcher.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] end it "should convert 'false' values to the boolean 'false' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["false", "other"]}) @searcher.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] end it "should add the parent's name if present" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) @searcher.entry2hash(@entry)[:parent].should == "foo" end it "should fail if more than one parent is specified" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) @searcher.entry2hash(@entry)[:parent].should == "foo" end end it "should search first for the provided key" do @searcher.expects(:name2hash).with("mynode.domain.com").returns({}) @searcher.find(@request) end it "should search for the short version of the provided key if the key looks like a hostname and no results are found for the key itself" do @searcher.expects(:name2hash).with("mynode.domain.com").returns(nil) @searcher.expects(:name2hash).with("mynode").returns({}) @searcher.find(@request) end it "should search for default information if no information can be found for the key" do @searcher.expects(:name2hash).with("mynode.domain.com").returns(nil) @searcher.expects(:name2hash).with("mynode").returns(nil) @searcher.expects(:name2hash).with("default").returns({}) @searcher.find(@request) end it "should return nil if no results are found in ldap" do @searcher.stubs(:name2hash).returns nil @searcher.find(@request).should be_nil end it "should return a node object if results are found in ldap" do @searcher.stubs(:name2hash).returns({}) @searcher.find(@request).should equal(@node) end describe "and node information is found in LDAP" do before do @result = {} @searcher.stubs(:name2hash).returns @result end it "should create the node with the correct name, even if it was found by a different name" do @searcher.expects(:name2hash).with("mynode.domain.com").returns nil @searcher.expects(:name2hash).with("mynode").returns @result Puppet::Node.expects(:new).with("mynode.domain.com").returns @node @searcher.find(@request) end it "should add any classes from ldap" do @result[:classes] = %w{a b c d} @node.expects(:classes=).with(%w{a b c d}) @searcher.find(@request) end it "should add all entry attributes as node parameters" do @result[:parameters] = {"one" => "two", "three" => "four"} @node.expects(:parameters=).with("one" => "two", "three" => "four") @searcher.find(@request) end it "should set the node's environment to the environment of the results" do @result[:environment] = "test" @node.expects(:environment=).with("test") @searcher.find(@request) end it "should retain false parameter values" do @result[:parameters] = {} @result[:parameters]["one"] = false @node.expects(:parameters=).with("one" => false) @searcher.find(@request) end it "should merge the node's facts after the parameters from ldap are assigned" do # Make sure we've got data to start with, so the parameters are actually set. @result[:parameters] = {} @result[:parameters]["one"] = "yay" # A hackish way to enforce order. set = false @node.expects(:parameters=).with { |*args| set = true } @node.expects(:fact_merge).with { |*args| raise "Facts were merged before parameters were set" unless set; true } @searcher.find(@request) end describe "and a parent node is specified" do before do @entry = {:classes => [], :parameters => {}} @parent = {:classes => [], :parameters => {}} @parent_parent = {:classes => [], :parameters => {}} @searcher.stubs(:name2hash).with(@name).returns(@entry) @searcher.stubs(:name2hash).with('parent').returns(@parent) @searcher.stubs(:name2hash).with('parent_parent').returns(@parent_parent) @searcher.stubs(:parent_attribute).returns(:parent) end it "should search for the parent node" do @entry[:parent] = "parent" @searcher.expects(:name2hash).with(@name).returns @entry @searcher.expects(:name2hash).with('parent').returns @parent @searcher.find(@request) end it "should fail if the parent cannot be found" do @entry[:parent] = "parent" @searcher.expects(:name2hash).with('parent').returns nil proc { @searcher.find(@request) }.should raise_error(Puppet::Error) end it "should add any parent classes to the node's classes" do @entry[:parent] = "parent" @entry[:classes] = %w{a b} @parent[:classes] = %w{c d} @node.expects(:classes=).with(%w{a b c d}) @searcher.find(@request) end it "should add any parent parameters to the node's parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["three"] = "four" @node.expects(:parameters=).with("one" => "two", "three" => "four") @searcher.find(@request) end it "should prefer node parameters over parent parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["one"] = "three" @node.expects(:parameters=).with("one" => "two") @searcher.find(@request) end it "should use the parent's environment if the node has none" do @entry[:parent] = "parent" @parent[:environment] = "parent" @node.stubs(:parameters=) @node.expects(:environment=).with("parent") @searcher.find(@request) end it "should prefer the node's environment to the parent's" do @entry[:parent] = "parent" @entry[:environment] = "child" @parent[:environment] = "parent" @node.stubs(:parameters=) @node.expects(:environment=).with("child") @searcher.find(@request) end it "should recursively look up parent information" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parent] = "parent_parent" @parent[:parameters]["three"] = "four" @parent_parent[:parameters]["five"] = "six" @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") @searcher.find(@request) end it "should not allow loops in parent declarations" do @entry[:parent] = "parent" @parent[:parent] = @name proc { @searcher.find(@request) }.should raise_error(ArgumentError) end end end end describe "when searching for multiple nodes" do before :each do @searcher = Puppet::Node::Ldap.new @options = {} @request = stub 'request', :key => "foo", :options => @options Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml end it "should find all nodes if no arguments are provided" do @searcher.expects(:ldapsearch).with("(objectclass=puppetClient)") # LAK:NOTE The search method requires an essentially bogus key. It's # an API problem that I don't really know how to fix. @searcher.search @request end describe "and a class is specified" do it "should find all nodes that are members of that class" do @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") @options[:class] = "one" @searcher.search @request end end describe "multiple classes are specified" do it "should find all nodes that are members of all classes" do @searcher.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") @options[:class] = %w{one two} @searcher.search @request end end it "should process each found entry" do # .yields can't be used to yield multiple values :/ @searcher.expects(:ldapsearch).yields("one") @searcher.expects(:entry2hash).with("one",nil).returns(:name => "foo") @searcher.search @request end it "should return a node for each processed entry with the name from the entry" do @searcher.expects(:ldapsearch).yields("whatever") @searcher.expects(:entry2hash).with("whatever",nil).returns(:name => "foo") result = @searcher.search(@request) result[0].should be_instance_of(Puppet::Node) result[0].name.should == "foo" end it "should merge each node's facts" do node = mock 'node' Puppet::Node.expects(:new).with("foo").returns node node.expects(:fact_merge) @searcher.stubs(:ldapsearch).yields("one") @searcher.stubs(:entry2hash).with("one",nil).returns(:name => "foo") @searcher.search(@request) end it "should pass the request's fqdn option to entry2hash" do node = mock 'node' @options[:fqdn] = :hello Puppet::Node.stubs(:new).with("foo").returns node node.stubs(:fact_merge) @searcher.stubs(:ldapsearch).yields("one") @searcher.expects(:entry2hash).with("one",:hello).returns(:name => "foo") @searcher.search(@request) end end end describe Puppet::Node::Ldap, " when developing the search query" do before do @searcher = Puppet::Node::Ldap.new end it "should return the value of the :ldapclassattrs split on commas as the class attributes" do Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two") @searcher.class_attributes.should == %w{one two} end it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do Puppet.stubs(:[]).with(:ldapparentattr).returns("") @searcher.parent_attribute.should be_nil end it "should return the value of the :ldapparentattr as the parent attribute" do Puppet.stubs(:[]).with(:ldapparentattr).returns("pere") @searcher.parent_attribute.should == "pere" end it "should use the value of the :ldapstring as the search filter" do Puppet.stubs(:[]).with(:ldapstring).returns("mystring") @searcher.search_filter("testing").should == "mystring" end it "should replace '%s' with the node name in the search filter if it is present" do Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring") @searcher.search_filter("testing").should == "mytestingstring" end it "should not modify the global :ldapstring when replacing '%s' in the search filter" do filter = mock 'filter' filter.expects(:include?).with("%s").returns(true) filter.expects(:gsub).with("%s", "testing").returns("mynewstring") Puppet.stubs(:[]).with(:ldapstring).returns(filter) @searcher.search_filter("testing").should == "mynewstring" end end describe Puppet::Node::Ldap, " when deciding attributes to search for" do before do @searcher = Puppet::Node::Ldap.new end it "should use 'nil' if the :ldapattrs setting is 'all'" do Puppet.stubs(:[]).with(:ldapattrs).returns("all") @searcher.search_attributes.should be_nil end it "should split the value of :ldapattrs on commas and use the result as the attribute list" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") @searcher.stubs(:class_attributes).returns([]) @searcher.stubs(:parent_attribute).returns(nil) @searcher.search_attributes.should == %w{one two} end it "should add the class attributes to the search attributes if not returning all attributes" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") @searcher.stubs(:class_attributes).returns(%w{three four}) @searcher.stubs(:parent_attribute).returns(nil) # Sort them so i don't have to care about return order @searcher.search_attributes.sort.should == %w{one two three four}.sort end it "should add the parent attribute to the search attributes if not returning all attributes" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") @searcher.stubs(:class_attributes).returns([]) @searcher.stubs(:parent_attribute).returns("parent") @searcher.search_attributes.sort.should == %w{one two parent}.sort end it "should not add nil parent attributes to the search attributes" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") @searcher.stubs(:class_attributes).returns([]) @searcher.stubs(:parent_attribute).returns(nil) @searcher.search_attributes.should == %w{one two} end end diff --git a/spec/unit/indirector/node/memory_spec.rb b/spec/unit/indirector/node/memory_spec.rb index 904ee1259..8b26a47c1 100755 --- a/spec/unit/indirector/node/memory_spec.rb +++ b/spec/unit/indirector/node/memory_spec.rb @@ -1,19 +1,18 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/node/memory' require 'shared_behaviours/memory_terminus' describe Puppet::Node::Memory do before do @name = "me" @searcher = Puppet::Node::Memory.new @instance = stub 'instance', :name => @name @request = stub 'request', :key => @name, :instance => @instance end it_should_behave_like "A Memory Terminus" end diff --git a/spec/unit/indirector/node/plain_spec.rb b/spec/unit/indirector/node/plain_spec.rb index 40d5211db..c6ba84e67 100755 --- a/spec/unit/indirector/node/plain_spec.rb +++ b/spec/unit/indirector/node/plain_spec.rb @@ -1,19 +1,18 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/node/plain' describe Puppet::Node::Plain do before do @searcher = Puppet::Node::Plain.new end it "should call node_merge() on the returned node" do node = mock 'node' Puppet::Node.expects(:new).with("mynode").returns(node) node.expects(:fact_merge) request = stub 'request', :key => "mynode" @searcher.find(request) end end diff --git a/spec/unit/indirector/node/rest_spec.rb b/spec/unit/indirector/node/rest_spec.rb index cb3012efa..fa5d1baf7 100755 --- a/spec/unit/indirector/node/rest_spec.rb +++ b/spec/unit/indirector/node/rest_spec.rb @@ -1,13 +1,12 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/node/rest' describe Puppet::Node::Rest do before do @searcher = Puppet::Node::Rest.new end end diff --git a/spec/unit/indirector/node/yaml_spec.rb b/spec/unit/indirector/node/yaml_spec.rb index 649bde06d..db9ffe435 100755 --- a/spec/unit/indirector/node/yaml_spec.rb +++ b/spec/unit/indirector/node/yaml_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node' require 'puppet/indirector/node/yaml' describe Puppet::Node::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Node::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Node::Yaml.doc.should_not be_nil end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:node) Puppet::Node::Yaml.indirection.should equal(indirection) end it "should have its name set to :node" do Puppet::Node::Yaml.name.should == :yaml end end diff --git a/spec/unit/indirector/plain_spec.rb b/spec/unit/indirector/plain_spec.rb index dfaa701bd..0894f70f0 100755 --- a/spec/unit/indirector/plain_spec.rb +++ b/spec/unit/indirector/plain_spec.rb @@ -1,28 +1,27 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/plain' describe Puppet::Indirector::Plain do before do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @plain_class = class Testing::MyPlain < Puppet::Indirector::Plain self end @searcher = @plain_class.new @request = stub 'request', :key => "yay" end it "should return return an instance of the indirected model" do object = mock 'object' @model.expects(:new).with(@request.key).returns object @searcher.find(@request).should equal(object) end end diff --git a/spec/unit/indirector/queue_spec.rb b/spec/unit/indirector/queue_spec.rb index 6f5b44b4c..b84ed2aea 100755 --- a/spec/unit/indirector/queue_spec.rb +++ b/spec/unit/indirector/queue_spec.rb @@ -1,122 +1,121 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/queue' class Puppet::Indirector::Queue::TestClient end class FooExampleData attr_accessor :name def self.pson_create(pson) new(pson['data'].to_sym) end def initialize(name = nil) @name = name if name end def render(format = :pson) to_pson end def to_pson(*args) {:type => self.class.to_s, :data => name}.to_pson(*args) end end describe Puppet::Indirector::Queue, :if => Puppet.features.pson? do before :each do @model = mock 'model' @indirection = stub 'indirection', :name => :my_queue, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).with(:my_queue).returns(@indirection) module MyQueue; end @store_class = class MyQueue::MyType < Puppet::Indirector::Queue self end @store = @store_class.new @subject_class = FooExampleData @subject = @subject_class.new @subject.name = :me Puppet[:queue_type] = :test_client Puppet::Util::Queue.stubs(:queue_type_to_class).with(:test_client).returns(Puppet::Indirector::Queue::TestClient) @request = stub 'request', :key => :me, :instance => @subject end it "should require PSON" do Puppet.features.expects(:pson?).returns false lambda { @store_class.new }.should raise_error(ArgumentError) end it 'should use the correct client type and queue' do @store.queue.should == :my_queue @store.client.should be_an_instance_of(Puppet::Indirector::Queue::TestClient) end describe "when saving" do it 'should render the instance using pson' do @subject.expects(:render).with(:pson) @store.client.stubs(:send_message) @store.save(@request) end it "should send the rendered message to the appropriate queue on the client" do @subject.expects(:render).returns "mypson" @store.client.expects(:send_message).with(:my_queue, "mypson") @store.save(@request) end it "should catch any exceptions raised" do @store.client.expects(:send_message).raises ArgumentError lambda { @store.save(@request) }.should raise_error(Puppet::Error) end end describe "when subscribing to the queue" do before do @store_class.stubs(:model).returns @model end it "should use the model's Format support to intern the message from pson" do @model.expects(:convert_from).with(:pson, "mymessage") @store_class.client.expects(:subscribe).yields("mymessage") @store_class.subscribe {|o| o } end it "should yield each interned received message" do @model.stubs(:convert_from).returns "something" @subject_two = @subject_class.new @subject_two.name = :too @store_class.client.expects(:subscribe).with(:my_queue).multiple_yields(@subject, @subject_two) received = [] @store_class.subscribe do |obj| received.push(obj) end received.should == %w{something something} end it "should log but not propagate errors" do @store_class.client.expects(:subscribe).yields("foo") @store_class.expects(:intern).raises(ArgumentError) expect { @store_class.subscribe {|o| o } }.should_not raise_error @logs.length.should == 1 @logs.first.message.should =~ /Error occured with subscription to queue my_queue for indirection my_queue: ArgumentError/ @logs.first.level.should == :err end end end diff --git a/spec/unit/indirector/report/processor_spec.rb b/spec/unit/indirector/report/processor_spec.rb index adc2638fc..bafbe6ee7 100755 --- a/spec/unit/indirector/report/processor_spec.rb +++ b/spec/unit/indirector/report/processor_spec.rb @@ -1,81 +1,81 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-23. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/indirector/report/processor' describe Puppet::Transaction::Report::Processor do before do Puppet.settings.stubs(:use).returns(true) end it "should provide a method for saving reports" do Puppet::Transaction::Report::Processor.new.should respond_to(:save) end end describe Puppet::Transaction::Report::Processor, " when saving a report" do before do Puppet.settings.stubs(:use) @reporter = Puppet::Transaction::Report::Processor.new end it "should not process the report if reports are set to 'none'" do Puppet::Reports.expects(:report).never Puppet.settings.expects(:value).with(:reports).returns("none") request = stub 'request', :instance => mock("report") @reporter.save(request) end it "should process the report with each configured report type" do Puppet.settings.stubs(:value).with(:reports).returns("one,two") @reporter.send(:reports).should == %w{one two} end end describe Puppet::Transaction::Report::Processor, " when processing a report" do before do Puppet.settings.stubs(:value).with(:reports).returns("one") Puppet.settings.stubs(:use) @reporter = Puppet::Transaction::Report::Processor.new @report_type = mock 'one' @dup_report = mock 'dupe report' @dup_report.stubs(:process) @report = mock 'report' @report.expects(:dup).returns(@dup_report) @request = stub 'request', :instance => @report Puppet::Reports.expects(:report).with("one").returns(@report_type) @dup_report.expects(:extend).with(@report_type) end # LAK:NOTE This is stupid, because the code is so short it doesn't # make sense to split it out, which means I just do the same test # three times so the spec looks right. it "should process a duplicate of the report, not the original" do @reporter.save(@request) end it "should extend the report with the report type's module" do @reporter.save(@request) end it "should call the report type's :process method" do @dup_report.expects(:process) @reporter.save(@request) end it "should not raise exceptions" do Puppet.settings.stubs(:value).with(:trace).returns(false) @dup_report.expects(:process).raises(ArgumentError) proc { @reporter.save(@request) }.should_not raise_error end end diff --git a/spec/unit/indirector/report/rest_spec.rb b/spec/unit/indirector/report/rest_spec.rb index 67a98e334..806ae6efd 100755 --- a/spec/unit/indirector/report/rest_spec.rb +++ b/spec/unit/indirector/report/rest_spec.rb @@ -1,28 +1,27 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/report/rest' describe Puppet::Transaction::Report::Rest do it "should be a subclass of Puppet::Indirector::REST" do Puppet::Transaction::Report::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should use the :report_server setting in preference to :reportserver" do Puppet.settings[:reportserver] = "reportserver" Puppet.settings[:report_server] = "report_server" Puppet::Transaction::Report::Rest.server.should == "report_server" end it "should use the :report_server setting in preference to :server" do Puppet.settings[:server] = "server" Puppet.settings[:report_server] = "report_server" Puppet::Transaction::Report::Rest.server.should == "report_server" end it "should have a value for report_server and report_port" do Puppet::Transaction::Report::Rest.server.should_not be_nil Puppet::Transaction::Report::Rest.port.should_not be_nil end end diff --git a/spec/unit/indirector/report/yaml_spec.rb b/spec/unit/indirector/report/yaml_spec.rb old mode 100644 new mode 100755 index e75fafe5e..7df4bb2e9 --- a/spec/unit/indirector/report/yaml_spec.rb +++ b/spec/unit/indirector/report/yaml_spec.rb @@ -1,38 +1,37 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transaction/report' require 'puppet/indirector/report/yaml' describe Puppet::Transaction::Report::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Transaction::Report::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Transaction::Report::Yaml.doc.should_not be_nil end it "should be registered with the report indirection" do indirection = Puppet::Indirector::Indirection.instance(:report) Puppet::Transaction::Report::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do Puppet::Transaction::Report::Yaml.name.should == :yaml end it "should inconditionnally save/load from the --lastrunreport setting" do indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil Puppet::Indirector::Indirection.stubs(:instance).with(:my_yaml).returns(indirection) store_class = Class.new(Puppet::Transaction::Report::Yaml) do def self.to_s "MyYaml::MyType" end end store = store_class.new store.path(:me).should == Puppet[:lastrunreport] end end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index bef56b1e1..965d54188 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -1,304 +1,303 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/request' describe Puppet::Indirector::Request do describe "when initializing" do it "should require an indirection name, a key, and a method" do lambda { Puppet::Indirector::Request.new }.should raise_error(ArgumentError) end it "should always convert the indirection name to a symbol" do Puppet::Indirector::Request.new("ind", :method, "mykey").indirection_name.should == :ind end it "should use provided value as the key if it is a string" do Puppet::Indirector::Request.new(:ind, :method, "mykey").key.should == "mykey" end it "should use provided value as the key if it is a symbol" do Puppet::Indirector::Request.new(:ind, :method, :mykey).key.should == :mykey end it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do instance = mock 'instance', :name => "mykey" request = Puppet::Indirector::Request.new(:ind, :method, instance) request.key.should == "mykey" request.instance.should equal(instance) end it "should support options specified as a hash" do lambda { Puppet::Indirector::Request.new(:ind, :method, :key, :one => :two) }.should_not raise_error(ArgumentError) end it "should support nil options" do lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.should_not raise_error(ArgumentError) end it "should support unspecified options" do lambda { Puppet::Indirector::Request.new(:ind, :method, :key) }.should_not raise_error(ArgumentError) end it "should use an empty options hash if nil was provided" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).options.should == {} end it "should default to a nil node" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).node.should be_nil end it "should set its node attribute if provided in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, :node => "foo.com").node.should == "foo.com" end it "should default to a nil ip" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).ip.should be_nil end it "should set its ip attribute if provided in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, :ip => "192.168.0.1").ip.should == "192.168.0.1" end it "should default to being unauthenticated" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_authenticated end it "should set be marked authenticated if configured in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, :authenticated => "eh").should be_authenticated end it "should keep its options as a hash even if a node is specified" do Puppet::Indirector::Request.new(:ind, :method, :key, :node => "eh").options.should be_instance_of(Hash) end it "should keep its options as a hash even if another option is specified" do Puppet::Indirector::Request.new(:ind, :method, :key, :foo => "bar").options.should be_instance_of(Hash) end it "should treat options other than :ip, :node, and :authenticated as options rather than attributes" do Puppet::Indirector::Request.new(:ind, :method, :key, :server => "bar").options[:server].should == "bar" end it "should normalize options to use symbols as keys" do Puppet::Indirector::Request.new(:ind, :method, :key, "foo" => "bar").options[:foo].should == "bar" end describe "and the request key is a URI" do describe "and the URI is a 'file' URI" do before do @request = Puppet::Indirector::Request.new(:ind, :method, "file:///my/file with spaces") end it "should set the request key to the unescaped full file path" do @request.key.should == "/my/file with spaces" end it "should not set the protocol" do @request.protocol.should be_nil end it "should not set the port" do @request.port.should be_nil end it "should not set the server" do @request.server.should be_nil end end it "should set the protocol to the URI scheme" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").protocol.should == "http" end it "should set the server if a server is provided" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").server.should == "host" end it "should set the server and port if both are provided" do Puppet::Indirector::Request.new(:ind, :method, "http://host:543/stuff").port.should == 543 end it "should default to the masterport if the URI scheme is 'puppet'" do Puppet.settings.expects(:value).with(:masterport).returns "321" Puppet::Indirector::Request.new(:ind, :method, "puppet://host/stuff").port.should == 321 end it "should use the provided port if the URI scheme is not 'puppet'" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff").port.should == 80 end it "should set the request key to the unescaped key part path from the URI" do Puppet::Indirector::Request.new(:ind, :method, "http://host/environment/terminus/stuff with spaces").key.should == "stuff with spaces" end it "should set the :uri attribute to the full URI" do Puppet::Indirector::Request.new(:ind, :method, "http:///stuff").uri.should == "http:///stuff" end end it "should allow indication that it should not read a cached instance" do Puppet::Indirector::Request.new(:ind, :method, :key, :ignore_cache => true).should be_ignore_cache end it "should default to not ignoring the cache" do Puppet::Indirector::Request.new(:ind, :method, :key).should_not be_ignore_cache end it "should allow indication that it should not not read an instance from the terminus" do Puppet::Indirector::Request.new(:ind, :method, :key, :ignore_terminus => true).should be_ignore_terminus end it "should default to not ignoring the terminus" do Puppet::Indirector::Request.new(:ind, :method, :key).should_not be_ignore_terminus end end it "should look use the Indirection class to return the appropriate indirection" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind request = Puppet::Indirector::Request.new(:myind, :method, :key) request.indirection.should equal(ind) end it "should use its indirection to look up the appropriate model" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind request = Puppet::Indirector::Request.new(:myind, :method, :key) ind.expects(:model).returns "mymodel" request.model.should == "mymodel" end it "should fail intelligently when asked to find a model but the indirection cannot be found" do Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns nil request = Puppet::Indirector::Request.new(:myind, :method, :key) lambda { request.model }.should raise_error(ArgumentError) end it "should have a method for determining if the request is plural or singular" do Puppet::Indirector::Request.new(:myind, :method, :key).should respond_to(:plural?) end it "should be considered plural if the method is 'search'" do Puppet::Indirector::Request.new(:myind, :search, :key).should be_plural end it "should not be considered plural if the method is not 'search'" do Puppet::Indirector::Request.new(:myind, :find, :key).should_not be_plural end it "should use its uri, if it has one, as its string representation" do Puppet::Indirector::Request.new(:myind, :find, "foo://bar/baz").to_s.should == "foo://bar/baz" end it "should use its indirection name and key, if it has no uri, as its string representation" do Puppet::Indirector::Request.new(:myind, :find, "key") == "/myind/key" end it "should be able to return the URI-escaped key" do Puppet::Indirector::Request.new(:myind, :find, "my key").escaped_key.should == URI.escape("my key") end it "should have an environment accessor" do Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => "foo").should respond_to(:environment) end it "should set its environment to an environment instance when a string is specified as its environment" do Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => "foo").environment.should == Puppet::Node::Environment.new("foo") end it "should use any passed in environment instances as its environment" do env = Puppet::Node::Environment.new("foo") Puppet::Indirector::Request.new(:myind, :find, "my key", :environment => env).environment.should equal(env) end it "should use the default environment when none is provided" do Puppet::Indirector::Request.new(:myind, :find, "my key" ).environment.should equal(Puppet::Node::Environment.new) end it "should support converting its options to a hash" do Puppet::Indirector::Request.new(:myind, :find, "my key" ).should respond_to(:to_hash) end it "should include all of its attributes when its options are converted to a hash" do Puppet::Indirector::Request.new(:myind, :find, "my key", :node => 'foo').to_hash[:node].should == 'foo' end describe "when building a query string from its options" do before do @request = Puppet::Indirector::Request.new(:myind, :find, "my key") end it "should return an empty query string if there are no options" do @request.stubs(:options).returns nil @request.query_string.should == "" end it "should return an empty query string if the options are empty" do @request.stubs(:options).returns({}) @request.query_string.should == "" end it "should prefix the query string with '?'" do @request.stubs(:options).returns(:one => "two") @request.query_string.should =~ /^\?/ end it "should include all options in the query string, separated by '&'" do @request.stubs(:options).returns(:one => "two", :three => "four") @request.query_string.sub(/^\?/, '').split("&").sort.should == %w{one=two three=four}.sort end it "should ignore nil options" do @request.stubs(:options).returns(:one => "two", :three => nil) @request.query_string.should_not be_include("three") end it "should convert 'true' option values into strings" do @request.stubs(:options).returns(:one => true) @request.query_string.should == "?one=true" end it "should convert 'false' option values into strings" do @request.stubs(:options).returns(:one => false) @request.query_string.should == "?one=false" end it "should convert to a string all option values that are integers" do @request.stubs(:options).returns(:one => 50) @request.query_string.should == "?one=50" end it "should convert to a string all option values that are floating point numbers" do @request.stubs(:options).returns(:one => 1.2) @request.query_string.should == "?one=1.2" end it "should CGI-escape all option values that are strings" do escaping = CGI.escape("one two") @request.stubs(:options).returns(:one => "one two") @request.query_string.should == "?one=#{escaping}" end it "should YAML-dump and CGI-escape arrays" do escaping = CGI.escape(YAML.dump(%w{one two})) @request.stubs(:options).returns(:one => %w{one two}) @request.query_string.should == "?one=#{escaping}" end it "should convert to a string and CGI-escape all option values that are symbols" do escaping = CGI.escape("sym bol") @request.stubs(:options).returns(:one => :"sym bol") @request.query_string.should == "?one=#{escaping}" end it "should fail if options other than booleans or strings are provided" do @request.stubs(:options).returns(:one => {:one => :two}) lambda { @request.query_string }.should raise_error(ArgumentError) end end end diff --git a/spec/unit/indirector/resource/ral_spec.rb b/spec/unit/indirector/resource/ral_spec.rb old mode 100644 new mode 100755 index 43deaa8eb..61290f7a7 --- a/spec/unit/indirector/resource/ral_spec.rb +++ b/spec/unit/indirector/resource/ral_spec.rb @@ -1,129 +1,128 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "Puppet::Resource::Ral" do describe "find" do before do @request = stub 'request', :key => "user/root" end it "should find an existing instance" do my_resource = stub "my user resource" wrong_instance = stub "wrong user", :name => "bob" my_instance = stub "my user", :name => "root", :to_resource => my_resource require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ wrong_instance, my_instance, wrong_instance ]) Puppet::Resource::Ral.new.find(@request).should == my_resource end it "if there is no instance, it should create one" do wrong_instance = stub "wrong user", :name => "bob" require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ wrong_instance, wrong_instance ]) result = Puppet::Resource::Ral.new.find(@request) result.should be_is_a(Puppet::Resource) result.title.should == "root" end end describe "search" do before do @request = stub 'request', :key => "user/", :options => {} end it "should convert ral resources into regular resources" do my_resource = stub "my user resource" my_instance = stub "my user", :name => "root", :to_resource => my_resource require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should filter results by name if there's a name in the key" do my_resource = stub "my user resource" my_resource.stubs(:to_resource).returns(my_resource) my_resource.stubs(:[]).with(:name).returns("root") wrong_resource = stub "wrong resource" wrong_resource.stubs(:to_resource).returns(wrong_resource) wrong_resource.stubs(:[]).with(:name).returns("bad") my_instance = stub "my user", :to_resource => my_resource wrong_instance = stub "wrong user", :to_resource => wrong_resource @request = stub 'request', :key => "user/root", :options => {} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should filter results by query parameters" do wrong_resource = stub "my user resource" wrong_resource.stubs(:to_resource).returns(wrong_resource) wrong_resource.stubs(:[]).with(:name).returns("root") my_resource = stub "wrong resource" my_resource.stubs(:to_resource).returns(my_resource) my_resource.stubs(:[]).with(:name).returns("bob") my_instance = stub "my user", :to_resource => my_resource wrong_instance = stub "wrong user", :to_resource => wrong_resource @request = stub 'request', :key => "user/", :options => {:name => "bob"} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should return sorted results" do a_resource = stub "alice resource" a_resource.stubs(:to_resource).returns(a_resource) a_resource.stubs(:title).returns("alice") b_resource = stub "bob resource" b_resource.stubs(:to_resource).returns(b_resource) b_resource.stubs(:title).returns("bob") a_instance = stub "alice user", :to_resource => a_resource b_instance = stub "bob user", :to_resource => b_resource @request = stub 'request', :key => "user/", :options => {} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ b_instance, a_instance ]) Puppet::Resource::Ral.new.search(@request).should == [a_resource, b_resource] end end describe "save" do before do @rebuilt_res = stub 'rebuilt instance' @ral_res = stub 'ral resource', :to_resource => @rebuilt_res @instance = stub 'instance', :to_ral => @ral_res @request = stub 'request', :key => "user/", :instance => @instance @catalog = stub 'catalog' Puppet::Resource::Catalog.stubs(:new).returns(@catalog) @catalog.stubs(:apply) @catalog.stubs(:add_resource) end it "should apply a new catalog with a ral object in it" do Puppet::Resource::Catalog.expects(:new).returns(@catalog) @catalog.expects(:add_resource).with(@ral_res) @catalog.expects(:apply) Puppet::Resource::Ral.new.save(@request) end it "should return a regular resource that used to be the ral resource" do Puppet::Resource::Ral.new.save(@request).should == @rebuilt_res end end end diff --git a/spec/unit/indirector/resource/rest_spec.rb b/spec/unit/indirector/resource/rest_spec.rb index 1285d338a..70c9a7f11 100755 --- a/spec/unit/indirector/resource/rest_spec.rb +++ b/spec/unit/indirector/resource/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/resource/rest' describe Puppet::Resource::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Resource::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/resource_type/parser_spec.rb b/spec/unit/indirector/resource_type/parser_spec.rb index f86b319f9..c4fc455a0 100755 --- a/spec/unit/indirector/resource_type/parser_spec.rb +++ b/spec/unit/indirector/resource_type/parser_spec.rb @@ -1,150 +1,149 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/resource_type/parser' require 'puppet_spec/files' describe Puppet::Indirector::ResourceType::Parser do include PuppetSpec::Files before do @terminus = Puppet::Indirector::ResourceType::Parser.new @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo") @krt = @request.environment.known_resource_types end it "should be registered with the resource_type indirection" do Puppet::Indirector::Terminus.terminus_class(:resource_type, :parser).should equal(Puppet::Indirector::ResourceType::Parser) end describe "when finding" do it "should return any found type from the request's environment" do type = Puppet::Resource::Type.new(:hostclass, "foo") @request.environment.known_resource_types.add(type) @terminus.find(@request).should == type end it "should attempt to load the type if none is found in memory" do dir = tmpdir("find_a_type") FileUtils.mkdir_p(dir) Puppet[:modulepath] = dir # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo::bar") manifest_path = File.join(dir, "foo", "manifests") FileUtils.mkdir_p(manifest_path) File.open(File.join(manifest_path, "bar.pp"), "w") { |f| f.puts "class foo::bar {}" } result = @terminus.find(@request) result.should be_instance_of(Puppet::Resource::Type) result.name.should == "foo::bar" end it "should return nil if no type can be found" do @terminus.find(@request).should be_nil end it "should prefer definitions to nodes" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "foo")) @terminus.find(@request).should == type end end describe "when searching" do before do @request.key = "*" end it "should use the request's environment's list of known resource types" do @request.environment.known_resource_types.expects(:hostclasses).returns({}) @terminus.search(@request) end it "should return all results if '*' is provided as the search string" do @request.key = "*" type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) result = @terminus.search(@request) result.should be_include(type) result.should be_include(node) result.should be_include(define) end it "should treat any search string not '*' as a regex" do @request.key = "a" foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) bar = @krt.add(Puppet::Resource::Type.new(:hostclass, "bar")) baz = @krt.add(Puppet::Resource::Type.new(:hostclass, "baz")) result = @terminus.search(@request) result.should be_include(bar) result.should be_include(baz) result.should_not be_include(foo) end it "should fail if a provided search string is not '*' and is not a valid regex" do @request.key = "*foo*" # Add one instance so we don't just get an empty array" @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) lambda { @terminus.search(@request) }.should raise_error(ArgumentError) end it "should return all known types" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) result = @terminus.search(@request) result.should be_include(type) result.should be_include(node) result.should be_include(define) end it "should not return the 'main' class" do main = @krt.add(Puppet::Resource::Type.new(:hostclass, "")) # So there is a return value foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) @terminus.search(@request).should_not be_include(main) end it "should return nil if no types can be found" do @terminus.search(@request).should be_nil end it "should load all resource types from all search paths" do dir = tmpdir("searching_in_all") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) Puppet[:modulepath] = "#{first}:#{second}" # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :search, "*") onepath = File.join(first, "one", "manifests") FileUtils.mkdir_p(onepath) twopath = File.join(first, "two", "manifests") FileUtils.mkdir_p(twopath) File.open(File.join(onepath, "oneklass.pp"), "w") { |f| f.puts "class one::oneklass {}" } File.open(File.join(twopath, "twoklass.pp"), "w") { |f| f.puts "class two::twoklass {}" } result = @terminus.search(@request) result.find { |t| t.name == "one::oneklass" }.should be_instance_of(Puppet::Resource::Type) result.find { |t| t.name == "two::twoklass" }.should be_instance_of(Puppet::Resource::Type) end end end diff --git a/spec/unit/indirector/resource_type/rest_spec.rb b/spec/unit/indirector/resource_type/rest_spec.rb index dceb109b2..3d8ddf44c 100755 --- a/spec/unit/indirector/resource_type/rest_spec.rb +++ b/spec/unit/indirector/resource_type/rest_spec.rb @@ -1,15 +1,14 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/resource_type/rest' describe Puppet::Indirector::ResourceType::Rest do it "should be registered with the resource_type indirection" do Puppet::Indirector::Terminus.terminus_class(:resource_type, :rest).should equal(Puppet::Indirector::ResourceType::Rest) end it "should be a subclass of Puppet::Indirector::Rest" do Puppet::Indirector::ResourceType::Rest.superclass.should == Puppet::Indirector::REST end end diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb index 326d85f9d..513eb8352 100755 --- a/spec/unit/indirector/rest_spec.rb +++ b/spec/unit/indirector/rest_spec.rb @@ -1,499 +1,515 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/rest' shared_examples_for "a REST http call" do it "should accept a path" do lambda { @search.send(@method, *@arguments) }.should_not raise_error(ArgumentError) end it "should require a path" do lambda { @searcher.send(@method) }.should raise_error(ArgumentError) end it "should return the results of deserializing the response to the request" do conn = mock 'connection' conn.stubs(:put).returns @response conn.stubs(:delete).returns @response conn.stubs(:get).returns @response Puppet::Network::HttpPool.stubs(:http_instance).returns conn @searcher.expects(:deserialize).with(@response).returns "myobject" @searcher.send(@method, *@arguments).should == 'myobject' end end describe Puppet::Indirector::REST do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = stub('model', :supported_formats => %w{}, :convert_from => nil) @instance = stub('model instance', :name= => nil) @indirection = stub('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model) Puppet::Indirector::Indirection.expects(:instance).returns(@indirection) module This module Is module A module Test end end end end @rest_class = class This::Is::A::Test::Class < Puppet::Indirector::REST self end end before :each do @response = stub('mock response', :body => 'result', :code => "200") @response.stubs(:[]).with('content-type').returns "text/plain" @response.stubs(:[]).with('content-encoding').returns nil @searcher = @rest_class.new @searcher.stubs(:model).returns @model end it "should include the v1 REST API module" do Puppet::Indirector::REST.ancestors.should be_include(Puppet::Network::HTTP::API::V1) end it "should have a method for specifying what setting a subclass should use to retrieve its server" do @rest_class.should respond_to(:use_server_setting) end it "should use any specified setting to pick the server" do @rest_class.expects(:server_setting).returns :servset Puppet.settings.expects(:value).with(:servset).returns "myserver" @rest_class.server.should == "myserver" end it "should default to :server for the server setting" do @rest_class.expects(:server_setting).returns nil Puppet.settings.expects(:value).with(:server).returns "myserver" @rest_class.server.should == "myserver" end it "should have a method for specifying what setting a subclass should use to retrieve its port" do @rest_class.should respond_to(:use_port_setting) end it "should use any specified setting to pick the port" do @rest_class.expects(:port_setting).returns :servset Puppet.settings.expects(:value).with(:servset).returns "321" @rest_class.port.should == 321 end it "should default to :port for the port setting" do @rest_class.expects(:port_setting).returns nil Puppet.settings.expects(:value).with(:masterport).returns "543" @rest_class.port.should == 543 end describe "when deserializing responses" do it "should return nil if the response code is 404" do response = mock 'response' response.expects(:code).returns "404" @searcher.deserialize(response).should be_nil end [300,400,403,405,500,501,502,503,504].each { |rc| describe "when the response code is #{rc}" do before :each do @model.expects(:convert_from).never @response = mock 'response' @response.stubs(:code).returns rc.to_s @response.stubs(:[]).with('content-encoding').returns nil @response.stubs(:message).returns "There was a problem (header)" end it "should fail" do @response.stubs(:body).returns nil lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError) end it "should take the error message from the body, if present" do @response.stubs(:body).returns "There was a problem (body)" lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (body)") end it "should take the error message from the response header if the body is empty" do @response.stubs(:body).returns "" lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") end it "should take the error message from the response header if the body is absent" do @response.stubs(:body).returns nil lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") end describe "and with http compression" do it "should uncompress the body" do @response.stubs(:body).returns("compressed body") @searcher.expects(:uncompress_body).with(@response).returns("uncompressed") lambda { @searcher.deserialize(@response) }.should raise_error { |e| e.message =~ /uncompressed/ } end end end } it "should return the results of converting from the format specified by the content-type header if the response code is in the 200s" do @model.expects(:convert_from).with("myformat", "mydata").returns "myobject" response = mock 'response' response.stubs(:[]).with("content-type").returns "myformat" response.stubs(:[]).with("content-encoding").returns nil response.stubs(:body).returns "mydata" response.stubs(:code).returns "200" @searcher.deserialize(response).should == "myobject" end it "should convert and return multiple instances if the return code is in the 200s and 'multiple' is specified" do @model.expects(:convert_from_multiple).with("myformat", "mydata").returns "myobjects" response = mock 'response' response.stubs(:[]).with("content-type").returns "myformat" response.stubs(:[]).with("content-encoding").returns nil response.stubs(:body).returns "mydata" response.stubs(:code).returns "200" @searcher.deserialize(response, true).should == "myobjects" end it "should strip the content-type header to keep only the mime-type" do @model.expects(:convert_from).with("text/plain", "mydata").returns "myobject" response = mock 'response' response.stubs(:[]).with("content-type").returns "text/plain; charset=utf-8" response.stubs(:[]).with("content-encoding").returns nil response.stubs(:body).returns "mydata" response.stubs(:code).returns "200" @searcher.deserialize(response) end it "should uncompress the body" do @model.expects(:convert_from).with("myformat", "uncompressed mydata").returns "myobject" response = mock 'response' response.stubs(:[]).with("content-type").returns "myformat" response.stubs(:body).returns "compressed mydata" response.stubs(:code).returns "200" @searcher.expects(:uncompress_body).with(response).returns("uncompressed mydata") @searcher.deserialize(response).should == "myobject" end end describe "when creating an HTTP client" do before do Puppet.settings.stubs(:value).returns("rest_testing") end it "should use the class's server and port if the indirection request provides neither" do @request = stub 'request', :key => "foo", :server => nil, :port => nil @searcher.class.expects(:port).returns 321 @searcher.class.expects(:server).returns "myserver" Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" @searcher.network(@request).should == "myconn" end it "should use the server from the indirection request if one is present" do @request = stub 'request', :key => "foo", :server => "myserver", :port => nil @searcher.class.stubs(:port).returns 321 Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" @searcher.network(@request).should == "myconn" end it "should use the port from the indirection request if one is present" do @request = stub 'request', :key => "foo", :server => nil, :port => 321 @searcher.class.stubs(:server).returns "myserver" Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" @searcher.network(@request).should == "myconn" end end describe "when doing a find" do before :each do @connection = stub('mock http connection', :get => @response) @searcher.stubs(:network).returns(@connection) # neuter the network connection # Use a key with spaces, so we can test escaping - @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar") + @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar", :environment => "myenv") end - it "should call the GET http method on a network connection" do - @searcher.expects(:network).returns @connection - @connection.expects(:get).returns @response - @searcher.find(@request) + describe "with a large body" do + it "should use the POST http method" do + params = {} + 'aa'.upto('zz') do |s| + params[s] = 'foo' + end + + @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar", params.merge(:environment => "myenv")) + + @connection.expects(:post).with do |uri, body| + uri == "/myenv/foo/foo%20bar" and body.split("&").sort == params.map {|key,value| "#{key}=#{value}"}.sort + end.returns(@response) + + @searcher.find(@request) + end + end + + describe "with a small body" do + it "should use the GET http method" do + @searcher.expects(:network).returns @connection + @connection.expects(:get).returns @response + @searcher.find(@request) + end end it "should deserialize and return the http response, setting name" do @connection.expects(:get).returns @response instance = stub 'object' instance.expects(:name=) @searcher.expects(:deserialize).with(@response).returns instance @searcher.find(@request).should == instance end it "should deserialize and return the http response, and not require name=" do @connection.expects(:get).returns @response instance = stub 'object' @searcher.expects(:deserialize).with(@response).returns instance @searcher.find(@request).should == instance end - it "should use the URI generated by the Handler module" do - @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" - @connection.expects(:get).with { |path, args| path == "/my/uri" }.returns(@response) + @connection.expects(:get).with { |path, args| path == "/myenv/foo/foo%20bar?" }.returns(@response) @searcher.find(@request) end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.find(@request) end it "should add Accept-Encoding header" do @searcher.expects(:add_accept_encoding).returns({"accept-encoding" => "gzip"}) @connection.expects(:get).with { |path, args| args["accept-encoding"] == "gzip" }.returns(@response) @searcher.find(@request) end it "should deserialize and return the network response" do @searcher.expects(:deserialize).with(@response).returns @instance @searcher.find(@request).should equal(@instance) end it "should set the name of the resulting instance to the asked-for name" do @searcher.expects(:deserialize).with(@response).returns @instance @instance.expects(:name=).with "foo bar" @searcher.find(@request) end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.find(@request) }.should raise_error(ArgumentError) end end describe "when doing a head" do before :each do @connection = stub('mock http connection', :head => @response) @searcher.stubs(:network).returns(@connection) # Use a key with spaces, so we can test escaping @request = Puppet::Indirector::Request.new(:foo, :head, "foo bar") end it "should call the HEAD http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:head).returns @response @searcher.head(@request) end it "should return true if there was a successful http response" do @connection.expects(:head).returns @response @response.stubs(:code).returns "200" @searcher.head(@request).should == true end it "should return false if there was a successful http response" do @connection.expects(:head).returns @response @response.stubs(:code).returns "404" @searcher.head(@request).should == false end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:head).with { |path, args| path == "/my/uri" }.returns(@response) @searcher.head(@request) end end describe "when doing a search" do before :each do @connection = stub('mock http connection', :get => @response) @searcher.stubs(:network).returns(@connection) # neuter the network connection @model.stubs(:convert_from_multiple) @request = Puppet::Indirector::Request.new(:foo, :search, "foo bar") end it "should call the GET http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:get).returns @response @searcher.search(@request) end it "should deserialize as multiple instances and return the http response" do @connection.expects(:get).returns @response @searcher.expects(:deserialize).with(@response, true).returns "myobject" @searcher.search(@request).should == 'myobject' end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/mys/uri" @connection.expects(:get).with { |path, args| path == "/mys/uri" }.returns(@response) @searcher.search(@request) end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.search(@request) end it "should return an empty array if serialization returns nil" do @model.stubs(:convert_from_multiple).returns nil @searcher.search(@request).should == [] end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.search(@request) }.should raise_error(ArgumentError) end end describe "when doing a destroy" do before :each do @connection = stub('mock http connection', :delete => @response) @searcher.stubs(:network).returns(@connection) # neuter the network connection @request = Puppet::Indirector::Request.new(:foo, :destroy, "foo bar") end it "should call the DELETE http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:delete).returns @response @searcher.destroy(@request) end it "should fail if any options are provided, since DELETE apparently does not support query options" do @request.stubs(:options).returns(:one => "two", :three => "four") lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) end it "should deserialize and return the http response" do @connection.expects(:delete).returns @response @searcher.expects(:deserialize).with(@response).returns "myobject" @searcher.destroy(@request).should == 'myobject' end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:delete).with { |path, args| path == "/my/uri" }.returns(@response) @searcher.destroy(@request) end it "should not include the query string" do @connection.stubs(:delete).returns @response @searcher.destroy(@request) end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:delete).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.destroy(@request) end it "should deserialize and return the network response" do @searcher.expects(:deserialize).with(@response).returns @instance @searcher.destroy(@request).should equal(@instance) end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) end end describe "when doing a save" do before :each do @connection = stub('mock http connection', :put => @response) @searcher.stubs(:network).returns(@connection) # neuter the network connection @instance = stub 'instance', :render => "mydata", :mime => "mime" @request = Puppet::Indirector::Request.new(:foo, :save, "foo bar") @request.instance = @instance end it "should call the PUT http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:put).returns @response @searcher.save(@request) end it "should fail if any options are provided, since DELETE apparently does not support query options" do @request.stubs(:options).returns(:one => "two", :three => "four") lambda { @searcher.save(@request) }.should raise_error(ArgumentError) end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:put).with { |path, args| path == "/my/uri" }.returns(@response) @searcher.save(@request) end it "should serialize the instance using the default format and pass the result as the body of the request" do @instance.expects(:render).returns "serial_instance" @connection.expects(:put).with { |path, data, args| data == "serial_instance" }.returns @response @searcher.save(@request) end it "should deserialize and return the http response" do @connection.expects(:put).returns @response @searcher.expects(:deserialize).with(@response).returns "myobject" @searcher.save(@request).should == 'myobject' end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:put).with { |path, data, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.save(@request) end it "should provide a Content-Type header containing the mime-type of the sent object" do @connection.expects(:put).with { |path, data, args| args['Content-Type'] == "mime" }.returns(@response) @instance.expects(:mime).returns "mime" @searcher.save(@request) end it "should deserialize and return the network response" do @searcher.expects(:deserialize).with(@response).returns @instance @searcher.save(@request).should equal(@instance) end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.save(@request) }.should raise_error(ArgumentError) end end end diff --git a/spec/unit/indirector/run/local_spec.rb b/spec/unit/indirector/run/local_spec.rb old mode 100644 new mode 100755 index cb012a0b0..8fb61d962 --- a/spec/unit/indirector/run/local_spec.rb +++ b/spec/unit/indirector/run/local_spec.rb @@ -1,20 +1,19 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/run/local' describe Puppet::Run::Local do it "should be a sublcass of Puppet::Indirector::Code" do Puppet::Run::Local.superclass.should equal(Puppet::Indirector::Code) end it "should call runner.run on save and return the runner" do runner = Puppet::Run.new runner.stubs(:run).returns(runner) request = Puppet::Indirector::Request.new(:indirection, :save, "anything") request.instance = runner = Puppet::Run.new Puppet::Run::Local.new.save(request).should == runner end end diff --git a/spec/unit/indirector/run/rest_spec.rb b/spec/unit/indirector/run/rest_spec.rb index cf90caa5b..4b80962d1 100755 --- a/spec/unit/indirector/run/rest_spec.rb +++ b/spec/unit/indirector/run/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/run/rest' describe Puppet::Run::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Run::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb index ca97cf49e..5d0859598 100755 --- a/spec/unit/indirector/ssl_file_spec.rb +++ b/spec/unit/indirector/ssl_file_spec.rb @@ -1,282 +1,282 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-10. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/indirector/ssl_file' describe Puppet::Indirector::SslFile do before :all do @indirection = stub 'indirection', :name => :testing, :model => @model Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) module Testing; end @file_class = class Testing::MyType < Puppet::Indirector::SslFile self end end before :each do @model = mock 'model' @setting = :certdir @file_class.store_in @setting @path = "/tmp/my_directory" Puppet[:noop] = false Puppet[@setting] = @path Puppet[:trace] = false end it "should use :main and :ssl upon initialization" do Puppet.settings.expects(:use).with(:main, :ssl) @file_class.new end it "should return a nil collection directory if no directory setting has been provided" do @file_class.store_in nil @file_class.collection_directory.should be_nil end it "should return a nil file location if no location has been provided" do @file_class.store_at nil @file_class.file_location.should be_nil end it "should fail if no store directory or file location has been set" do @file_class.store_in nil @file_class.store_at nil lambda { @file_class.new }.should raise_error(Puppet::DevError) end describe "when managing ssl files" do before do Puppet.settings.stubs(:use) @searcher = @file_class.new @cert = stub 'certificate', :name => "myname" @certpath = File.join(@path, "myname.pem") @request = stub 'request', :key => @cert.name, :instance => @cert end it "should consider the file a ca file if the name is equal to what the SSL::Host class says is the CA name" do Puppet::SSL::Host.expects(:ca_name).returns "amaca" @searcher.should be_ca("amaca") end describe "when choosing the location for certificates" do it "should set them at the ca setting's path if a ca setting is available and the name resolves to the CA name" do @file_class.store_in nil @file_class.store_at :mysetting @file_class.store_ca_at :casetting Puppet.settings.stubs(:value).with(:casetting).returns "/ca/file" @searcher.expects(:ca?).with(@cert.name).returns true @searcher.path(@cert.name).should == "/ca/file" end it "should set them at the file location if a file setting is available" do @file_class.store_in nil @file_class.store_at :mysetting Puppet.settings.stubs(:value).with(:mysetting).returns "/some/file" @searcher.path(@cert.name).should == "/some/file" end it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do @searcher.path(@cert.name).should == @certpath end end describe "when finding certificates on disk" do describe "and no certificate is present" do before do # Stub things so the case management bits work. FileTest.stubs(:exist?).with(File.dirname(@certpath)).returns false FileTest.expects(:exist?).with(@certpath).returns false end it "should return nil" do @searcher.find(@request).should be_nil end end describe "and a certificate is present" do before do FileTest.expects(:exist?).with(@certpath).returns true end it "should return an instance of the model, which it should use to read the certificate" do cert = mock 'cert' model = mock 'model' @file_class.stubs(:model).returns model model.expects(:new).with("myname").returns cert cert.expects(:read).with(@certpath) @searcher.find(@request).should equal(cert) end end describe "and a certificate is present but has uppercase letters" do before do @request = stub 'request', :key => "myhost" end # This is kind of more an integration test; it's for #1382, until # the support for upper-case certs can be removed around mid-2009. it "should rename the existing file to the lower-case path" do @path = @searcher.path("myhost") FileTest.expects(:exist?).with(@path).returns(false) dir, file = File.split(@path) FileTest.expects(:exist?).with(dir).returns true Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase] File.expects(:rename).with(File.join(dir, file.upcase), @path) cert = mock 'cert' model = mock 'model' @searcher.stubs(:model).returns model @searcher.model.expects(:new).with("myhost").returns cert cert.expects(:read).with(@path) @searcher.find(@request) end end end describe "when saving certificates to disk" do before do FileTest.stubs(:directory?).returns true FileTest.stubs(:writable?).returns true end it "should fail if the directory is absent" do FileTest.expects(:directory?).with(File.dirname(@certpath)).returns false lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should fail if the directory is not writeable" do FileTest.stubs(:directory?).returns true FileTest.expects(:writable?).with(File.dirname(@certpath)).returns false lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should save to the path the output of converting the certificate to a string" do fh = mock 'filehandle' fh.expects(:print).with("mycert") @searcher.stubs(:write).yields fh @cert.expects(:to_s).returns "mycert" @searcher.save(@request) end describe "and a directory setting is set" do it "should use the Settings class to write the file" do @searcher.class.store_in @setting fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:writesub).with(@setting, @certpath).yields fh @searcher.save(@request) end end describe "and a file location is set" do it "should use the filehandle provided by the Settings" do @searcher.class.store_at @setting fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:write).with(@setting).yields fh @searcher.save(@request) end end describe "and the name is the CA name and a ca setting is set" do it "should use the filehandle provided by the Settings" do @searcher.class.store_at @setting @searcher.class.store_ca_at :castuff Puppet.settings.stubs(:value).with(:castuff).returns "castuff stub" fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:write).with(:castuff).yields fh @searcher.stubs(:ca?).returns true @searcher.save(@request) end end end describe "when destroying certificates" do describe "that do not exist" do before do FileTest.expects(:exist?).with(@certpath).returns false end it "should return false" do @searcher.destroy(@request).should be_false end end describe "that exist" do before do FileTest.expects(:exist?).with(@certpath).returns true end it "should unlink the certificate file" do File.expects(:unlink).with(@certpath) @searcher.destroy(@request) end it "should log that is removing the file" do File.stubs(:exist?).returns true File.stubs(:unlink) Puppet.expects(:notice) @searcher.destroy(@request) end end end describe "when searching for certificates" do before do @model = mock 'model' @file_class.stubs(:model).returns @model end it "should return a certificate instance for all files that exist" do Dir.expects(:entries).with(@path).returns %w{one.pem two.pem} one = stub 'one', :read => nil two = stub 'two', :read => nil @model.expects(:new).with("one").returns one @model.expects(:new).with("two").returns two @searcher.search(@request).should == [one, two] end it "should read each certificate in using the model's :read method" do Dir.expects(:entries).with(@path).returns %w{one.pem} one = stub 'one' one.expects(:read).with(File.join(@path, "one.pem")) @model.expects(:new).with("one").returns one @searcher.search(@request) end it "should skip any files that do not match /\.pem$/" do Dir.expects(:entries).with(@path).returns %w{. .. one.pem} one = stub 'one', :read => nil @model.expects(:new).with("one").returns one @searcher.search(@request) end end end end diff --git a/spec/unit/indirector/status/rest_spec.rb b/spec/unit/indirector/status/rest_spec.rb index 436c86881..b203e6e20 100755 --- a/spec/unit/indirector/status/rest_spec.rb +++ b/spec/unit/indirector/status/rest_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/status/rest' describe Puppet::Indirector::Status::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Indirector::Status::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/terminus_spec.rb b/spec/unit/indirector/terminus_spec.rb index bc255aa5b..41770e1e3 100755 --- a/spec/unit/indirector/terminus_spec.rb +++ b/spec/unit/indirector/terminus_spec.rb @@ -1,245 +1,244 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/defaults' require 'puppet/indirector' require 'puppet/indirector/file' describe Puppet::Indirector::Terminus do before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @indirection = stub 'indirection', :name => :my_stuff, :register_terminus_type => nil Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Testing::Abstract" end end @terminus_class = Class.new(@abstract_terminus) do def self.to_s "MyStuff::TermType" end end @terminus = @terminus_class.new end describe Puppet::Indirector::Terminus do it "should provide a method for setting terminus class documentation" do @terminus_class.should respond_to(:desc) end it "should support a class-level name attribute" do @terminus_class.should respond_to(:name) end it "should support a class-level indirection attribute" do @terminus_class.should respond_to(:indirection) end it "should support a class-level terminus-type attribute" do @terminus_class.should respond_to(:terminus_type) end it "should support a class-level model attribute" do @terminus_class.should respond_to(:model) end it "should accept indirection instances as its indirection" do indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil proc { @terminus_class.indirection = indirection }.should_not raise_error @terminus_class.indirection.should equal(indirection) end it "should look up indirection instances when only a name has been provided" do indirection = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) @terminus_class.indirection = :myind @terminus_class.indirection.should equal(indirection) end it "should fail when provided a name that does not resolve to an indirection" do Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) # It shouldn't overwrite our existing one (or, more normally, it shouldn't set # anything). @terminus_class.indirection.should equal(@indirection) end end describe Puppet::Indirector::Terminus, " when creating terminus classes" do it "should associate the subclass with an indirection based on the subclass constant" do @terminus.indirection.should equal(@indirection) end it "should set the subclass's type to the abstract terminus name" do @terminus.terminus_type.should == :abstract end it "should set the subclass's name to the indirection name" do @terminus.name.should == :term_type end it "should set the subclass's model to the indirection model" do @indirection.expects(:model).returns :yay @terminus.model.should == :yay end end describe Puppet::Indirector::Terminus, " when a terminus instance" do it "should return the class's name as its name" do @terminus.name.should == :term_type end it "should return the class's indirection as its indirection" do @terminus.indirection.should equal(@indirection) end it "should set the instances's type to the abstract terminus type's name" do @terminus.terminus_type.should == :abstract end it "should set the instances's model to the indirection's model" do @indirection.expects(:model).returns :yay @terminus.model.should == :yay end end end # LAK: This could reasonably be in the Indirection instances, too. It doesn't make # a whole heckuva lot of difference, except that with the instance loading in # the Terminus base class, we have to have a check to see if we're already # instance-loading a given terminus class type. describe Puppet::Indirector::Terminus, " when managing terminus classes" do it "should provide a method for registering terminus classes" do Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) end it "should provide a method for returning terminus classes by name and type" do terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever Puppet::Indirector::Terminus.register_terminus_class(terminus) Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) end it "should set up autoloading for any terminus class types requested" do Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) end it "should load terminus classes that are not found" do # Set up instance loading; it would normally happen automatically Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) Puppet::Indirector::Terminus.terminus_class(:test1, :yay) end it "should fail when no indirection can be found" do Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(nil) @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Abstract" end end proc { @terminus = Class.new(@abstract_terminus) do def self.to_s "MyIndirection::TestType" end end }.should raise_error(ArgumentError) end it "should register the terminus class with the terminus base class" do Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| type.indirection_name == :my_indirection and type.name == :test_terminus end @indirection = stub 'indirection', :name => :my_indirection, :register_terminus_type => nil Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(@indirection) @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Abstract" end end @terminus = Class.new(@abstract_terminus) do def self.to_s "MyIndirection::TestTerminus" end end end end describe Puppet::Indirector::Terminus, " when parsing class constants for indirection and terminus names" do before do @subclass = mock 'subclass' @subclass.stubs(:to_s).returns("TestInd::OneTwo") @subclass.stubs(:mark_as_abstract_terminus) Puppet::Indirector::Terminus.stubs(:register_terminus_class) end it "should fail when anonymous classes are used" do proc { Puppet::Indirector::Terminus.inherited(Class.new) }.should raise_error(Puppet::DevError) end it "should use the last term in the constant for the terminus class name" do @subclass.expects(:name=).with(:one_two) @subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(@subclass) end it "should convert the terminus name to a downcased symbol" do @subclass.expects(:name=).with(:one_two) @subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(@subclass) end it "should use the second to last term in the constant for the indirection name" do @subclass.expects(:indirection=).with(:test_ind) @subclass.stubs(:name=) @subclass.stubs(:terminus_type=) Puppet::Indirector::File.inherited(@subclass) end it "should convert the indirection name to a downcased symbol" do @subclass.expects(:indirection=).with(:test_ind) @subclass.stubs(:name=) @subclass.stubs(:terminus_type=) Puppet::Indirector::File.inherited(@subclass) end it "should convert camel case to lower case with underscores as word separators" do @subclass.expects(:name=).with(:one_two) @subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(@subclass) end end describe Puppet::Indirector::Terminus, " when creating terminus class types" do before do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @subclass = Class.new(Puppet::Indirector::Terminus) do def self.to_s "Puppet::Indirector::Terminus::MyTermType" end end end it "should set the name of the abstract subclass to be its class constant" do @subclass.name.should equal(:my_term_type) end it "should mark abstract terminus types as such" do @subclass.should be_abstract_terminus end it "should not allow instances of abstract subclasses to be created" do proc { @subclass.new }.should raise_error(Puppet::DevError) end end diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb index 188e300d6..c43dbcaf6 100755 --- a/spec/unit/indirector/yaml_spec.rb +++ b/spec/unit/indirector/yaml_spec.rb @@ -1,159 +1,158 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/indirector/yaml' describe Puppet::Indirector::Yaml, " when choosing file location" do before :all do @indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil Puppet::Indirector::Indirection.expects(:instance).with(:my_yaml).returns(@indirection) module MyYaml; end @store_class = class MyYaml::MyType < Puppet::Indirector::Yaml self end end before :each do @store = @store_class.new @subject = Object.new @subject.singleton_class.send(:attr_accessor, :name) @subject.name = :me @dir = "/what/ever" Puppet.settings.stubs(:value).returns("fakesettingdata") Puppet.settings.stubs(:value).with(:clientyamldir).returns(@dir) Puppet.run_mode.stubs(:master?).returns false @request = stub 'request', :key => :me, :instance => @subject end describe Puppet::Indirector::Yaml, " when choosing file location" do it "should use the server_datadir if the run_mode is master" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" @store.path(:me).should =~ %r{^/server/yaml/dir} end it "should use the client yamldir if the run_mode is not master" do Puppet.run_mode.expects(:master?).returns false Puppet.settings.expects(:value).with(:clientyamldir).returns "/client/yaml/dir" @store.path(:me).should =~ %r{^/client/yaml/dir} end it "should use the extension if one is specified" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" @store.path(:me,'.farfignewton').should =~ %r{\.farfignewton$} end it "should assume an extension of .yaml if none is specified" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" @store.path(:me).should =~ %r{\.yaml$} end it "should store all files in a single file root set in the Puppet defaults" do @store.path(:me).should =~ %r{^#{@dir}} end it "should use the terminus name for choosing the subdirectory" do @store.path(:me).should =~ %r{^#{@dir}/my_yaml} end it "should use the object's name to determine the file name" do @store.path(:me).should =~ %r{me.yaml$} end end describe Puppet::Indirector::Yaml, " when storing objects as YAML" do it "should only store objects that respond to :name" do @request.stubs(:instance).returns Object.new proc { @store.save(@request) }.should raise_error(ArgumentError) end it "should convert Ruby objects to YAML and write them to disk using a write lock" do yaml = @subject.to_yaml file = mock 'file' path = @store.send(:path, @subject.name) FileTest.expects(:exist?).with(File.dirname(path)).returns(true) @store.expects(:writelock).with(path, 0660).yields(file) file.expects(:print).with(yaml) @store.save(@request) end it "should create the indirection subdirectory if it does not exist" do yaml = @subject.to_yaml file = mock 'file' path = @store.send(:path, @subject.name) dir = File.dirname(path) FileTest.expects(:exist?).with(dir).returns(false) Dir.expects(:mkdir).with(dir) @store.expects(:writelock).yields(file) file.expects(:print).with(yaml) @store.save(@request) end end describe Puppet::Indirector::Yaml, " when retrieving YAML" do it "should read YAML in from disk using a read lock and convert it to Ruby objects" do path = @store.send(:path, @subject.name) yaml = @subject.to_yaml FileTest.expects(:exist?).with(path).returns(true) fh = mock 'filehandle' @store.expects(:readlock).with(path).yields fh fh.expects(:read).returns yaml @store.find(@request).instance_variable_get("@name").should == :me end it "should fail coherently when the stored YAML is invalid" do path = @store.send(:path, @subject.name) FileTest.expects(:exist?).with(path).returns(true) # Something that will fail in yaml yaml = "--- !ruby/object:Hash" fh = mock 'filehandle' @store.expects(:readlock).yields fh fh.expects(:read).returns yaml proc { @store.find(@request) }.should raise_error(Puppet::Error) end end describe Puppet::Indirector::Yaml, " when searching" do it "should return an array of fact instances with one instance for each file when globbing *" do @request = stub 'request', :key => "*", :instance => @subject @one = mock 'one' @two = mock 'two' @store.expects(:path).with(@request.key,'').returns :glob Dir.expects(:glob).with(:glob).returns(%w{one.yaml two.yaml}) YAML.expects(:load_file).with("one.yaml").returns @one; YAML.expects(:load_file).with("two.yaml").returns @two; @store.search(@request).should == [@one, @two] end it "should return an array containing a single instance of fact when globbing 'one*'" do @request = stub 'request', :key => "one*", :instance => @subject @one = mock 'one' @store.expects(:path).with(@request.key,'').returns :glob Dir.expects(:glob).with(:glob).returns(%w{one.yaml}) YAML.expects(:load_file).with("one.yaml").returns @one; @store.search(@request).should == [@one] end it "should return an empty array when the glob doesn't match anything" do @request = stub 'request', :key => "f*ilglobcanfail*", :instance => @subject @store.expects(:path).with(@request.key,'').returns :glob Dir.expects(:glob).with(:glob).returns [] @store.search(@request).should == [] end end end diff --git a/spec/unit/indirector_spec.rb b/spec/unit/indirector_spec.rb index 7bab426c0..0c09831db 100755 --- a/spec/unit/indirector_spec.rb +++ b/spec/unit/indirector_spec.rb @@ -1,86 +1,144 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/defaults' require 'puppet/indirector' +describe Puppet::Indirector, "when configuring routes" do + before :each do + Puppet::Node.indirection.reset_terminus_class + Puppet::Node.indirection.cache_class = nil + end + + after :each do + Puppet::Node.indirection.reset_terminus_class + Puppet::Node.indirection.cache_class = nil + end + + it "should configure routes as requested" do + routes = { + "node" => { + "terminus" => "exec", + "cache" => "plain" + } + } + + Puppet::Indirector.configure_routes(routes) + + Puppet::Node.indirection.terminus_class.should == "exec" + Puppet::Node.indirection.cache_class.should == "plain" + end + + it "should fail when given an invalid indirection" do + routes = { + "fake_indirection" => { + "terminus" => "exec", + "cache" => "plain" + } + } + + expect { Puppet::Indirector.configure_routes(routes) }.should raise_error(/fake_indirection does not exist/) + end + + it "should fail when given an invalid terminus" do + routes = { + "node" => { + "terminus" => "fake_terminus", + "cache" => "plain" + } + } + + expect { Puppet::Indirector.configure_routes(routes) }.should raise_error(/Could not find terminus fake_terminus/) + end + + it "should fail when given an invalid cache" do + routes = { + "node" => { + "terminus" => "exec", + "cache" => "fake_cache" + } + } + + expect { Puppet::Indirector.configure_routes(routes) }.should raise_error(/Could not find terminus fake_cache/) + end +end + describe Puppet::Indirector, " when available to a model" do before do @thingie = Class.new do extend Puppet::Indirector end end it "should provide a way for the model to register an indirection under a name" do @thingie.should respond_to(:indirects) end end describe Puppet::Indirector, "when registering an indirection" do before do @thingie = Class.new do extend Puppet::Indirector attr_reader :name def initialize(name) @name = name end end end it "should require a name when registering a model" do Proc.new {@thingie.send(:indirects) }.should raise_error(ArgumentError) end it "should create an indirection instance to manage each indirecting model" do @indirection = @thingie.indirects(:test) @indirection.should be_instance_of(Puppet::Indirector::Indirection) end it "should not allow a model to register under multiple names" do # Keep track of the indirection instance so we can delete it on cleanup @indirection = @thingie.indirects :first Proc.new { @thingie.indirects :second }.should raise_error(ArgumentError) end it "should make the indirection available via an accessor" do @indirection = @thingie.indirects :first @thingie.indirection.should equal(@indirection) end it "should pass any provided options to the indirection during initialization" do klass = mock 'terminus class' Puppet::Indirector::Indirection.expects(:new).with(@thingie, :first, {:some => :options}) @indirection = @thingie.indirects :first, :some => :options end it "should extend the class with the Format Handler" do @indirection = @thingie.indirects :first @thingie.singleton_class.ancestors.should be_include(Puppet::Network::FormatHandler) end after do @indirection.delete if @indirection end end describe Puppet::Indirector, "when redirecting a model" do before do @thingie = Class.new do extend Puppet::Indirector attr_reader :name def initialize(name) @name = name end end @indirection = @thingie.send(:indirects, :test) end it "should include the Envelope module in the model" do @thingie.ancestors.should be_include(Puppet::Indirector::Envelope) end after do @indirection.delete end end diff --git a/spec/unit/interface/action_builder_spec.rb b/spec/unit/interface/action_builder_spec.rb new file mode 100755 index 000000000..5b04df900 --- /dev/null +++ b/spec/unit/interface/action_builder_spec.rb @@ -0,0 +1,69 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/interface/action_builder' + +describe Puppet::Interface::ActionBuilder do + describe "::build" do + it "should build an action" do + action = Puppet::Interface::ActionBuilder.build(nil, :foo) do + end + action.should be_a(Puppet::Interface::Action) + action.name.should == :foo + end + + it "should define a method on the face which invokes the action" do + face = Puppet::Interface.new(:action_builder_test_interface, '0.0.1') + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + when_invoked do + "invoked the method" + end + end + + face.foo.should == "invoked the method" + end + + it "should require a block" do + expect { Puppet::Interface::ActionBuilder.build(nil, :foo) }. + should raise_error("Action :foo must specify a block") + end + + describe "when handling options" do + let :face do Puppet::Interface.new(:option_handling, '0.0.1') end + + it "should have a #option DSL function" do + method = nil + Puppet::Interface::ActionBuilder.build(face, :foo) do + method = self.method(:option) + end + method.should be + end + + it "should define an option without a block" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + option "--bar" + end + action.should be_option :bar + end + + it "should accept an empty block" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + option "--bar" do + # This space left deliberately blank. + end + end + action.should be_option :bar + end + end + + context "inline documentation" do + let :face do Puppet::Interface.new(:inline_action_docs, '0.0.1') end + + it "should set the summary" do + action = Puppet::Interface::ActionBuilder.build(face, :foo) do + summary "this is some text" + end + action.summary.should == "this is some text" + end + end + end +end diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb new file mode 100755 index 000000000..c4b21eaac --- /dev/null +++ b/spec/unit/interface/action_manager_spec.rb @@ -0,0 +1,232 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +# This is entirely an internal class for Interface, so we have to load it instead of our class. +require 'puppet/interface' + +class ActionManagerTester + include Puppet::Interface::ActionManager +end + +describe Puppet::Interface::ActionManager do + subject { ActionManagerTester.new } + + describe "when included in a class" do + it "should be able to define an action" do + subject.action(:foo) do + when_invoked { "something "} + end + end + + it "should be able to define a 'script' style action" do + subject.script :bar do + "a bar is where beer is found" + end + end + + it "should be able to list defined actions" do + subject.action(:foo) do + when_invoked { "something" } + end + subject.action(:bar) do + when_invoked { "something" } + end + + subject.actions.should =~ [:foo, :bar] + end + + it "should list 'script' actions" do + subject.script :foo do "foo" end + subject.actions.should =~ [:foo] + end + + it "should list both script and normal actions" do + subject.action :foo do + when_invoked do "foo" end + end + subject.script :bar do "a bar is where beer is found" end + + subject.actions.should =~ [:foo, :bar] + end + + it "should be able to indicate when an action is defined" do + subject.action(:foo) do + when_invoked { "something" } + end + + subject.should be_action(:foo) + end + + it "should indicate an action is defined for script actions" do + subject.script :foo do "foo" end + subject.should be_action :foo + end + + it "should correctly treat action names specified as strings" do + subject.action(:foo) do + when_invoked { "something" } + end + + subject.should be_action("foo") + end + end + + describe "when used to extend a class" do + subject { Class.new.extend(Puppet::Interface::ActionManager) } + + it "should be able to define an action" do + subject.action(:foo) do + when_invoked { "something "} + end + end + + it "should be able to list defined actions" do + subject.action(:foo) do + when_invoked { "something" } + end + subject.action(:bar) do + when_invoked { "something" } + end + + subject.actions.should include(:bar) + subject.actions.should include(:foo) + end + + it "should be able to indicate when an action is defined" do + subject.action(:foo) { "something" } + subject.should be_action(:foo) + end + end + + describe "when used both at the class and instance level" do + before do + @klass = Class.new do + include Puppet::Interface::ActionManager + extend Puppet::Interface::ActionManager + end + @instance = @klass.new + end + + it "should be able to define an action at the class level" do + @klass.action(:foo) do + when_invoked { "something "} + end + end + + it "should create an instance method when an action is defined at the class level" do + @klass.action(:foo) do + when_invoked { "something" } + end + @instance.foo.should == "something" + end + + it "should be able to define an action at the instance level" do + @instance.action(:foo) do + when_invoked { "something "} + end + end + + it "should create an instance method when an action is defined at the instance level" do + @instance.action(:foo) do + when_invoked { "something" } + end + @instance.foo.should == "something" + end + + it "should be able to list actions defined at the class level" do + @klass.action(:foo) do + when_invoked { "something" } + end + @klass.action(:bar) do + when_invoked { "something" } + end + + @klass.actions.should include(:bar) + @klass.actions.should include(:foo) + end + + it "should be able to list actions defined at the instance level" do + @instance.action(:foo) do + when_invoked { "something" } + end + @instance.action(:bar) do + when_invoked { "something" } + end + + @instance.actions.should include(:bar) + @instance.actions.should include(:foo) + end + + it "should be able to list actions defined at both instance and class level" do + @klass.action(:foo) do + when_invoked { "something" } + end + @instance.action(:bar) do + when_invoked { "something" } + end + + @instance.actions.should include(:bar) + @instance.actions.should include(:foo) + end + + it "should be able to indicate when an action is defined at the class level" do + @klass.action(:foo) do + when_invoked { "something" } + end + @instance.should be_action(:foo) + end + + it "should be able to indicate when an action is defined at the instance level" do + @klass.action(:foo) do + when_invoked { "something" } + end + @instance.should be_action(:foo) + end + + it "should list actions defined in superclasses" do + @subclass = Class.new(@klass) + @instance = @subclass.new + + @klass.action(:parent) do + when_invoked { "a" } + end + @subclass.action(:sub) do + when_invoked { "a" } + end + @instance.action(:instance) do + when_invoked { "a" } + end + + @instance.should be_action(:parent) + @instance.should be_action(:sub) + @instance.should be_action(:instance) + end + + it "should create an instance method when an action is defined in a superclass" do + @subclass = Class.new(@klass) + @instance = @subclass.new + + @klass.action(:foo) do + when_invoked { "something" } + end + @instance.foo.should == "something" + end + end + + describe "#get_action" do + let :parent_class do + parent_class = Class.new(Puppet::Interface) + parent_class.action(:foo) {} + parent_class + end + + it "should check that we can find inherited actions when we are a class" do + Class.new(parent_class).get_action(:foo).name.should == :foo + end + + it "should check that we can find inherited actions when we are an instance" do + instance = parent_class.new(:foo, '0.0.0') + instance.get_action(:foo).name.should == :foo + end + end +end diff --git a/spec/unit/interface/action_spec.rb b/spec/unit/interface/action_spec.rb new file mode 100755 index 000000000..8c6782976 --- /dev/null +++ b/spec/unit/interface/action_spec.rb @@ -0,0 +1,172 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/interface/action' + +describe Puppet::Interface::Action do + describe "when validating the action name" do + [nil, '', 'foo bar', '-foobar'].each do |input| + it "should treat #{input.inspect} as an invalid name" do + expect { Puppet::Interface::Action.new(nil, input) }. + should raise_error(/is an invalid action name/) + end + end + end + + describe "when invoking" do + it "should be able to call other actions on the same object" do + face = Puppet::Interface.new(:my_face, '0.0.1') do + action(:foo) do + when_invoked { 25 } + end + + action(:bar) do + when_invoked { "the value of foo is '#{foo}'" } + end + end + face.foo.should == 25 + face.bar.should == "the value of foo is '25'" + end + + # bar is a class action calling a class action + # quux is a class action calling an instance action + # baz is an instance action calling a class action + # qux is an instance action calling an instance action + it "should be able to call other actions on the same object when defined on a class" do + class Puppet::Interface::MyInterfaceBaseClass < Puppet::Interface + action(:foo) do + when_invoked { 25 } + end + + action(:bar) do + when_invoked { "the value of foo is '#{foo}'" } + end + + action(:quux) do + when_invoked { "qux told me #{qux}" } + end + end + + face = Puppet::Interface::MyInterfaceBaseClass.new(:my_inherited_face, '0.0.1') do + action(:baz) do + when_invoked { "the value of foo in baz is '#{foo}'" } + end + + action(:qux) do + when_invoked { baz } + end + end + face.foo.should == 25 + face.bar.should == "the value of foo is '25'" + face.quux.should == "qux told me the value of foo in baz is '25'" + face.baz.should == "the value of foo in baz is '25'" + face.qux.should == "the value of foo in baz is '25'" + end + + context "when calling the Ruby API" do + let :face do + Puppet::Interface.new(:ruby_api, '1.0.0') do + action :bar do + when_invoked do |options| + options + end + end + end + end + + it "should work when no options are supplied" do + options = face.bar + options.should == {} + end + + it "should work when options are supplied" do + options = face.bar :bar => "beer" + options.should == { :bar => "beer" } + end + end + end + + describe "with action-level options" do + it "should support options with an empty block" do + face = Puppet::Interface.new(:action_level_options, '0.0.1') do + action :foo do + option "--bar" do + # this line left deliberately blank + end + end + end + + face.should_not be_option :bar + face.get_action(:foo).should be_option :bar + end + + it "should return only action level options when there are no face options" do + face = Puppet::Interface.new(:action_level_options, '0.0.1') do + action :foo do option "--bar" end + end + + face.get_action(:foo).options.should =~ [:bar] + end + + describe "with both face and action options" do + let :face do + Puppet::Interface.new(:action_level_options, '0.0.1') do + action :foo do option "--bar" end + action :baz do option "--bim" end + option "--quux" + end + end + + it "should return combined face and action options" do + face.get_action(:foo).options.should =~ [:bar, :quux] + end + + it "should fetch options that the face inherited" do + parent = Class.new(Puppet::Interface) + parent.option "--foo" + child = parent.new(:inherited_options, '0.0.1') do + option "--bar" + action :action do option "--baz" end + end + + action = child.get_action(:action) + action.should be + + [:baz, :bar, :foo].each do |name| + action.get_option(name).should be_an_instance_of Puppet::Interface::Option + end + end + + it "should get an action option when asked" do + face.get_action(:foo).get_option(:bar). + should be_an_instance_of Puppet::Interface::Option + end + + it "should get a face option when asked" do + face.get_action(:foo).get_option(:quux). + should be_an_instance_of Puppet::Interface::Option + end + + it "should return options only for this action" do + face.get_action(:baz).options.should =~ [:bim, :quux] + end + end + + it_should_behave_like "things that declare options" do + def add_options_to(&block) + face = Puppet::Interface.new(:with_options, '0.0.1') do + action(:foo, &block) + end + face.get_action(:foo) + end + end + + it "should fail when a face option duplicates an action option" do + expect { + Puppet::Interface.new(:action_level_options, '0.0.1') do + option "--foo" + action :bar do option "--foo" end + end + }.should raise_error ArgumentError, /Option foo conflicts with existing option foo/i + end + end +end diff --git a/spec/unit/interface/face_collection_spec.rb b/spec/unit/interface/face_collection_spec.rb new file mode 100755 index 000000000..d1114dde7 --- /dev/null +++ b/spec/unit/interface/face_collection_spec.rb @@ -0,0 +1,170 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'tmpdir' +require 'puppet/interface/face_collection' + +describe Puppet::Interface::FaceCollection do + # To avoid cross-pollution we have to save and restore both the hash + # containing all the interface data, and the array used by require. Restoring + # both means that we don't leak side-effects across the code. --daniel 2011-04-06 + # + # Worse luck, we *also* need to flush $" of anything defining a face, + # because otherwise we can cross-pollute from other test files and end up + # with no faces loaded, but the require value set true. --daniel 2011-04-10 + before :each do + @original_faces = subject.instance_variable_get("@faces").dup + @original_required = $".dup + $".delete_if do |path| path =~ %r{/face/.*\.rb$} end + subject.instance_variable_get("@faces").clear + end + + after :each do + subject.instance_variable_set("@faces", @original_faces) + $".clear ; @original_required.each do |item| $" << item end + end + + describe "::faces" do + it "REVISIT: should have some tests here, if we describe it" + end + + describe "::validate_version" do + it 'should permit three number versions' do + subject.validate_version('10.10.10').should == true + end + + it 'should permit versions with appended descriptions' do + subject.validate_version('10.10.10beta').should == true + end + + it 'should not permit versions with more than three numbers' do + subject.validate_version('1.2.3.4').should == false + end + + it 'should not permit versions with only two numbers' do + subject.validate_version('10.10').should == false + end + + it 'should not permit versions with only one number' do + subject.validate_version('123').should == false + end + + it 'should not permit versions with text in any position but at the end' do + subject.validate_version('v1.1.1').should == false + end + end + + describe "::[]" do + before :each do + subject.instance_variable_get("@faces")[:foo]['0.0.1'] = 10 + end + + it "should return the face with the given name" do + subject["foo", '0.0.1'].should == 10 + end + + it "should attempt to load the face if it isn't found" do + subject.expects(:require).with('puppet/face/bar') + subject["bar", '0.0.1'] + end + + it "should attempt to load the default face for the specified version :current" do + subject.expects(:require).with('puppet/face/fozzie') + subject['fozzie', :current] + end + end + + describe "::face?" do + it "should return true if the face specified is registered" do + subject.instance_variable_get("@faces")[:foo]['0.0.1'] = 10 + subject.face?("foo", '0.0.1').should == true + end + + it "should attempt to require the face if it is not registered" do + subject.expects(:require).with do |file| + subject.instance_variable_get("@faces")[:bar]['0.0.1'] = true + file == 'puppet/face/bar' + end + subject.face?("bar", '0.0.1').should == true + end + + it "should return true if requiring the face registered it" do + subject.stubs(:require).with do + subject.instance_variable_get("@faces")[:bar]['0.0.1'] = 20 + end + end + + it "should return false if the face is not registered" do + subject.stubs(:require).returns(true) + subject.face?("bar", '0.0.1').should be_false + end + + it "should return false if the face file itself is missing" do + subject.stubs(:require). + raises(LoadError, 'no such file to load -- puppet/face/bar') + subject.face?("bar", '0.0.1').should be_false + end + + it "should register the version loaded by `:current` as `:current`" do + subject.expects(:require).with do |file| + subject.instance_variable_get("@faces")[:huzzah]['2.0.1'] = :huzzah_face + file == 'puppet/face/huzzah' + end + subject.face?("huzzah", :current) + subject.instance_variable_get("@faces")[:huzzah][:current].should == :huzzah_face + end + + context "with something on disk" do + it "should register the version loaded from `puppet/face/{name}` as `:current`" do + subject.should be_face "huzzah", '2.0.1' + subject.should be_face "huzzah", :current + Puppet::Face[:huzzah, '2.0.1'].should == Puppet::Face[:huzzah, :current] + end + + it "should index :current when the code was pre-required" do + subject.instance_variable_get("@faces")[:huzzah].should_not be_key :current + require 'puppet/face/huzzah' + subject.face?(:huzzah, :current).should be_true + end + end + + it "should not cause an invalid face to be enumerated later" do + subject.face?(:there_is_no_face, :current).should be_false + subject.faces.should_not include :there_is_no_face + end + end + + describe "::register" do + it "should store the face by name" do + face = Puppet::Face.new(:my_face, '0.0.1') + subject.register(face) + subject.instance_variable_get("@faces").should == {:my_face => {'0.0.1' => face}} + end + end + + describe "::underscorize" do + faulty = [1, "#foo", "$bar", "sturm und drang", :"sturm und drang"] + valid = { + "Foo" => :foo, + :Foo => :foo, + "foo_bar" => :foo_bar, + :foo_bar => :foo_bar, + "foo-bar" => :foo_bar, + :"foo-bar" => :foo_bar, + } + + valid.each do |input, expect| + it "should map #{input.inspect} to #{expect.inspect}" do + result = subject.underscorize(input) + result.should == expect + end + end + + faulty.each do |input| + it "should fail when presented with #{input.inspect} (#{input.class})" do + expect { subject.underscorize(input) }. + should raise_error ArgumentError, /not a valid face name/ + end + end + end +end diff --git a/spec/unit/interface/option_builder_spec.rb b/spec/unit/interface/option_builder_spec.rb new file mode 100755 index 000000000..fae48324e --- /dev/null +++ b/spec/unit/interface/option_builder_spec.rb @@ -0,0 +1,29 @@ +require 'puppet/interface/option_builder' + +describe Puppet::Interface::OptionBuilder do + let :face do Puppet::Interface.new(:option_builder_testing, '0.0.1') end + + it "should be able to construct an option without a block" do + Puppet::Interface::OptionBuilder.build(face, "--foo"). + should be_an_instance_of Puppet::Interface::Option + end + + describe "when using the DSL block" do + it "should work with an empty block" do + option = Puppet::Interface::OptionBuilder.build(face, "--foo") do + # This block deliberately left blank. + end + + option.should be_an_instance_of Puppet::Interface::Option + end + + it "should support documentation declarations" do + text = "this is the description" + option = Puppet::Interface::OptionBuilder.build(face, "--foo") do + desc text + end + option.should be_an_instance_of Puppet::Interface::Option + option.desc.should == text + end + end +end diff --git a/spec/unit/interface/option_spec.rb b/spec/unit/interface/option_spec.rb new file mode 100755 index 000000000..3bcd121e2 --- /dev/null +++ b/spec/unit/interface/option_spec.rb @@ -0,0 +1,75 @@ +require 'puppet/interface/option' + +describe Puppet::Interface::Option do + let :face do Puppet::Interface.new(:option_testing, '0.0.1') end + + describe "#optparse_to_name" do + ["", "=BAR", " BAR", "=bar", " bar"].each do |postfix| + { "--foo" => :foo, "-f" => :f }.each do |base, expect| + input = base + postfix + it "should map #{input.inspect} to #{expect.inspect}" do + option = Puppet::Interface::Option.new(face, input) + option.name.should == expect + end + end + end + + [:foo, 12, nil, {}, []].each do |input| + it "should fail sensible when given #{input.inspect}" do + expect { Puppet::Interface::Option.new(face, input) }. + should raise_error ArgumentError, /is not valid for an option argument/ + end + end + + ["-foo", "-foo=BAR", "-foo BAR"].each do |input| + it "should fail with a single dash for long option #{input.inspect}" do + expect { Puppet::Interface::Option.new(face, input) }. + should raise_error ArgumentError, /long options need two dashes \(--\)/ + end + end + end + + it "requires a face when created" do + expect { Puppet::Interface::Option.new }. + should raise_error ArgumentError, /wrong number of arguments/ + end + + it "also requires some declaration arguments when created" do + expect { Puppet::Interface::Option.new(face) }. + should raise_error ArgumentError, /No option declarations found/ + end + + it "should infer the name from an optparse string" do + option = Puppet::Interface::Option.new(face, "--foo") + option.name.should == :foo + end + + it "should infer the name when multiple optparse string are given" do + option = Puppet::Interface::Option.new(face, "--foo", "-f") + option.name.should == :foo + end + + it "should prefer the first long option name over a short option name" do + option = Puppet::Interface::Option.new(face, "-f", "--foo") + option.name.should == :foo + end + + it "should create an instance when given a face and name" do + Puppet::Interface::Option.new(face, "--foo"). + should be_instance_of Puppet::Interface::Option + end + + describe "#to_s" do + it "should transform a symbol into a string" do + option = Puppet::Interface::Option.new(face, "--foo") + option.name.should == :foo + option.to_s.should == "foo" + end + + it "should use - rather than _ to separate words in strings but not symbols" do + option = Puppet::Interface::Option.new(face, "--foo-bar") + option.name.should == :foo_bar + option.to_s.should == "foo-bar" + end + end +end diff --git a/spec/unit/interface_spec.rb b/spec/unit/interface_spec.rb new file mode 100755 index 000000000..e52b45d8a --- /dev/null +++ b/spec/unit/interface_spec.rb @@ -0,0 +1,207 @@ +require 'spec_helper' +require 'puppet/face' +require 'puppet/interface' + +describe Puppet::Interface do + subject { Puppet::Interface } + + before :all do + @faces = Puppet::Interface::FaceCollection.instance_variable_get("@faces").dup + end + + before :each do + Puppet::Interface::FaceCollection.instance_variable_get("@faces").clear + end + + after :all do + Puppet::Interface::FaceCollection.instance_variable_set("@faces", @faces) + end + + describe "#[]" do + it "should fail when no version is requested" do + expect { subject[:huzzah] }.should raise_error ArgumentError + end + + it "should raise an exception when the requested version is unavailable" do + expect { subject[:huzzah, '17.0.0'] }.should raise_error, Puppet::Error + end + + it "should raise an exception when the requested face doesn't exist" do + expect { subject[:burrble_toot, :current] }.should raise_error, Puppet::Error + end + end + + describe "#define" do + it "should register the face" do + face = subject.define(:face_test_register, '0.0.1') + face.should == subject[:face_test_register, '0.0.1'] + end + + it "should load actions" do + subject.any_instance.expects(:load_actions) + subject.define(:face_test_load_actions, '0.0.1') + end + + it "should require a version number" do + expect { subject.define(:no_version) }.to raise_error ArgumentError + end + + it "should support summary builder and accessor methods" do + subject.new(:foo, '1.0.0').should respond_to(:summary).with(0).arguments + subject.new(:foo, '1.0.0').should respond_to(:summary=).with(1).arguments + end + + it "should set the summary text" do + text = "hello, freddy, my little pal" + subject.define(:face_test_summary, '1.0.0') do + summary text + end + subject[:face_test_summary, '1.0.0'].summary.should == text + end + + it "should support mutating the summary" do + text = "hello, freddy, my little pal" + subject.define(:face_test_summary, '1.0.0') do + summary text + end + subject[:face_test_summary, '1.0.0'].summary.should == text + subject[:face_test_summary, '1.0.0'].summary = text + text + subject[:face_test_summary, '1.0.0'].summary.should == text + text + end + end + + describe "#initialize" do + it "should require a version number" do + expect { subject.new(:no_version) }.to raise_error ArgumentError + end + + it "should require a valid version number" do + expect { subject.new(:bad_version, 'Rasins') }. + should raise_error ArgumentError + end + + it "should instance-eval any provided block" do + face = subject.new(:face_test_block, '0.0.1') do + action(:something) do + when_invoked { "foo" } + end + end + + face.something.should == "foo" + end + end + + it "should have a name" do + subject.new(:me, '0.0.1').name.should == :me + end + + it "should stringify with its own name" do + subject.new(:me, '0.0.1').to_s.should =~ /\bme\b/ + end + + it "should allow overriding of the default format" do + face = subject.new(:me, '0.0.1') + face.set_default_format :foo + face.default_format.should == :foo + end + + it "should default to :pson for its format" do + subject.new(:me, '0.0.1').default_format.should == :pson + end + + # Why? + it "should create a class-level autoloader" do + subject.autoloader.should be_instance_of(Puppet::Util::Autoload) + end + + it "should try to require faces that are not known" do + pending "mocking require causes random stack overflow" + subject::FaceCollection.expects(:require).with "puppet/face/foo" + subject[:foo, '0.0.1'] + end + + it "should be able to load all actions in all search paths" + + + it_should_behave_like "things that declare options" do + def add_options_to(&block) + subject.new(:with_options, '0.0.1', &block) + end + end + + describe "with face-level options" do + it "should not return any action-level options" do + face = subject.new(:with_options, '0.0.1') do + option "--foo" + option "--bar" + action :baz do + option "--quux" + end + end + face.options.should =~ [:foo, :bar] + end + + it "should fail when a face option duplicates an action option" do + expect { + subject.new(:action_level_options, '0.0.1') do + action :bar do option "--foo" end + option "--foo" + end + }.should raise_error ArgumentError, /Option foo conflicts with existing option foo on/i + end + + it "should work when two actions have the same option" do + face = subject.new(:with_options, '0.0.1') do + action :foo do option "--quux" end + action :bar do option "--quux" end + end + + face.get_action(:foo).options.should =~ [:quux] + face.get_action(:bar).options.should =~ [:quux] + end + end + + describe "with inherited options" do + let :parent do + parent = Class.new(subject) + parent.option("--inherited") + parent.action(:parent_action) do end + parent + end + + let :face do + face = parent.new(:example, '0.2.1') + face.option("--local") + face.action(:face_action) do end + face + end + + describe "#options" do + it "should list inherited options" do + face.options.should =~ [:inherited, :local] + end + + it "should see all options on face actions" do + face.get_action(:face_action).options.should =~ [:inherited, :local] + end + + it "should see all options on inherited actions accessed on the subclass" do + face.get_action(:parent_action).options.should =~ [:inherited, :local] + end + + it "should not see subclass actions on the parent class" do + parent.options.should =~ [:inherited] + end + + it "should not see subclass actions on actions accessed on the parent class" do + parent.get_action(:parent_action).options.should =~ [:inherited] + end + end + + describe "#get_option" do + it "should return an inherited option object" do + face.get_option(:inherited).should be_an_instance_of subject::Option + end + end + end +end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index f3120e16b..8d38657f9 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -1,570 +1,569 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet_spec/files' describe Puppet::Module do include PuppetSpec::Files before do # This is necessary because of the extra checks we have for the deprecated # 'plugins' directory FileTest.stubs(:exist?).returns false end it "should have a class method that returns a named module from a given environment" do env = mock 'module' env.expects(:module).with("mymod").returns "yep" Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should == "yep" end it "should return nil if asked for a named module that doesn't exist" do env = mock 'module' env.expects(:module).with("mymod").returns nil Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should be_nil end it "should support a 'version' attribute" do mod = Puppet::Module.new("mymod") mod.version = 1.09 mod.version.should == 1.09 end it "should support a 'source' attribute" do mod = Puppet::Module.new("mymod") mod.source = "http://foo/bar" mod.source.should == "http://foo/bar" end it "should support a 'project_page' attribute" do mod = Puppet::Module.new("mymod") mod.project_page = "http://foo/bar" mod.project_page.should == "http://foo/bar" end it "should support an 'author' attribute" do mod = Puppet::Module.new("mymod") mod.author = "Luke Kanies " mod.author.should == "Luke Kanies " end it "should support a 'license' attribute" do mod = Puppet::Module.new("mymod") mod.license = "GPL2" mod.license.should == "GPL2" end it "should support a 'summary' attribute" do mod = Puppet::Module.new("mymod") mod.summary = "GPL2" mod.summary.should == "GPL2" end it "should support a 'description' attribute" do mod = Puppet::Module.new("mymod") mod.description = "GPL2" mod.description.should == "GPL2" end it "should support specifying a compatible puppet version" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" mod.puppetversion.should == "0.25" end it "should validate that the puppet version is compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.expects(:version).returns "0.25" mod.validate_puppet_version end it "should fail if the specified puppet version is not compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.stubs(:version).returns "0.24" lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) end describe "when specifying required modules" do it "should support specifying a required module" do mod = Puppet::Module.new("mymod") mod.requires "foobar" end it "should support specifying multiple required modules" do mod = Puppet::Module.new("mymod") mod.requires "foobar" mod.requires "baz" end it "should support specifying a required module and version" do mod = Puppet::Module.new("mymod") mod.requires "foobar", 1.0 end it "should fail when required modules are missing" do mod = Puppet::Module.new("mymod") mod.requires "foobar" mod.environment.expects(:module).with("foobar").returns nil lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) end it "should fail when required modules are present but of the wrong version" do mod = Puppet::Module.new("mymod") mod.requires "foobar", 1.0 foobar = Puppet::Module.new("foobar") foobar.version = 2.0 mod.environment.expects(:module).with("foobar").returns foobar lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::IncompatibleModule) end it "should have valid dependencies when no dependencies have been specified" do mod = Puppet::Module.new("mymod") lambda { mod.validate_dependencies }.should_not raise_error end it "should fail when some dependencies are present but others aren't" do mod = Puppet::Module.new("mymod") mod.requires "foobar" mod.requires "baz" mod.environment.expects(:module).with("foobar").returns Puppet::Module.new("foobar") mod.environment.expects(:module).with("baz").returns nil lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) end it "should have valid dependencies when all dependencies are met" do mod = Puppet::Module.new("mymod") mod.requires "foobar", 1.0 mod.requires "baz" foobar = Puppet::Module.new("foobar") foobar.version = 1.0 baz = Puppet::Module.new("baz") mod.environment.expects(:module).with("foobar").returns foobar mod.environment.expects(:module).with("baz").returns baz lambda { mod.validate_dependencies }.should_not raise_error end it "should validate its dependendencies on initialization" do Puppet::Module.any_instance.expects(:validate_dependencies) Puppet::Module.new("mymod") end end describe "when managing supported platforms" do it "should support specifying a supported platform" do mod = Puppet::Module.new("mymod") mod.supports "solaris" end it "should support specifying a supported platform and version" do mod = Puppet::Module.new("mymod") mod.supports "solaris", 1.0 end it "should fail when not running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" mod.supports "hpux" lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::UnsupportedPlatform) end it "should fail when supported platforms are present but of the wrong version" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when no supported platforms have been specified" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") lambda { mod.validate_supported_platform }.should_not raise_error end it "should be considered supported when running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when running on any of multiple supported platforms" do pending "Not sure how to send client platform to the module" end it "should validate its platform support on initialization" do pending "Not sure how to send client platform to the module" end end it "should return nil if asked for a module whose name is 'nil'" do Puppet::Module.find(nil, "myenv").should be_nil end it "should provide support for logging" do Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) end it "should be able to be converted to a string" do Puppet::Module.new("foo").to_s.should == "Module foo" end it "should add the path to its string form if the module is found" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a" mod.to_s.should == "Module foo(/a)" end it "should fail if its name is not alphanumeric" do lambda { Puppet::Module.new(".something") }.should raise_error(Puppet::Module::InvalidName) end it "should require a name at initialization" do lambda { Puppet::Module.new }.should raise_error(ArgumentError) end it "should convert an environment name into an Environment instance" do Puppet::Module.new("foo", "prod").environment.should be_instance_of(Puppet::Node::Environment) end it "should accept an environment at initialization" do Puppet::Module.new("foo", :prod).environment.name.should == :prod end it "should use the default environment if none is provided" do env = Puppet::Node::Environment.new Puppet::Module.new("foo").environment.should equal(env) end it "should use any provided Environment instance" do env = Puppet::Node::Environment.new Puppet::Module.new("foo", env).environment.should equal(env) end it "should return the path to the first found instance in its environment's module paths as its path" do dir = tmpdir("deep_path") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) Puppet[:modulepath] = "#{first}:#{second}" modpath = File.join(first, "foo") FileUtils.mkdir_p(modpath) # Make a second one, which we shouldn't find FileUtils.mkdir_p(File.join(second, "foo")) mod = Puppet::Module.new("foo") mod.path.should == modpath end it "should be able to find itself in a directory other than the first directory in the module path" do dir = tmpdir("deep_path") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) Puppet[:modulepath] = "#{first}:#{second}" modpath = File.join(second, "foo") FileUtils.mkdir_p(modpath) mod = Puppet::Module.new("foo") mod.should be_exist mod.path.should == modpath end it "should be considered existent if it exists in at least one module path" do mod = Puppet::Module.new("foo") mod.expects(:path).returns "/a/foo" mod.should be_exist end it "should be considered nonexistent if it does not exist in any of the module paths" do mod = Puppet::Module.new("foo") mod.expects(:path).returns nil mod.should_not be_exist end [:plugins, :templates, :files, :manifests].each do |filetype| dirname = filetype == :plugins ? "lib" : filetype.to_s it "should be able to return individual #{filetype}" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == path end it "should consider #{filetype} to be present if their base directory exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s + "?").should be_true end it "should consider #{filetype} to be absent if their base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s + "?").should be_false end it "should consider #{filetype} to be absent if the module base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s + "?").should be_false end it "should return nil if asked to return individual #{filetype} that don't exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return nil when asked for individual #{filetype} if the module does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return the base directory if asked for a nil path" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" base = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(base).returns true mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base end end %w{plugins files}.each do |filetype| short = filetype.sub(/s$/, '') dirname = filetype == "plugins" ? "lib" : filetype.to_s it "should be able to return the #{short} directory" do Puppet::Module.new("foo").should respond_to(short + "_directory") end it "should return the path to the #{short} directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.send(short + "_directory").should == "/a/foo/#{dirname}" end end it "should throw a warning if plugins are in a 'plugins' directory rather than a 'lib' directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" FileTest.expects(:exist?).with("/a/foo/plugins").returns true mod.plugin_directory.should == "/a/foo/plugins" @logs.first.message.should == "using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'" @logs.first.level.should == :warning end it "should default to 'lib' for the plugins directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.plugin_directory.should == "/a/foo/lib" end end describe Puppet::Module, " when building its search path" do it "should use the current environment's search path if no environment is specified" do env = mock 'env' env.expects(:modulepath).returns "eh" Puppet::Node::Environment.expects(:new).with(nil).returns env Puppet::Module.modulepath.should == "eh" end it "should use the specified environment's search path if an environment is specified" do env = mock 'env' env.expects(:modulepath).returns "eh" Puppet::Node::Environment.expects(:new).with("foo").returns env Puppet::Module.modulepath("foo").should == "eh" end end describe Puppet::Module, "when finding matching manifests" do before do @mod = Puppet::Module.new("mymod") @mod.stubs(:path).returns "/a" @pq_glob_with_extension = "yay/*.xx" @fq_glob_with_extension = "/a/manifests/#{@pq_glob_with_extension}" end it "should return all manifests matching the glob pattern" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.stubs(:directory?).returns false @mod.match_manifests(@pq_glob_with_extension).should == %w{foo bar} end it "should not return directories" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.expects(:directory?).with("foo").returns false FileTest.expects(:directory?).with("bar").returns true @mod.match_manifests(@pq_glob_with_extension).should == %w{foo} end it "should default to the 'init' file if no glob pattern is specified" do Dir.expects(:glob).with("/a/manifests/init.{pp,rb}").returns(%w{/a/manifests/init.pp}) @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} end it "should return all manifests matching the glob pattern in all existing paths" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{a b}) @mod.match_manifests(@pq_glob_with_extension).should == %w{a b} end it "should match the glob pattern plus '.{pp,rb}' if no extention is specified" do Dir.expects(:glob).with("/a/manifests/yay/foo.{pp,rb}").returns(%w{yay}) @mod.match_manifests("yay/foo").should == %w{yay} end it "should return an empty array if no manifests matched" do Dir.expects(:glob).with(@fq_glob_with_extension).returns([]) @mod.match_manifests(@pq_glob_with_extension).should == [] end end describe Puppet::Module do before do Puppet::Module.any_instance.stubs(:path).returns "/my/mod/path" @module = Puppet::Module.new("foo") end it "should use 'License' in its current path as its metadata file" do @module.license_file.should == "/my/mod/path/License" end it "should return nil as its license file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").license_file.should be_nil end it "should cache the license file" do Puppet::Module.any_instance.expects(:path).once.returns nil mod = Puppet::Module.new("foo") mod.license_file.should == mod.license_file end it "should use 'metadata.json' in its current path as its metadata file" do @module.metadata_file.should == "/my/mod/path/metadata.json" end it "should return nil as its metadata file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").metadata_file.should be_nil end it "should cache the metadata file" do Puppet::Module.any_instance.expects(:path).once.returns nil mod = Puppet::Module.new("foo") mod.metadata_file.should == mod.metadata_file end it "should know if it has a metadata file" do FileTest.expects(:exist?).with(@module.metadata_file).returns true @module.should be_has_metadata end it "should know if it is missing a metadata file" do FileTest.expects(:exist?).with(@module.metadata_file).returns false @module.should_not be_has_metadata end it "should be able to parse its metadata file" do @module.should respond_to(:load_metadata) end it "should parse its metadata file on initialization if it is present" do Puppet::Module.any_instance.expects(:has_metadata?).returns true Puppet::Module.any_instance.expects(:load_metadata) Puppet::Module.new("yay") end describe "when loading the medatada file", :if => Puppet.features.json? do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25" } @text = @data.to_json @module = Puppet::Module.new("foo") @module.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns @text end %w{source author version license}.each do |attr| it "should set #{attr} if present in the metadata file" do @module.load_metadata @module.send(attr).should == @data[attr.to_sym] end it "should fail if #{attr} is not present in the metadata file" do @data.delete(attr.to_sym) @text = @data.to_json File.stubs(:read).with("/my/file").returns @text lambda { @module.load_metadata }.should raise_error(Puppet::Module::MissingMetadata) end end it "should set puppetversion if present in the metadata file" do @module.load_metadata @module.puppetversion.should == @data[:puppetversion] end it "should fail if the discovered name is different than the metadata name" end end diff --git a/spec/unit/network/authconfig_spec.rb b/spec/unit/network/authconfig_spec.rb index 9d69e99ac..c47b2e0c5 100755 --- a/spec/unit/network/authconfig_spec.rb +++ b/spec/unit/network/authconfig_spec.rb @@ -1,292 +1,291 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/authconfig' describe Puppet::Network::AuthConfig do before do @rights = stubs 'rights' Puppet::Network::Rights.stubs(:new).returns(@rights) @rights.stubs(:each).returns([]) FileTest.stubs(:exists?).returns(true) File.stubs(:stat).returns(stub('stat', :ctime => :now)) Time.stubs(:now).returns Time.now @authconfig = Puppet::Network::AuthConfig.new("dummy", false) end describe "when initializing" do before :each do Puppet::Network::AuthConfig.any_instance.stubs(:read) end it "should use the authconfig default pathname if none provided" do Puppet.expects(:[]).with(:authconfig).returns("dummy") Puppet::Network::AuthConfig.new end it "should raise an error if no file is defined finally" do Puppet.stubs(:[]).with(:authconfig).returns(nil) lambda { Puppet::Network::AuthConfig.new }.should raise_error(Puppet::DevError) end it "should read and parse the file if parsenow is true" do Puppet::Network::AuthConfig.any_instance.expects(:read) Puppet::Network::AuthConfig.new("dummy", true) end end describe "when checking authorization" do before :each do @authconfig.stubs(:read) @call = stub 'call', :intern => "name" @handler = stub 'handler', :intern => "handler" @method = stub_everything 'method' @request = stub 'request', :call => @call, :handler => @handler, :method => @method, :name => "me", :ip => "1.2.3.4" end it "should attempt to read the authconfig file" do @rights.stubs(:include?) @authconfig.expects(:read) @authconfig.allowed?(@request) end it "should use a name right if it exists" do right = stub 'right' @rights.stubs(:include?).with("name").returns(true) @rights.stubs(:[]).with("name").returns(right) right.expects(:allowed?).with("me", "1.2.3.4") @authconfig.allowed?(@request) end it "should use a namespace right otherwise" do right = stub 'right' @rights.stubs(:include?).with("name").returns(false) @rights.stubs(:include?).with("handler").returns(true) @rights.stubs(:[]).with("handler").returns(right) right.expects(:allowed?).with("me", "1.2.3.4") @authconfig.allowed?(@request) end it "should return whatever the found rights returns" do right = stub 'right' @rights.stubs(:include?).with("name").returns(true) @rights.stubs(:[]).with("name").returns(right) right.stubs(:allowed?).with("me", "1.2.3.4").returns(:returned) @authconfig.allowed?(@request).should == :returned end end describe "when parsing authconfig file" do before :each do @fd = stub 'fd' File.stubs(:open).yields(@fd) @rights.stubs(:include?).returns(false) @rights.stubs(:[]) end it "should skip comments" do @fd.stubs(:each).yields(' # comment') @rights.expects(:newright).never @authconfig.read end it "should increment line number even on commented lines" do @fd.stubs(:each).multiple_yields(' # comment','[puppetca]') @rights.expects(:newright).with('[puppetca]', 2, 'dummy') @authconfig.read end it "should skip blank lines" do @fd.stubs(:each).yields(' ') @rights.expects(:newright).never @authconfig.read end it "should increment line number even on blank lines" do @fd.stubs(:each).multiple_yields(' ','[puppetca]') @rights.expects(:newright).with('[puppetca]', 2, 'dummy') @authconfig.read end it "should throw an error if the current namespace right already exist" do @fd.stubs(:each).yields('[puppetca]') @rights.stubs(:include?).with("puppetca").returns(true) lambda { @authconfig.read }.should raise_error end it "should not throw an error if the current path right already exist" do @fd.stubs(:each).yields('path /hello') @rights.stubs(:newright).with("/hello",1, 'dummy') @rights.stubs(:include?).with("/hello").returns(true) lambda { @authconfig.read }.should_not raise_error end it "should create a new right for found namespaces" do @fd.stubs(:each).yields('[puppetca]') @rights.expects(:newright).with("[puppetca]", 1, 'dummy') @authconfig.read end it "should create a new right for each found namespace line" do @fd.stubs(:each).multiple_yields('[puppetca]', '[fileserver]') @rights.expects(:newright).with("[puppetca]", 1, 'dummy') @rights.expects(:newright).with("[fileserver]", 2, 'dummy') @authconfig.read end it "should create a new right for each found path line" do @fd.stubs(:each).multiple_yields('path /certificates') @rights.expects(:newright).with("/certificates", 1, 'dummy') @authconfig.read end it "should create a new right for each found regex line" do @fd.stubs(:each).multiple_yields('path ~ .rb$') @rights.expects(:newright).with("~ .rb$", 1, 'dummy') @authconfig.read end it "should create an allow ACE on each subsequent allow" do acl = stub 'acl', :info @fd.stubs(:each).multiple_yields('[puppetca]', 'allow 127.0.0.1') @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) acl.expects(:allow).with('127.0.0.1') @authconfig.read end it "should create a deny ACE on each subsequent deny" do acl = stub 'acl', :info @fd.stubs(:each).multiple_yields('[puppetca]', 'deny 127.0.0.1') @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) acl.expects(:deny).with('127.0.0.1') @authconfig.read end it "should inform the current ACL if we get the 'method' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('path /certificates', 'method search,find') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_method).with('search') acl.expects(:restrict_method).with('find') @authconfig.read end it "should raise an error if the 'method' directive is used in a right different than a path/regex one" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('[puppetca]', 'method search,find') @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) lambda { @authconfig.read }.should raise_error end it "should inform the current ACL if we get the 'environment' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('path /certificates', 'environment production,development') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_environment).with('production') acl.expects(:restrict_environment).with('development') @authconfig.read end it "should raise an error if the 'environment' directive is used in a right different than a path/regex one" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('[puppetca]', 'environment env') @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) lambda { @authconfig.read }.should raise_error end it "should inform the current ACL if we get the 'auth' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('path /certificates', 'auth yes') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_authenticated).with('yes') @authconfig.read end it "should also allow the longest 'authenticated' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('path /certificates', 'authenticated yes') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_authenticated).with('yes') @authconfig.read end it "should raise an error if the 'auth' directive is used in a right different than a path/regex one" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each).multiple_yields('[puppetca]', 'auth yes') @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) lambda { @authconfig.read }.should raise_error end end end diff --git a/spec/unit/network/authstore_spec.rb b/spec/unit/network/authstore_spec.rb old mode 100644 new mode 100755 index d477ee301..d62c8abaa --- a/spec/unit/network/authstore_spec.rb +++ b/spec/unit/network/authstore_spec.rb @@ -1,370 +1,369 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/authconfig' describe Puppet::Network::AuthStore do describe "when checking if the acl has some entries" do before :each do @authstore = Puppet::Network::AuthStore.new end it "should be empty if no ACE have been entered" do @authstore.should be_empty end it "should not be empty if it is a global allow" do @authstore.allow('*') @authstore.should_not be_empty end it "should not be empty if at least one allow has been entered" do @authstore.allow('1.1.1.*') @authstore.should_not be_empty end it "should not be empty if at least one deny has been entered" do @authstore.deny('1.1.1.*') @authstore.should_not be_empty end end end describe Puppet::Network::AuthStore::Declaration do ['100.101.99.98','100.100.100.100','1.2.3.4','11.22.33.44'].each { |ip| describe "when the pattern is a simple numeric IP such as #{ip}" do before :each do @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) end it "should match the specified IP" do @declaration.should be_match('www.testsite.org',ip) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org','200.101.99.98') end end (1..3).each { |n| describe "when the pattern is a IP mask with #{n} numeric segments and a *" do before :each do @ip_pattern = ip.split('.')[0,n].join('.')+'.*' @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip_pattern) end it "should match an IP in the range" do @declaration.should be_match('www.testsite.org',ip) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org','200.101.99.98') end it "should not match IPs that differ in the last non-wildcard segment" do other = ip.split('.') other[n-1].succ! @declaration.should_not be_match('www.testsite.org',other.join('.')) end end } } describe "when the pattern is a numeric IP with a back reference" do before :each do @ip = '100.101.$1' @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip).interpolate('12.34'.match(/(.*)/)) end it "should match an IP with the appropriate interpolation" do @declaration.should be_match('www.testsite.org',@ip.sub(/\$1/,'12.34')) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org',@ip.sub(/\$1/,'66.34')) end end [ "02001:0000:1234:0000:0000:C1C0:ABCD:0876", "2001:0000:1234:0000:00001:C1C0:ABCD:0876", " 2001:0000:1234:0000:0000:C1C0:ABCD:0876 0", "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", "3ffe:0b00:0000:0001:0000:0000:000a", "FF02:0000:0000:0000:0000:0000:0000:0000:0001", "3ffe:b00::1::a", "1:2:3::4:5::7:8", "12345::6:7:8", "1::5:400.2.3.4", "1::5:260.2.3.4", "1::5:256.2.3.4", "1::5:1.256.3.4", "1::5:1.2.256.4", "1::5:1.2.3.256", "1::5:300.2.3.4", "1::5:1.300.3.4", "1::5:1.2.300.4", "1::5:1.2.3.300", "1::5:900.2.3.4", "1::5:1.900.3.4", "1::5:1.2.900.4", "1::5:1.2.3.900", "1::5:300.300.300.300", "1::5:3000.30.30.30", "1::400.2.3.4", "1::260.2.3.4", "1::256.2.3.4", "1::1.256.3.4", "1::1.2.256.4", "1::1.2.3.256", "1::300.2.3.4", "1::1.300.3.4", "1::1.2.300.4", "1::1.2.3.300", "1::900.2.3.4", "1::1.900.3.4", "1::1.2.900.4", "1::1.2.3.900", "1::300.300.300.300", "1::3000.30.30.30", "::400.2.3.4", "::260.2.3.4", "::256.2.3.4", "::1.256.3.4", "::1.2.256.4", "::1.2.3.256", "::300.2.3.4", "::1.300.3.4", "::1.2.300.4", "::1.2.3.300", "::900.2.3.4", "::1.900.3.4", "::1.2.900.4", "::1.2.3.900", "::300.300.300.300", "::3000.30.30.30", "2001:DB8:0:0:8:800:200C:417A:221", # unicast, full "FF01::101::2" # multicast, compressed ].each { |invalid_ip| describe "when the pattern is an invalid IPv6 address such as #{invalid_ip}" do it "should raise an exception" do lambda { Puppet::Network::AuthStore::Declaration.new(:allow,invalid_ip) }.should raise_error end end } [ "1.2.3.4", "2001:0000:1234:0000:0000:C1C0:ABCD:0876", "3ffe:0b00:0000:0000:0001:0000:0000:000a", "FF02:0000:0000:0000:0000:0000:0000:0001", "0000:0000:0000:0000:0000:0000:0000:0001", "0000:0000:0000:0000:0000:0000:0000:0000", "::ffff:192.168.1.26", "2::10", "ff02::1", "fe80::", "2002::", "2001:db8::", "2001:0db8:1234::", "::ffff:0:0", "::1", "::ffff:192.168.1.1", "1:2:3:4:5:6:7:8", "1:2:3:4:5:6::8", "1:2:3:4:5::8", "1:2:3:4::8", "1:2:3::8", "1:2::8", "1::8", "1::2:3:4:5:6:7", "1::2:3:4:5:6", "1::2:3:4:5", "1::2:3:4", "1::2:3", "1::8", "::2:3:4:5:6:7:8", "::2:3:4:5:6:7", "::2:3:4:5:6", "::2:3:4:5", "::2:3:4", "::2:3", "::8", "1:2:3:4:5:6::", "1:2:3:4:5::", "1:2:3:4::", "1:2:3::", "1:2::", "1::", "1:2:3:4:5::7:8", "1:2:3:4::7:8", "1:2:3::7:8", "1:2::7:8", "1::7:8", "1:2:3:4:5:6:1.2.3.4", "1:2:3:4:5::1.2.3.4", "1:2:3:4::1.2.3.4", "1:2:3::1.2.3.4", "1:2::1.2.3.4", "1::1.2.3.4", "1:2:3:4::5:1.2.3.4", "1:2:3::5:1.2.3.4", "1:2::5:1.2.3.4", "1::5:1.2.3.4", "1::5:11.22.33.44", "fe80::217:f2ff:254.7.237.98", "fe80::217:f2ff:fe07:ed62", "2001:DB8:0:0:8:800:200C:417A", # unicast, full "FF01:0:0:0:0:0:0:101", # multicast, full "0:0:0:0:0:0:0:1", # loopback, full "0:0:0:0:0:0:0:0", # unspecified, full "2001:DB8::8:800:200C:417A", # unicast, compressed "FF01::101", # multicast, compressed "::1", # loopback, compressed, non-routable "::", # unspecified, compressed, non-routable "0:0:0:0:0:0:13.1.68.3", # IPv4-compatible IPv6 address, full, deprecated "0:0:0:0:0:FFFF:129.144.52.38", # IPv4-mapped IPv6 address, full "::13.1.68.3", # IPv4-compatible IPv6 address, compressed, deprecated "::FFFF:129.144.52.38", # IPv4-mapped IPv6 address, compressed "2001:0DB8:0000:CD30:0000:0000:0000:0000/60", # full, with prefix "2001:0DB8::CD30:0:0:0:0/60", # compressed, with prefix "2001:0DB8:0:CD30::/60", # compressed, with prefix #2 "::/128", # compressed, unspecified address type, non-routable "::1/128", # compressed, loopback address type, non-routable "FF00::/8", # compressed, multicast address type "FE80::/10", # compressed, link-local unicast, non-routable "FEC0::/10", # compressed, site-local unicast, deprecated "127.0.0.1", # standard IPv4, loopback, non-routable "0.0.0.0", # standard IPv4, unspecified, non-routable "255.255.255.255", # standard IPv4 "fe80:0000:0000:0000:0204:61ff:fe9d:f156", "fe80:0:0:0:204:61ff:fe9d:f156", "fe80::204:61ff:fe9d:f156", "fe80:0000:0000:0000:0204:61ff:254.157.241.086", "fe80:0:0:0:204:61ff:254.157.241.86", "fe80::204:61ff:254.157.241.86", "::1", "fe80::", "fe80::1" ].each { |ip| describe "when the pattern is a valid IP such as #{ip}" do before :each do @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) end it "should match the specified IP" do @declaration.should be_match('www.testsite.org',ip) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org','200.101.99.98') end end unless ip =~ /:.*\./ # Hybrid IPs aren't supported by ruby's ipaddr } { 'spirit.mars.nasa.gov' => 'a PQDN', 'ratchet.2ndsiteinc.com' => 'a PQDN with digits', 'a.c.ru' => 'a PQDN with short segments', }.each {|pqdn,desc| describe "when the pattern is #{desc}" do before :each do @host = pqdn @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) end it "should match the specified PQDN" do @declaration.should be_match(@host,'200.101.99.98') end it "should not match a similar FQDN" do pending "FQDN consensus" @declaration.should_not be_match(@host+'.','200.101.99.98') end end } ['abc.12seps.edu.phisher.biz','www.google.com','slashdot.org'].each { |host| (1...(host.split('.').length)).each { |n| describe "when the pattern is #{"*."+host.split('.')[-n,n].join('.')}" do before :each do @pattern = "*."+host.split('.')[-n,n].join('.') @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@pattern) end it "should match #{host}" do @declaration.should be_match(host,'1.2.3.4') end it "should not match www.testsite.gov" do @declaration.should_not be_match('www.testsite.gov','200.101.99.98') end it "should not match hosts that differ in the first non-wildcard segment" do other = host.split('.') other[-n].succ! @declaration.should_not be_match(other.join('.'),'1.2.3.4') end end } } describe "when the pattern is a FQDN" do before :each do @host = 'spirit.mars.nasa.gov.' @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) end it "should match the specified FQDN" do pending "FQDN consensus" @declaration.should be_match(@host,'200.101.99.98') end it "should not match a similar PQDN" do @declaration.should_not be_match(@host[0..-2],'200.101.99.98') end end describe "when the pattern is an opaque string with a back reference" do before :each do @host = 'c216f41a-f902-4bfb-a222-850dd957bebb' @item = "/catalog/#{@host}" @pattern = %{^/catalog/([^/]+)$} @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') end it "should match an IP with the appropriate interpolation" do @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') end end describe "when the pattern is an opaque string with a back reference and the matched data contains dots" do before :each do @host = 'admin.mgmt.nym1' @item = "/catalog/#{@host}" @pattern = %{^/catalog/([^/]+)$} @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') end it "should match a name with the appropriate interpolation" do @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') end end describe "when the pattern is an opaque string with a back reference and the matched data contains dots with an initial prefix that looks like an IP address" do before :each do @host = '01.admin.mgmt.nym1' @item = "/catalog/#{@host}" @pattern = %{^/catalog/([^/]+)$} @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') end it "should match a name with the appropriate interpolation" do @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') end end describe "when comparing patterns" do before :each do @ip = Puppet::Network::AuthStore::Declaration.new(:allow,'127.0.0.1') @host_name = Puppet::Network::AuthStore::Declaration.new(:allow,'www.hard_knocks.edu') @opaque = Puppet::Network::AuthStore::Declaration.new(:allow,'hey_dude') end it "should consider ip addresses before host names" do (@ip < @host_name).should be_true end it "should consider ip addresses before opaque strings" do (@ip < @opaque).should be_true end it "should consider host_names before opaque strings" do (@host_name < @opaque).should be_true end end end diff --git a/spec/unit/network/client_spec.rb b/spec/unit/network/client_spec.rb index 820f10f12..102a053c0 100755 --- a/spec/unit/network/client_spec.rb +++ b/spec/unit/network/client_spec.rb @@ -1,45 +1,45 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-24. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/network/client' describe Puppet::Network::Client do before do Puppet.settings.stubs(:use).returns(true) Puppet::Network::HttpPool.stubs(:cert_setup) end describe "when keep-alive is enabled" do before do Puppet::Network::HttpPool.stubs(:keep_alive?).returns true end it "should start the http client up on creation" do http = mock 'http' http.stub_everything http.expects(:start) Net::HTTP.stubs(:new).returns http # Pick a random subclass... Puppet::Network::Client.runner.new :Server => Puppet[:server] end end describe "when keep-alive is disabled" do before do Puppet::Network::HttpPool.stubs(:keep_alive?).returns false end it "should not start the http client up on creation" do http = mock 'http' http.stub_everything http.expects(:start).never Net::HTTP.stubs(:new).returns http # Pick a random subclass... Puppet::Network::Client.runner.new :Server => Puppet[:server] end end end diff --git a/spec/unit/network/format_handler_spec.rb b/spec/unit/network/format_handler_spec.rb index 556fada80..8b535c3ab 100755 --- a/spec/unit/network/format_handler_spec.rb +++ b/spec/unit/network/format_handler_spec.rb @@ -1,336 +1,335 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/format_handler' class FormatTester extend Puppet::Network::FormatHandler end describe Puppet::Network::FormatHandler do after do formats = Puppet::Network::FormatHandler.instance_variable_get("@formats") formats.each do |name, format| formats.delete(name) unless format.is_a?(Puppet::Network::Format) end end it "should be able to list supported formats" do FormatTester.should respond_to(:supported_formats) end it "should include all supported formats" do one = stub 'supported', :supported? => true, :name => :one, :weight => 1 two = stub 'supported', :supported? => false, :name => :two, :weight => 1 three = stub 'supported', :supported? => true, :name => :three, :weight => 1 four = stub 'supported', :supported? => false, :name => :four, :weight => 1 Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four result = FormatTester.supported_formats result.length.should == 2 result.should be_include(:one) result.should be_include(:three) end it "should return the supported formats in decreasing order of weight" do one = stub 'supported', :supported? => true, :name => :one, :weight => 1 two = stub 'supported', :supported? => true, :name => :two, :weight => 6 three = stub 'supported', :supported? => true, :name => :three, :weight => 2 four = stub 'supported', :supported? => true, :name => :four, :weight => 8 Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four FormatTester.supported_formats.should == [:four, :two, :three, :one] end describe "with a preferred serialization format setting" do before do one = stub 'supported', :supported? => true, :name => :one, :weight => 1 two = stub 'supported', :supported? => true, :name => :two, :weight => 6 Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two] Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two end describe "that is supported" do before do Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :one end it "should return the preferred serialization format first" do FormatTester.supported_formats.should == [:one, :two] end end describe "that is not supported" do before do Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :unsupported end it "should still return the default format first" do FormatTester.supported_formats.should == [:two, :one] end it "should log a debug message" do Puppet.expects(:debug).with("Value of 'preferred_serialization_format' (unsupported) is invalid for FormatTester, using default (two)") Puppet.expects(:debug).with("FormatTester supports formats: one two; using two") FormatTester.supported_formats end end end it "should return the first format as the default format" do FormatTester.expects(:supported_formats).returns [:one, :two] FormatTester.default_format.should == :one end it "should be able to use a protected format for better logging on errors" do Puppet::Network::FormatHandler.should respond_to(:protected_format) end it "should delegate all methods from the informative format to the specified format" do format = mock 'format' format.stubs(:name).returns(:myformat) Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format format.expects(:render).with("foo").returns "yay" Puppet::Network::FormatHandler.protected_format(:myformat).render("foo").should == "yay" end it "should provide better logging if a failure is encountered when delegating from the informative format to the real format" do format = mock 'format' format.stubs(:name).returns(:myformat) Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format format.expects(:render).with("foo").raises "foo" lambda { Puppet::Network::FormatHandler.protected_format(:myformat).render("foo") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should raise an error if we couldn't find a format by name or mime-type" do Puppet::Network::FormatHandler.stubs(:format).with(:myformat).returns nil lambda { Puppet::Network::FormatHandler.protected_format(:myformat) }.should raise_error end describe "when using formats" do before do @format = mock 'format' @format.stubs(:supported?).returns true @format.stubs(:name).returns :my_format Puppet::Network::FormatHandler.stubs(:format).with(:my_format).returns @format Puppet::Network::FormatHandler.stubs(:mime).with("text/myformat").returns @format Puppet::Network::Format.stubs(:===).returns false Puppet::Network::Format.stubs(:===).with(@format).returns true end it "should be able to test whether a format is supported" do FormatTester.should respond_to(:support_format?) end it "should use the Format to determine whether a given format is supported" do @format.expects(:supported?).with(FormatTester) FormatTester.support_format?(:my_format) end it "should be able to convert from a given format" do FormatTester.should respond_to(:convert_from) end it "should call the format-specific converter when asked to convert from a given format" do @format.expects(:intern).with(FormatTester, "mydata") FormatTester.convert_from(:my_format, "mydata") end it "should call the format-specific converter when asked to convert from a given format by mime-type" do @format.expects(:intern).with(FormatTester, "mydata") FormatTester.convert_from("text/myformat", "mydata") end it "should call the format-specific converter when asked to convert from a given format by format instance" do @format.expects(:intern).with(FormatTester, "mydata") FormatTester.convert_from(@format, "mydata") end it "should raise a FormatError when an exception is encountered when converting from a format" do @format.expects(:intern).with(FormatTester, "mydata").raises "foo" lambda { FormatTester.convert_from(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should be able to use a specific hook for converting into multiple instances" do @format.expects(:intern_multiple).with(FormatTester, "mydata") FormatTester.convert_from_multiple(:my_format, "mydata") end it "should raise a FormatError when an exception is encountered when converting multiple items from a format" do @format.expects(:intern_multiple).with(FormatTester, "mydata").raises "foo" lambda { FormatTester.convert_from_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should be able to use a specific hook for rendering multiple instances" do @format.expects(:render_multiple).with("mydata") FormatTester.render_multiple(:my_format, "mydata") end it "should raise a FormatError when an exception is encountered when rendering multiple items into a format" do @format.expects(:render_multiple).with("mydata").raises "foo" lambda { FormatTester.render_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end end describe "when managing formats" do it "should have a method for defining a new format" do Puppet::Network::FormatHandler.should respond_to(:create) end it "should create a format instance when asked" do format = stub 'format', :name => :foo Puppet::Network::Format.expects(:new).with(:foo).returns format Puppet::Network::FormatHandler.create(:foo) end it "should instance_eval any block provided when creating a format" do format = stub 'format', :name => :instance_eval format.expects(:yayness) Puppet::Network::Format.expects(:new).returns format Puppet::Network::FormatHandler.create(:instance_eval) do yayness end end it "should be able to retrieve a format by name" do format = Puppet::Network::FormatHandler.create(:by_name) Puppet::Network::FormatHandler.format(:by_name).should equal(format) end it "should be able to retrieve a format by extension" do format = Puppet::Network::FormatHandler.create(:by_extension, :extension => "foo") Puppet::Network::FormatHandler.format_by_extension("foo").should equal(format) end it "should return nil if asked to return a format by an unknown extension" do Puppet::Network::FormatHandler.format_by_extension("yayness").should be_nil end it "should be able to retrieve formats by name irrespective of case and class" do format = Puppet::Network::FormatHandler.create(:by_name) Puppet::Network::FormatHandler.format(:By_Name).should equal(format) end it "should be able to retrieve a format by mime type" do format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") Puppet::Network::FormatHandler.mime("foo/bar").should equal(format) end it "should be able to retrieve a format by mime type irrespective of case" do format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") Puppet::Network::FormatHandler.mime("Foo/Bar").should equal(format) end it "should be able to return all formats" do one = stub 'one', :name => :one two = stub 'two', :name => :two Puppet::Network::Format.expects(:new).with(:one).returns(one) Puppet::Network::Format.expects(:new).with(:two).returns(two) Puppet::Network::FormatHandler.create(:one) Puppet::Network::FormatHandler.create(:two) list = Puppet::Network::FormatHandler.formats list.should be_include(:one) list.should be_include(:two) end end describe "when an instance" do it "should be able to test whether a format is supported" do FormatTester.new.should respond_to(:support_format?) end it "should be able to convert to a given format" do FormatTester.new.should respond_to(:render) end it "should be able to get a format mime-type" do FormatTester.new.should respond_to(:mime) end it "should raise a FormatError when a rendering error is encountered" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).raises "eh" lambda { tester.render(:foo) }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should call the format-specific converter when asked to convert to a given format" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).returns "foo" tester.render(:foo).should == "foo" end it "should call the format-specific converter when asked to convert to a given format by mime-type" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:mime).with("text/foo").returns format Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).returns "foo" tester.render("text/foo").should == "foo" end it "should call the format converter when asked to convert to a given format instance" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::Format.stubs(:===).with(format).returns(true) Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).returns "foo" tester.render(format).should == "foo" end it "should render to the default format if no format is provided when rendering" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format FormatTester.expects(:default_format).returns :foo tester = FormatTester.new format.expects(:render).with(tester) tester.render end it "should call the format-specific converter when asked for the mime-type of a given format" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:mime).returns "text/foo" tester.mime(:foo).should == "text/foo" end it "should return the default format mime-type if no format is provided" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format FormatTester.expects(:default_format).returns :foo tester = FormatTester.new format.expects(:mime).returns "text/foo" tester.mime.should == "text/foo" end end end diff --git a/spec/unit/network/format_spec.rb b/spec/unit/network/format_spec.rb index 658729e4c..f59593479 100755 --- a/spec/unit/network/format_spec.rb +++ b/spec/unit/network/format_spec.rb @@ -1,198 +1,197 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/format' # A class with all of the necessary # hooks. class FormatRenderer def self.to_multiple_my_format(list) end def self.from_multiple_my_format(text) end def self.from_my_format(text) end def to_my_format end end describe Puppet::Network::Format do describe "when initializing" do it "should require a name" do lambda { Puppet::Network::Format.new }.should raise_error(ArgumentError) end it "should be able to provide its name" do Puppet::Network::Format.new(:my_format).name.should == :my_format end it "should always convert its name to a downcased symbol" do Puppet::Network::Format.new(:My_Format).name.should == :my_format end it "should be able to set its downcased mime type at initialization" do format = Puppet::Network::Format.new(:my_format, :mime => "Foo/Bar") format.mime.should == "foo/bar" end it "should default to text plus the name of the format as the mime type" do Puppet::Network::Format.new(:my_format).mime.should == "text/my_format" end it "should fail if unsupported options are provided" do lambda { Puppet::Network::Format.new(:my_format, :foo => "bar") }.should raise_error(ArgumentError) end end describe "instances" do before do @format = Puppet::Network::Format.new(:my_format) end it "should support being confined" do @format.should respond_to(:confine) end it "should not be considered suitable if confinement conditions are not met" do @format.confine :true => false @format.should_not be_suitable end it "should be able to determine if a class is supported" do @format.should respond_to(:supported?) end it "should consider a class to be supported if it has the individual and multiple methods for rendering and interning" do @format.should be_supported(FormatRenderer) end it "should default to its required methods being the individual and multiple methods for rendering and interning" do Puppet::Network::Format.new(:foo).required_methods.sort { |a,b| a.to_s <=> b.to_s }.should == [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].sort { |a,b| a.to_s <=> b.to_s } end it "should consider a class supported if the provided class has all required methods present" do format = Puppet::Network::Format.new(:foo) [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| format.expects(:required_method_present?).with { |name, klass, type| name == method and klass == String }.returns true end format.should be_required_methods_present(String) end it "should consider a class not supported if any required methods are missing from the provided class" do format = Puppet::Network::Format.new(:foo) format.stubs(:required_method_present?).returns true format.expects(:required_method_present?).with { |name, *args| name == :intern_method }.returns false format.should_not be_required_methods_present(String) end it "should be able to specify the methods required for support" do Puppet::Network::Format.new(:foo, :required_methods => [:render_method, :intern_method]).required_methods.should == [:render_method, :intern_method] end it "should only test for required methods if specific methods are specified as required" do format = Puppet::Network::Format.new(:foo, :required_methods => [:intern_method]) format.expects(:required_method_present?).with { |name, klass, type| name == :intern_method } format.required_methods_present?(String) end it "should not consider a class supported unless the format is suitable" do @format.expects(:suitable?).returns false @format.should_not be_supported(FormatRenderer) end it "should always downcase mimetypes" do @format.mime = "Foo/Bar" @format.mime.should == "foo/bar" end it "should support having a weight" do @format.should respond_to(:weight) end it "should default to a weight of of 5" do @format.weight.should == 5 end it "should be able to override its weight at initialization" do Puppet::Network::Format.new(:foo, :weight => 1).weight.should == 1 end it "should default to its extension being equal to its name" do Puppet::Network::Format.new(:foo).extension.should == "foo" end it "should support overriding the extension" do Puppet::Network::Format.new(:foo, :extension => "bar").extension.should == "bar" end [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| it "should allow assignment of the #{method}" do Puppet::Network::Format.new(:foo, method => :foo).send(method).should == :foo end end end describe "when converting between instances and formatted text" do before do @format = Puppet::Network::Format.new(:my_format) @instance = FormatRenderer.new end it "should have a method for rendering a single instance" do @format.should respond_to(:render) end it "should have a method for rendering multiple instances" do @format.should respond_to(:render_multiple) end it "should have a method for interning text" do @format.should respond_to(:intern) end it "should have a method for interning text into multiple instances" do @format.should respond_to(:intern_multiple) end it "should return the results of calling the instance-specific render method if the method is present" do @instance.expects(:to_my_format).returns "foo" @format.render(@instance).should == "foo" end it "should return the results of calling the class-specific render_multiple method if the method is present" do @instance.class.expects(:to_multiple_my_format).returns ["foo"] @format.render_multiple([@instance]).should == ["foo"] end it "should return the results of calling the class-specific intern method if the method is present" do FormatRenderer.expects(:from_my_format).with("foo").returns @instance @format.intern(FormatRenderer, "foo").should equal(@instance) end it "should return the results of calling the class-specific intern_multiple method if the method is present" do FormatRenderer.expects(:from_multiple_my_format).with("foo").returns [@instance] @format.intern_multiple(FormatRenderer, "foo").should == [@instance] end it "should fail if asked to render and the instance does not respond to 'to_'" do lambda { @format.render("foo") }.should raise_error(NotImplementedError) end it "should fail if asked to intern and the class does not respond to 'from_'" do lambda { @format.intern(String, "foo") }.should raise_error(NotImplementedError) end it "should fail if asked to intern multiple and the class does not respond to 'from_multiple_'" do lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) end it "should fail if asked to render multiple and the instance does not respond to 'to_multiple_'" do lambda { @format.render_multiple(["foo", "bar"]) }.should raise_error(NotImplementedError) end end end diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb index 30e88c578..72d355192 100755 --- a/spec/unit/network/formats_spec.rb +++ b/spec/unit/network/formats_spec.rb @@ -1,334 +1,333 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/formats' class PsonTest attr_accessor :string def ==(other) string == other.string end def self.from_pson(data) new(data) end def initialize(string) @string = string end def to_pson(*args) { 'type' => self.class.name, 'data' => @string }.to_pson(*args) end end describe "Puppet Network Format" do it "should include a yaml format" do Puppet::Network::FormatHandler.format(:yaml).should_not be_nil end describe "yaml" do before do @yaml = Puppet::Network::FormatHandler.format(:yaml) end it "should have its mime type set to text/yaml" do @yaml.mime.should == "text/yaml" end it "should be supported on Strings" do @yaml.should be_supported(String) end it "should render by calling 'to_yaml' on the instance" do instance = mock 'instance' instance.expects(:to_yaml).returns "foo" @yaml.render(instance).should == "foo" end it "should render multiple instances by calling 'to_yaml' on the array" do instances = [mock('instance')] instances.expects(:to_yaml).returns "foo" @yaml.render_multiple(instances).should == "foo" end it "should intern by calling 'YAML.load'" do text = "foo" YAML.expects(:load).with("foo").returns "bar" @yaml.intern(String, text).should == "bar" end it "should intern multiples by calling 'YAML.load'" do text = "foo" YAML.expects(:load).with("foo").returns "bar" @yaml.intern_multiple(String, text).should == "bar" end end describe "base64 compressed yaml", :if => Puppet.features.zlib? do yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) before do @yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) end it "should have its mime type set to text/b64_zlib_yaml" do @yaml.mime.should == "text/b64_zlib_yaml" end it "should render by calling 'to_yaml' on the instance" do instance = mock 'instance' instance.expects(:to_yaml).returns "foo" @yaml.render(instance) end it "should encode generated yaml on render" do instance = mock 'instance', :to_yaml => "foo" @yaml.expects(:encode).with("foo").returns "bar" @yaml.render(instance).should == "bar" end it "should render multiple instances by calling 'to_yaml' on the array" do instances = [mock('instance')] instances.expects(:to_yaml).returns "foo" @yaml.render_multiple(instances) end it "should encode generated yaml on render" do instances = [mock('instance')] instances.stubs(:to_yaml).returns "foo" @yaml.expects(:encode).with("foo").returns "bar" @yaml.render(instances).should == "bar" end it "should intern by calling decode" do text = "foo" @yaml.expects(:decode).with("foo").returns "bar" @yaml.intern(String, text).should == "bar" end it "should intern multiples by calling 'decode'" do text = "foo" @yaml.expects(:decode).with("foo").returns "bar" @yaml.intern_multiple(String, text).should == "bar" end it "should decode by base64 decoding, uncompressing and Yaml loading" do Base64.expects(:decode64).with("zorg").returns "foo" Zlib::Inflate.expects(:inflate).with("foo").returns "baz" YAML.expects(:load).with("baz").returns "bar" @yaml.decode("zorg").should == "bar" end it "should encode by compressing and base64 encoding" do Zlib::Deflate.expects(:deflate).with("foo", Zlib::BEST_COMPRESSION).returns "bar" Base64.expects(:encode64).with("bar").returns "baz" @yaml.encode("foo").should == "baz" end describe "when zlib is disabled" do before do Puppet[:zlib] = false end it "use_zlib? should return false" do @yaml.use_zlib?.should == false end it "should refuse to encode" do lambda{ @yaml.encode("foo") }.should raise_error end it "should refuse to decode" do lambda{ @yaml.decode("foo") }.should raise_error end end describe "when zlib is not installed" do it "use_zlib? should return false" do Puppet[:zlib] = true Puppet.features.expects(:zlib?).returns(false) @yaml.use_zlib?.should == false end end end it "should include a marshal format" do Puppet::Network::FormatHandler.format(:marshal).should_not be_nil end describe "marshal" do before do @marshal = Puppet::Network::FormatHandler.format(:marshal) end it "should have its mime type set to text/marshal" do Puppet::Network::FormatHandler.format(:marshal).mime.should == "text/marshal" end it "should be supported on Strings" do @marshal.should be_supported(String) end it "should render by calling 'Marshal.dump' on the instance" do instance = mock 'instance' Marshal.expects(:dump).with(instance).returns "foo" @marshal.render(instance).should == "foo" end it "should render multiple instances by calling 'to_marshal' on the array" do instances = [mock('instance')] Marshal.expects(:dump).with(instances).returns "foo" @marshal.render_multiple(instances).should == "foo" end it "should intern by calling 'Marshal.load'" do text = "foo" Marshal.expects(:load).with("foo").returns "bar" @marshal.intern(String, text).should == "bar" end it "should intern multiples by calling 'Marshal.load'" do text = "foo" Marshal.expects(:load).with("foo").returns "bar" @marshal.intern_multiple(String, text).should == "bar" end end describe "plaintext" do before do @text = Puppet::Network::FormatHandler.format(:s) end it "should have its mimetype set to text/plain" do @text.mime.should == "text/plain" end it "should use 'txt' as its extension" do @text.extension.should == "txt" end end describe "dot" do before do @dot = Puppet::Network::FormatHandler.format(:dot) end it "should have its mimetype set to text/dot" do @dot.mime.should == "text/dot" end end describe Puppet::Network::FormatHandler.format(:raw) do before do @format = Puppet::Network::FormatHandler.format(:raw) end it "should exist" do @format.should_not be_nil end it "should have its mimetype set to application/x-raw" do @format.mime.should == "application/x-raw" end it "should always be supported" do @format.should be_supported(String) end it "should fail if its multiple_render method is used" do lambda { @format.render_multiple("foo") }.should raise_error(NotImplementedError) end it "should fail if its multiple_intern method is used" do lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) end it "should have a weight of 1" do @format.weight.should == 1 end end it "should include a pson format" do Puppet::Network::FormatHandler.format(:pson).should_not be_nil end describe "pson", :if => Puppet.features.pson? do before do @pson = Puppet::Network::FormatHandler.format(:pson) end it "should have its mime type set to text/pson" do Puppet::Network::FormatHandler.format(:pson).mime.should == "text/pson" end it "should require the :render_method" do Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:render_method) end it "should require the :intern_method" do Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:intern_method) end it "should have a weight of 10" do @pson.weight.should == 10 end describe "when supported" do it "should render by calling 'to_pson' on the instance" do instance = PsonTest.new("foo") instance.expects(:to_pson).returns "foo" @pson.render(instance).should == "foo" end it "should render multiple instances by calling 'to_pson' on the array" do instances = [mock('instance')] instances.expects(:to_pson).returns "foo" @pson.render_multiple(instances).should == "foo" end it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the data into an instance" do text = "foo" PSON.expects(:parse).with("foo").returns("type" => "PsonTest", "data" => "foo") PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" @pson.intern(PsonTest, text).should == "parsed_pson" end it "should not render twice if 'PSON.parse' creates the appropriate instance" do text = "foo" instance = PsonTest.new("foo") PSON.expects(:parse).with("foo").returns(instance) PsonTest.expects(:from_pson).never @pson.intern(PsonTest, text).should equal(instance) end it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the actual into an instance if the pson has no class/data separation" do text = "foo" PSON.expects(:parse).with("foo").returns("foo") PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" @pson.intern(PsonTest, text).should == "parsed_pson" end it "should intern multiples by parsing the text and using 'class.intern' on each resulting data structure" do text = "foo" PSON.expects(:parse).with("foo").returns ["bar", "baz"] PsonTest.expects(:from_pson).with("bar").returns "BAR" PsonTest.expects(:from_pson).with("baz").returns "BAZ" @pson.intern_multiple(PsonTest, text).should == %w{BAR BAZ} end end end end diff --git a/spec/unit/network/handler/fileserver_spec.rb b/spec/unit/network/handler/fileserver_spec.rb old mode 100644 new mode 100755 index 014b82e69..52c4a71f5 --- a/spec/unit/network/handler/fileserver_spec.rb +++ b/spec/unit/network/handler/fileserver_spec.rb @@ -1,170 +1,169 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/handler/fileserver' describe Puppet::Network::Handler::FileServer do include PuppetSpec::Files def create_file(filename) File.open(filename, "w") { |f| f.puts filename} end def create_nested_file dirname = File.join(@basedir, "nested_dir") Dir.mkdir(dirname) file = File.join(dirname, "nested_dir_file") create_file(file) end before do @basedir = tmpdir("test_network_handler") @file = File.join(@basedir, "aFile") @link = File.join(@basedir, "aLink") create_file(@file) @mount = Puppet::Network::Handler::FileServer::Mount.new("some_path", @basedir) end it "should list a single directory" do @mount.list("/", false, false).should == [["/", "directory"]] end it "should list a file within a directory when given the file path" do @mount.list("/aFile", false, "false").should == [["/", "file"]] end it "should list a file within a directory when given the file path with recursion" do @mount.list("/aFile", true, "false").should == [["/", "file"]] end it "should return nil for a non-existent path" do @mount.list("/no_such_file", false, false).should be(nil) end it "should list a symbolic link as a file when given the link path" do File.symlink(@file, @link) @mount.list("/aLink", false, false).should == [["/", "file"]] end it "should return nil for a dangling symbolic link when given the link path" do File.symlink("/some/where", @link) @mount.list("/aLink", false, false).should be(nil) end it "should list directory contents of a flat directory structure when asked to recurse" do list = @mount.list("/", true, false) list.should include(["/aFile", "file"]) list.should include(["/", "directory"]) list.should have(2).items end it "should list the contents of a nested directory" do create_nested_file list = @mount.list("/", true, false) list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort end it "should list the contents of a directory ignoring files that match" do create_nested_file list = @mount.list("/", true, "*File") list.sort.should == [ ["/", "directory"] , ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort end it "should list the contents of a directory ignoring directories that match" do create_nested_file list = @mount.list("/", true, "*nested_dir") list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort end it "should list the contents of a directory ignoring all ignore patterns that match" do create_nested_file list = @mount.list("/", true, ["*File" , "*nested_dir"]) list.should == [ ["/", "directory"] ] end it "should list the directory when recursing to a depth of zero" do create_nested_file list = @mount.list("/", 0, false) list.should == [["/", "directory"]] end it "should list the base directory and files and nested directory to a depth of one" do create_nested_file list = @mount.list("/", 1, false) list.sort.should == [ ["/aFile", "file"], ["/nested_dir", "directory"], ["/", "directory"] ].sort end it "should list the base directory and files and nested directory to a depth of two" do create_nested_file list = @mount.list("/", 2, false) list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort end it "should list the base directory and files and nested directory to a depth greater than the directory structure" do create_nested_file list = @mount.list("/", 42, false) list.sort.should == [ ["/aFile", "file"], ["/", "directory"] , ["/nested_dir", "directory"], ["/nested_dir/nested_dir_file", "file"]].sort end it "should list a valid symbolic link as a file when recursing base dir" do File.symlink(@file, @link) list = @mount.list("/", true, false) list.sort.should == [ ["/", "directory"], ["/aFile", "file"], ["/aLink", "file"] ].sort end it "should not error when a dangling symlink is present" do File.symlink("/some/where", @link) lambda { @mount.list("/", true, false) }.should_not raise_error end it "should return the directory contents of valid entries when a dangling symlink is present" do File.symlink("/some/where", @link) list = @mount.list("/", true, false) list.sort.should == [ ["/aFile", "file"], ["/", "directory"] ].sort end describe Puppet::Network::Handler::FileServer::PluginMount do PLUGINS = Puppet::Network::Handler::FileServer::PLUGINS # create a module plugin hierarchy def create_plugin(mod, plugin) dirname = File.join(@basedir, mod) Dir.mkdir(dirname) plugins = File.join(dirname, PLUGINS) Dir.mkdir(plugins) facter = File.join(plugins, plugin) Dir.mkdir(facter) create_file(File.join(facter,"fact.rb")) end before :each do @modules = ["one","two"] @modules.each { |m| create_plugin(m, "facter") } Puppet::Node::Environment.new.stubs(:modulepath).returns @basedir @mount = Puppet::Network::Handler::FileServer::PluginMount.new(PLUGINS) @mount.allow("*") end it "should list a file within a directory when given the file path with recursion" do @mount.list("facter/fact.rb", true, "false").should == [["/", "file"], ["/", "file"]] end it "should return a merged view of all plugins for all modules" do list = @mount.list("facter",true,false) list.should == [["/", "directory"], ["/fact.rb", "file"], ["/", "directory"], ["/fact.rb", "file"]] end it "should not fail for inexistant plugins type" do @mount.list("puppet/parser",true,false) end end after do FileUtils.rm_rf(@basedir) end end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb old mode 100644 new mode 100755 index 257eec5de..bd95071c1 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -1,167 +1,192 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/http/api/v1' class V1RestApiTester include Puppet::Network::HTTP::API::V1 end describe Puppet::Network::HTTP::API::V1 do before do @tester = V1RestApiTester.new end it "should be able to convert a URI into a request" do @tester.should respond_to(:uri2indirection) end it "should be able to convert a request into a URI" do @tester.should respond_to(:indirection2uri) end describe "when converting a URI into a request" do before do @tester.stubs(:handler).returns "foo" end it "should require the http method, the URI, and the query parameters" do # Not a terribly useful test, but an important statement for the spec lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) end it "should use the first field of the URI as the environment" do @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should == "env" end it "should fail if the environment is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) end it "should use the environment from the URI even if one is specified in the parameters" do @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].should == "env" end it "should use the second field of the URI as the indirection name" do @tester.uri2indirection("GET", "/env/foo/bar", {})[0].should == "foo" end it "should fail if the indirection name is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) end it "should use the remainder of the URI as the indirection key" do @tester.uri2indirection("GET", "/env/foo/bar", {})[2].should == "bar" end it "should support the indirection key being a /-separated file path" do @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {})[2].should == "bee/baz/bomb" end it "should fail if no indirection key is specified" do lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do @tester.uri2indirection("GET", "/env/foo/bar", {})[1].should == :find end + it "should choose 'find' as the indirection method if the http method is a POST and the indirection name is singular" do + @tester.uri2indirection("POST", "/env/foo/bar", {})[1].should == :find + end + it "should choose 'head' as the indirection method if the http method is a HEAD and the indirection name is singular" do @tester.uri2indirection("HEAD", "/env/foo/bar", {})[1].should == :head end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do @tester.uri2indirection("GET", "/env/foos/bar", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is facts_search" do @tester.uri2indirection("GET", "/env/facts_search/bar", {})[1].should == :search end it "should change indirection name to 'facts' if the http method is a GET and the indirection name is facts_search" do @tester.uri2indirection("GET", "/env/facts_search/bar", {})[0].should == 'facts' end it "should not change indirection name from 'facts' if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {})[0].should == 'facts' end it "should change indirection name to 'status' if the http method is a GET and the indirection name is statuses" do @tester.uri2indirection("GET", "/env/statuses/bar", {})[0].should == 'status' end it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do @tester.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do @tester.uri2indirection("PUT", "/env/foo/bar", {})[1].should == :save end it "should fail if an indirection method cannot be picked" do lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) end it "should URI unescape the indirection key" do escaped = URI.escape("foo bar") indirection_name, method, key, params = @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}) key.should == "foo bar" end end describe "when converting a request into a URI" do before do @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", :foo => :bar, :environment => "myenv") end it "should use the environment as the first field of the URI" do @tester.indirection2uri(@request).split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do @tester.indirection2uri(@request).split("/")[2].should == "foo" end it "should pluralize the indirection name if the method is 'search'" do @request.stubs(:method).returns :search @tester.indirection2uri(@request).split("/")[2].should == "foos" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped end it "should add the query string to the URI" do @request.expects(:query_string).returns "?query" @tester.indirection2uri(@request).should =~ /\?query$/ end end + describe "when converting a request into a URI with body" do + before :each do + @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", :foo => :bar, :environment => "myenv") + end + + it "should use the environment as the first field of the URI" do + @tester.request_to_uri_and_body(@request).first.split("/")[1].should == "myenv" + end + + it "should use the indirection as the second field of the URI" do + @tester.request_to_uri_and_body(@request).first.split("/")[2].should == "foo" + end + + it "should use the escaped key as the remainder of the URI" do + escaped = URI.escape("with spaces") + @tester.request_to_uri_and_body(@request).first.split("/")[3].sub(/\?.+/, '').should == escaped + end + + it "should return the URI and body separately" do + @tester.request_to_uri_and_body(@request).should == ["/myenv/foo/with%20spaces", "foo=bar"] + end + end end diff --git a/spec/unit/network/http/compression_spec.rb b/spec/unit/network/http/compression_spec.rb index 85c62f358..5c919c6c5 100755 --- a/spec/unit/network/http/compression_spec.rb +++ b/spec/unit/network/http/compression_spec.rb @@ -1,197 +1,196 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "http compression" do describe "when zlib is not available" do before(:each) do Puppet.features.stubs(:zlib?).returns false require 'puppet/network/http/compression' class HttpUncompressor include Puppet::Network::HTTP::Compression::None end @uncompressor = HttpUncompressor.new end it "should have a module function that returns the None underlying module" do Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::None end it "should not add any Accept-Encoding header" do @uncompressor.add_accept_encoding({}).should == {} end it "should not tamper the body" do response = stub 'response', :body => "data" @uncompressor.uncompress_body(response).should == "data" end it "should yield an identity uncompressor" do response = stub 'response' @uncompressor.uncompress(response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) } end end describe "when zlib is available", :if => Puppet.features.zlib? do before(:each) do Puppet.features.stubs(:zlib?).returns true require 'puppet/network/http/compression' class HttpUncompressor include Puppet::Network::HTTP::Compression::Active end @uncompressor = HttpUncompressor.new end it "should have a module function that returns the Active underlying module" do Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::Active end it "should add an Accept-Encoding header when http compression is available" do Puppet.settings.expects(:[]).with(:http_compression).returns(true) headers = @uncompressor.add_accept_encoding({}) headers.should have_key('accept-encoding') headers['accept-encoding'].should =~ /gzip/ headers['accept-encoding'].should =~ /deflate/ headers['accept-encoding'].should =~ /identity/ end it "should not add Accept-Encoding header if http compression is not available" do Puppet.settings.stubs(:[]).with(:http_compression).returns(false) @uncompressor.add_accept_encoding({}).should == {} end describe "when uncompressing response body" do before do @response = stub 'response' @response.stubs(:[]).with('content-encoding') @response.stubs(:body).returns("mydata") end it "should return untransformed response body with no content-encoding" do @uncompressor.uncompress_body(@response).should == "mydata" end it "should return untransformed response body with 'identity' content-encoding" do @response.stubs(:[]).with('content-encoding').returns('identity') @uncompressor.uncompress_body(@response).should == "mydata" end it "should use a Zlib inflater with 'deflate' content-encoding" do @response.stubs(:[]).with('content-encoding').returns('deflate') inflater = stub 'inflater' Zlib::Inflate.expects(:new).returns(inflater) inflater.expects(:inflate).with("mydata").returns "uncompresseddata" @uncompressor.uncompress_body(@response).should == "uncompresseddata" end it "should use a GzipReader with 'gzip' content-encoding" do @response.stubs(:[]).with('content-encoding').returns('gzip') io = stub 'io' StringIO.expects(:new).with("mydata").returns io reader = stub 'gzip reader' Zlib::GzipReader.expects(:new).with(io).returns(reader) reader.expects(:read).returns "uncompresseddata" @uncompressor.uncompress_body(@response).should == "uncompresseddata" end end describe "when uncompressing by chunk" do before do @response = stub 'response' @response.stubs(:[]).with('content-encoding') @inflater = stub_everything 'inflater' Zlib::Inflate.stubs(:new).returns(@inflater) end it "should yield an identity uncompressor with no content-encoding" do @uncompressor.uncompress(@response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) } end it "should yield an identity uncompressor with 'identity' content-encoding" do @response.stubs(:[]).with('content-encoding').returns 'identity' @uncompressor.uncompress(@response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) } end %w{gzip deflate}.each do |c| it "should yield a Zlib uncompressor with '#{c}' content-encoding" do @response.stubs(:[]).with('content-encoding').returns c @uncompressor.uncompress(@response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::Active::ZlibAdapter) } end end it "should close the underlying adapter" do adapter = stub_everything 'adapter' Puppet::Network::HTTP::Compression::IdentityAdapter.expects(:new).returns(adapter) adapter.expects(:close) @uncompressor.uncompress(@response) { |u| } end end describe "zlib adapter" do before do @inflater = stub_everything 'inflater' Zlib::Inflate.stubs(:new).returns(@inflater) @adapter = Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new end it "should initialize the underlying inflater with gzip/zlib header parsing" do Zlib::Inflate.expects(:new).with(15+32) Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new end it "should inflate the given chunk" do @inflater.expects(:inflate).with("chunk") @adapter.uncompress("chunk") end it "should return the inflated chunk" do @inflater.stubs(:inflate).with("chunk").returns("uncompressed") @adapter.uncompress("chunk").should == "uncompressed" end it "should try a 'regular' inflater on Zlib::DataError" do @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) inflater = stub_everything 'inflater2' inflater.expects(:inflate).with("chunk").returns("uncompressed") Zlib::Inflate.expects(:new).with.returns(inflater) @adapter.uncompress("chunk") end it "should raise the error the second time" do @inflater.stubs(:inflate).raises(Zlib::DataError.new("not a zlib stream")) Zlib::Inflate.expects(:new).with.returns(@inflater) lambda { @adapter.uncompress("chunk") }.should raise_error end it "should finish the stream on close" do @inflater.expects(:finish) @adapter.close end it "should close the stream on close" do @inflater.expects(:close) @adapter.close end end end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 97d17fcf8..83969c504 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -1,472 +1,471 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/http/handler' require 'puppet/network/rest_authorization' class HttpHandled include Puppet::Network::HTTP::Handler end describe Puppet::Network::HTTP::Handler do before do @handler = HttpHandled.new end it "should include the v1 REST API" do Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1) end it "should include the Rest Authorization system" do Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::RestAuthorization) end it "should have a method for initializing" do @handler.should respond_to(:initialize_for_puppet) end describe "when initializing" do it "should fail when no server type has been provided" do lambda { @handler.initialize_for_puppet }.should raise_error(ArgumentError) end it "should set server type" do @handler.initialize_for_puppet("foo") @handler.server.should == "foo" end end it "should be able to process requests" do @handler.should respond_to(:process) end describe "when processing a request" do before do @request = stub('http request') @request.stubs(:[]).returns "foo" @response = stub('http response') @model_class = stub('indirected model class') @indirection = stub('indirection') @model_class.stubs(:indirection).returns(@indirection) @result = stub 'result', :render => "mytext" @handler.stubs(:check_authorization) stub_server_interface end # Stub out the interface we require our including classes to # implement. def stub_server_interface @handler.stubs(:accept_header ).returns "format_one,format_two" @handler.stubs(:content_type_header).returns "text/yaml" @handler.stubs(:set_content_type ).returns "my_result" @handler.stubs(:set_response ).returns "my_result" @handler.stubs(:path ).returns "/my_handler/my_result" @handler.stubs(:http_method ).returns("GET") @handler.stubs(:params ).returns({}) @handler.stubs(:content_type ).returns("text/plain") end it "should create an indirection request from the path, parameters, and http method" do @handler.expects(:path).with(@request).returns "mypath" @handler.expects(:http_method).with(@request).returns "mymethod" @handler.expects(:params).with(@request).returns "myparams" @handler.expects(:uri2indirection).with("mymethod", "mypath", "myparams").returns stub("request", :method => :find) @handler.stubs(:do_find) @handler.process(@request, @response) end it "should call the 'do' method and delegate authorization to the RestAuthorization layer" do @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) @handler.expects(:do_mymethod).with("facts", "key", {:node => "name"}, @request, @response) @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}) @handler.process(@request, @response) end it "should return 403 if the request is not authorized" do @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) @handler.expects(:do_mymethod).never @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden")) @handler.expects(:set_response).with { |response, body, status| status == 403 } @handler.process(@request, @response) end it "should serialize a controller exception when an exception is thrown while finding the model instance" do @handler.expects(:uri2indirection).returns(["facts", :find, "key", {:node => "name"}]) @handler.expects(:do_find).raises(ArgumentError, "The exception") @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 } @handler.process(@request, @response) end it "should set the format to text/plain when serializing an exception" do @handler.expects(:set_content_type).with(@response, "text/plain") @handler.do_exception(@response, "A test", 404) end it "should raise an error if the request is formatted in an unknown format" do @handler.stubs(:content_type_header).returns "unknown format" lambda { @handler.request_format(@request) }.should raise_error end it "should still find the correct format if content type contains charset information" do @handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8" @handler.request_format(@request).should == "s" end describe "when finding a model instance" do before do @indirection.stubs(:find).returns @result Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat end it "should use the indirection request to find the model class" do @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key" do @indirection.expects(:find).with do |key, args| key == "my_result" end.returns @result @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do @indirection.expects(:find).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result @handler.do_find("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should set the content type to the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should fail if no accept header is provided" do @handler.expects(:accept_header).with(@request).returns nil lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should fail if the accept header does not contain a valid format" do @handler.expects(:accept_header).with(@request).returns "" lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(RuntimeError) end it "should not use an unsuitable format" do @handler.expects(:accept_header).with(@request).returns "foo,bar" foo = mock 'foo', :suitable? => false bar = mock 'bar', :suitable? => true Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar @handler.expects(:set_content_type).with(@response, bar) # the suitable one @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should render the result using the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @result.expects(:render).with(@oneformat) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should pass the result through without rendering it if the result is a string" do @indirection.stubs(:find).returns "foo" @handler.expects(:set_response).with(@response, "foo") @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the default status when a model find call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a serialized object when a model find call succeeds" do @model_instance = stub('model instance') @model_instance.expects(:render).returns "my_rendered_object" @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" } @indirection.stubs(:find).returns(@model_instance) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when no model instance can be found" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @indirection.stubs(:find).returns(nil) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should write a log message when no model instance can be found" do @model_class.stubs(:name).returns "my name" @indirection.stubs(:find).returns(nil) Puppet.expects(:info).with("Could not find my_handler for 'my_result'") @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should serialize the result in with the appropriate format" do @model_instance = stub('model instance') @handler.expects(:format_to_use).returns(@oneformat) @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object" @indirection.stubs(:find).returns(@model_instance) @handler.do_find("my_handler", "my_result", {}, @request, @response) end end describe "when performing head operation" do before do @irequest = stub 'indirection_request', :method => :head, :indirection_name => "my_handler", :to_hash => {}, :key => "my_result", :model => @model_class @model_class.stubs(:head).returns true end it "should use the indirection request to find the model class" do @irequest.expects(:model).returns @model_class @handler.do_head(@irequest, @request, @response) end it "should use the escaped request key" do @model_class.expects(:head).with do |key, args| key == "my_result" end.returns true @handler.do_head(@irequest, @request, @response) end it "should not generate a response when a model head call succeeds" do @handler.expects(:set_response).never @handler.do_head(@irequest, @request, @response) end it "should return a 404 when the model head call returns false" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @model_class.stubs(:head).returns(false) @handler.do_head(@irequest, @request, @response) end end describe "when searching for model instances" do before do Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result1 = mock 'result1' @result2 = mock 'results' @result = [@result1, @result2] @model_class.stubs(:render_multiple).returns "my rendered instances" @indirection.stubs(:search).returns(@result) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat end it "should use the indirection request to find the model" do @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do @indirection.expects(:search).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result @handler.do_search("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status when a model search call succeeds" do @indirection.stubs(:search).returns(@result) @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should set the content type to the first format returned by the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a list of serialized objects when a model search call succeeds" do @handler.expects(:accept_header).with(@request).returns "one,two" @indirection.stubs(:search).returns(@result) @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances" @handler.expects(:set_response).with { |response, data| data == "my rendered instances" } @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return [] when searching returns an empty array" do @handler.expects(:accept_header).with(@request).returns "one,two" @indirection.stubs(:search).returns([]) @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]" @handler.expects(:set_response).with { |response, data| data == "[]" } @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when searching returns nil" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @indirection.stubs(:search).returns(nil) @handler.do_search("my_handler", "my_result", {}, @request, @response) end end describe "when destroying a model instance" do before do Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result = stub 'result', :render => "the result" @indirection.stubs(:destroy).returns @result end it "should use the indirection request to find the model" do @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key to destroy the instance in the model" do @indirection.expects(:destroy).with do |key, args| key == "foo bar" end @handler.do_destroy("my_handler", "foo bar", {}, @request, @response) end it "should use a common method for determining the request parameters" do @indirection.expects(:destroy).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end @handler.do_destroy("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status code a model destroy call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should return a yaml-encoded result when a model destroy call succeeds" do @result = stub 'result', :to_yaml => "the result" @indirection.expects(:destroy).returns(@result) @handler.expects(:set_response).with { |response, body, status| body == "the result" } @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end end describe "when saving a model instance" do before do Puppet::Indirector::Indirection.stubs(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @handler.stubs(:body).returns('my stuff') @handler.stubs(:content_type_header).returns("text/yaml") @result = stub 'result', :render => "the result" @model_instance = stub('indirected model instance') @model_class.stubs(:convert_from).returns(@model_instance) @indirection.stubs(:save) @format = stub 'format', :suitable? => true, :name => "format", :mime => "text/format" Puppet::Network::FormatHandler.stubs(:format).returns @format @yamlformat = stub 'yaml', :suitable? => true, :name => "yaml", :mime => "text/yaml" Puppet::Network::FormatHandler.stubs(:format).with("yaml").returns @yamlformat end it "should use the indirection request to find the model" do @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the 'body' hook to retrieve the body of the request" do @handler.expects(:body).returns "my body" @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should fail to save model if data is not specified" do @handler.stubs(:body).returns('') lambda { @handler.do_save("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should use a common method for determining the request parameters" do @indirection.expects(:save).with(@model_instance, 'key').once @handler.do_save("my_handler", "key", {}, @request, @response) end it "should use the default status when a model save call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should return the yaml-serialized result when a model save call succeeds" do @indirection.stubs(:save).returns(@model_instance) @model_instance.expects(:to_yaml).returns('foo') @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should set the content to yaml" do @handler.expects(:set_content_type).with(@response, @yamlformat) @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the content-type header to know the body format" do @handler.expects(:content_type_header).returns("text/format") Puppet::Network::FormatHandler.stubs(:mime).with("text/format").returns @format @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance @handler.do_save("my_handler", "my_result", {}, @request, @response) end end end describe "when resolving node" do it "should use a look-up from the ip address" do Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com") @handler.resolve_node(:ip => "1.2.3.4") end it "should return the look-up result" do Resolv.stubs(:getname).with("1.2.3.4").returns("host.domain.com") @handler.resolve_node(:ip => "1.2.3.4").should == "host.domain.com" end it "should return the ip address if resolving fails" do Resolv.stubs(:getname).with("1.2.3.4").raises(RuntimeError, "no such host") @handler.resolve_node(:ip => "1.2.3.4").should == "1.2.3.4" end end end diff --git a/spec/unit/network/http/mongrel/rest_spec.rb b/spec/unit/network/http/mongrel/rest_spec.rb index 086fec522..3e454cf8f 100755 --- a/spec/unit/network/http/mongrel/rest_spec.rb +++ b/spec/unit/network/http/mongrel/rest_spec.rb @@ -1,248 +1,247 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/http' describe "Puppet::Network::HTTP::MongrelREST", :if => Puppet.features.mongrel? do before do require 'puppet/network/http/mongrel/rest' end it "should include the Puppet::Network::HTTP::Handler module" do Puppet::Network::HTTP::MongrelREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end describe "when initializing" do it "should call the Handler's initialization hook with its provided arguments as the server and handler" do Puppet::Network::HTTP::MongrelREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") Puppet::Network::HTTP::MongrelREST.new(:server => "my", :handler => "arguments") end end describe "when receiving a request" do before do @params = {} @request = stub('mongrel http request', :params => @params) @head = stub('response head') @body = stub('response body', :write => true) @response = stub('mongrel http response') @response.stubs(:start).yields(@head, @body) @model_class = stub('indirected model class') @mongrel = stub('mongrel http server', :register => true) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mongrel, :handler => :foo) end describe "and using the HTTP Handler interface" do it "should return the HTTP_ACCEPT parameter as the accept header" do @params.expects(:[]).with("HTTP_ACCEPT").returns "myaccept" @handler.accept_header(@request).should == "myaccept" end it "should return the Content-Type parameter as the Content-Type header" do @params.expects(:[]).with("HTTP_CONTENT_TYPE").returns "mycontent" @handler.content_type_header(@request).should == "mycontent" end it "should use the REQUEST_METHOD as the http method" do @params.expects(:[]).with(Mongrel::Const::REQUEST_METHOD).returns "mymethod" @handler.http_method(@request).should == "mymethod" end it "should return the request path as the path" do @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar" @handler.path(@request).should == "/foo/bar" end it "should return the request body as the body" do @request.expects(:body).returns StringIO.new("mybody") @handler.body(@request).should == "mybody" end it "should set the response's content-type header when setting the content type" do @header = mock 'header' @response.expects(:header).returns @header @header.expects(:[]=).with('Content-Type', "mytype") @handler.set_content_type(@response, "mytype") end it "should set the status and write the body when setting the response for a successful request" do head = mock 'head' body = mock 'body' @response.expects(:start).with(200).yields(head, body) body.expects(:write).with("mybody") @handler.set_response(@response, "mybody", 200) end describe "when the result is a File" do it "should use response send_file" do head = mock 'head' body = mock 'body' stat = stub 'stat', :size => 100 file = stub 'file', :stat => stat, :path => "/tmp/path" file.stubs(:is_a?).with(File).returns(true) @response.expects(:start).with(200).yields(head, body) @response.expects(:send_status).with(100) @response.expects(:send_header) @response.expects(:send_file).with("/tmp/path") @handler.set_response(@response, file, 200) end end it "should set the status and reason and write the body when setting the response for a successful request" do head = mock 'head' body = mock 'body' @response.expects(:start).with(400, false, "mybody").yields(head, body) body.expects(:write).with("mybody") @handler.set_response(@response, "mybody", 400) end end describe "and determining the request parameters" do before do @request.stubs(:params).returns({}) end it "should skip empty parameter values" do @request.expects(:params).returns('QUERY_STRING' => "&=") lambda { @handler.params(@request) }.should_not raise_error end it "should include the HTTP request parameters, with the keys as symbols" do @request.expects(:params).returns('QUERY_STRING' => 'foo=baz&bar=xyzzy') result = @handler.params(@request) result[:foo].should == "baz" result[:bar].should == "xyzzy" end it "should CGI-decode the HTTP parameters" do encoding = CGI.escape("foo bar") @request.expects(:params).returns('QUERY_STRING' => "foo=#{encoding}") result = @handler.params(@request) result[:foo].should == "foo bar" end it "should convert the string 'true' to the boolean" do @request.expects(:params).returns('QUERY_STRING' => 'foo=true') result = @handler.params(@request) result[:foo].should be_true end it "should convert the string 'false' to the boolean" do @request.expects(:params).returns('QUERY_STRING' => 'foo=false') result = @handler.params(@request) result[:foo].should be_false end it "should convert integer arguments to Integers" do @request.expects(:params).returns('QUERY_STRING' => 'foo=15') result = @handler.params(@request) result[:foo].should == 15 end it "should convert floating point arguments to Floats" do @request.expects(:params).returns('QUERY_STRING' => 'foo=1.5') result = @handler.params(@request) result[:foo].should == 1.5 end it "should YAML-load and URI-decode values that are YAML-encoded" do escaping = CGI.escape(YAML.dump(%w{one two})) @request.expects(:params).returns('QUERY_STRING' => "foo=#{escaping}") result = @handler.params(@request) result[:foo].should == %w{one two} end it "should not allow the client to set the node via the query string" do @request.stubs(:params).returns('QUERY_STRING' => "node=foo") @handler.params(@request)[:node].should be_nil end it "should not allow the client to set the IP address via the query string" do @request.stubs(:params).returns('QUERY_STRING' => "ip=foo") @handler.params(@request)[:ip].should be_nil end it "should pass the client's ip address to model find" do @request.stubs(:params).returns("REMOTE_ADDR" => "ipaddress") @handler.params(@request)[:ip].should == "ipaddress" end it "should pass the client's provided X-Forwared-For value as the ip" do @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipaddress") @handler.params(@request)[:ip].should == "ipaddress" end it "should pass the client's provided X-Forwared-For first value as the ip" do @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipproxy1,ipproxy2,ipaddress") @handler.params(@request)[:ip].should == "ipaddress" end it "should pass the client's provided X-Forwared-For value as the ip instead of the REMOTE_ADDR" do @request.stubs(:params).returns("REMOTE_ADDR" => "remote_addr") @request.stubs(:params).returns("HTTP_X_FORWARDED_FOR" => "ipaddress") @handler.params(@request)[:ip].should == "ipaddress" end it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" @request.stubs(:params).returns("myheader" => "/CN=host.domain.com") @handler.params(@request) end it "should retrieve the hostname by matching the certificate parameter" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" @request.stubs(:params).returns("myheader" => "/CN=host.domain.com") @handler.params(@request)[:node].should == "host.domain.com" end it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") @handler.params(@request) end it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" @request.stubs(:params).returns("myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") @handler.params(@request)[:authenticated].should be_true end it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" @request.stubs(:params).returns("myheader" => "whatever", "certheader" => "/CN=host.domain.com") @handler.params(@request)[:authenticated].should be_false end it "should consider the host unauthenticated if no certificate information is present" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" @request.stubs(:params).returns("myheader" => nil, "certheader" => "SUCCESS") @handler.params(@request)[:authenticated].should be_false end it "should resolve the node name with an ip address look-up if no certificate is present" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" @request.stubs(:params).returns("myheader" => nil) @handler.expects(:resolve_node).returns("host.domain.com") @handler.params(@request)[:node].should == "host.domain.com" end end end end diff --git a/spec/unit/network/http/mongrel_spec.rb b/spec/unit/network/http/mongrel_spec.rb index 5a67a3fae..56d0afbed 100755 --- a/spec/unit/network/http/mongrel_spec.rb +++ b/spec/unit/network/http/mongrel_spec.rb @@ -1,125 +1,125 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Rick Bradley on 2007-10-15. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/network/http' describe "Puppet::Network::HTTP::Mongrel", "after initializing", :if => Puppet.features.mongrel? do it "should not be listening" do require 'puppet/network/http/mongrel' Puppet::Network::HTTP::Mongrel.new.should_not be_listening end end describe "Puppet::Network::HTTP::Mongrel", "when turning on listening", :if => Puppet.features.mongrel? do before do require 'puppet/network/http/mongrel' @server = Puppet::Network::HTTP::Mongrel.new @mock_mongrel = mock('mongrel') @mock_mongrel.stubs(:run) @mock_mongrel.stubs(:register) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @mock_puppet_mongrel = mock('puppet_mongrel') Puppet::Network::HTTPServer::Mongrel.stubs(:new).returns(@mock_puppet_mongrel) @listen_params = { :address => "127.0.0.1", :port => 31337, :protocols => [ :rest, :xmlrpc ], :xmlrpc_handlers => [ :status, :fileserver ] } end it "should fail if already listening" do @server.listen(@listen_params) Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) end it "should require at least one protocol" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) end it "should require a listening address to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) end it "should require a listening port to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) end it "should order a mongrel server to start" do @mock_mongrel.expects(:run) @server.listen(@listen_params) end it "should tell mongrel to listen on the specified address and port" do Mongrel::HttpServer.expects(:new).with("127.0.0.1", 31337).returns(@mock_mongrel) @server.listen(@listen_params) end it "should be listening" do Mongrel::HttpServer.expects(:new).returns(@mock_mongrel) @server.listen(@listen_params) @server.should be_listening end describe "when providing REST services" do it "should instantiate a handler at / for handling REST calls" do Puppet::Network::HTTP::MongrelREST.expects(:new).returns "myhandler" @mock_mongrel.expects(:register).with("/", "myhandler") @server.listen(@listen_params) end it "should use a Mongrel + REST class to configure Mongrel when REST services are requested" do @server.expects(:class_for_protocol).with(:rest).at_least_once.returns(Puppet::Network::HTTP::MongrelREST) @server.listen(@listen_params) end end describe "when providing XMLRPC services" do it "should do nothing if no xmlrpc handlers have been provided" do Puppet::Network::HTTPServer::Mongrel.expects(:new).never @server.listen(@listen_params.merge(:xmlrpc_handlers => [])) end it "should create an instance of the existing Mongrel http server with the right handlers" do Puppet::Network::HTTPServer::Mongrel.expects(:new).with([:status, :master]).returns(@mock_puppet_mongrel) @server.listen(@listen_params.merge(:xmlrpc_handlers => [:status, :master])) end it "should register the Mongrel server instance at /RPC2" do @mock_mongrel.expects(:register).with("/RPC2", @mock_puppet_mongrel) @server.listen(@listen_params.merge(:xmlrpc_handlers => [:status, :master])) end end end describe "Puppet::Network::HTTP::Mongrel", "when turning off listening", :if => Puppet.features.mongrel? do before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) @mock_mongrel.stubs(:register) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @server = Puppet::Network::HTTP::Mongrel.new @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } end it "should fail unless listening" do Proc.new { @server.unlisten }.should raise_error(RuntimeError) end it "should order mongrel server to stop" do @server.listen(@listen_params) @mock_mongrel.expects(:stop) @server.unlisten end it "should not be listening" do @server.listen(@listen_params) @mock_mongrel.stubs(:stop) @server.unlisten @server.should_not be_listening end end diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb index dd49fa057..8a5666f56 100755 --- a/spec/unit/network/http/rack/rest_spec.rb +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -1,247 +1,246 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/http/rack' if Puppet.features.rack? require 'puppet/network/http/rack/rest' describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do it "should include the Puppet::Network::HTTP::Handler module" do Puppet::Network::HTTP::RackREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end describe "when initializing" do it "should call the Handler's initialization hook with its provided arguments" do Puppet::Network::HTTP::RackREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") Puppet::Network::HTTP::RackREST.new(:server => "my", :handler => "arguments") end end describe "when serving a request" do before :all do @model_class = stub('indirected model class') Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @handler = Puppet::Network::HTTP::RackREST.new(:handler => :foo) end before :each do @response = Rack::Response.new end def mk_req(uri, opts = {}) env = Rack::MockRequest.env_for(uri, opts) Rack::Request.new(env) end describe "and using the HTTP Handler interface" do it "should return the HTTP_ACCEPT parameter as the accept header" do req = mk_req('/', 'HTTP_ACCEPT' => 'myaccept') @handler.accept_header(req).should == "myaccept" end it "should return the CONTENT_TYPE parameter as the content type header" do req = mk_req('/', 'CONTENT_TYPE' => 'mycontent') @handler.content_type_header(req).should == "mycontent" end it "should use the REQUEST_METHOD as the http method" do req = mk_req('/', :method => 'MYMETHOD') @handler.http_method(req).should == "MYMETHOD" end it "should return the request path as the path" do req = mk_req('/foo/bar') @handler.path(req).should == "/foo/bar" end it "should return the request body as the body" do req = mk_req('/foo/bar', :input => 'mybody') @handler.body(req).should == "mybody" end it "should set the response's content-type header when setting the content type" do @header = mock 'header' @response.expects(:header).returns @header @header.expects(:[]=).with('Content-Type', "mytype") @handler.set_content_type(@response, "mytype") end it "should set the status and write the body when setting the response for a request" do @response.expects(:status=).with(400) @response.expects(:write).with("mybody") @handler.set_response(@response, "mybody", 400) end describe "when result is a File" do before :each do stat = stub 'stat', :size => 100 @file = stub 'file', :stat => stat, :path => "/tmp/path" @file.stubs(:is_a?).with(File).returns(true) end it "should set the Content-Length header as a string" do @response.expects(:[]=).with("Content-Length", '100') @handler.set_response(@response, @file, 200) end it "should return a RackFile adapter as body" do @response.expects(:body=).with { |val| val.is_a?(Puppet::Network::HTTP::RackREST::RackFile) } @handler.set_response(@response, @file, 200) end end end describe "and determining the request parameters" do it "should include the HTTP request parameters, with the keys as symbols" do req = mk_req('/?foo=baz&bar=xyzzy') result = @handler.params(req) result[:foo].should == "baz" result[:bar].should == "xyzzy" end it "should CGI-decode the HTTP parameters" do encoding = CGI.escape("foo bar") req = mk_req("/?foo=#{encoding}") result = @handler.params(req) result[:foo].should == "foo bar" end it "should convert the string 'true' to the boolean" do req = mk_req("/?foo=true") result = @handler.params(req) result[:foo].should be_true end it "should convert the string 'false' to the boolean" do req = mk_req("/?foo=false") result = @handler.params(req) result[:foo].should be_false end it "should convert integer arguments to Integers" do req = mk_req("/?foo=15") result = @handler.params(req) result[:foo].should == 15 end it "should convert floating point arguments to Floats" do req = mk_req("/?foo=1.5") result = @handler.params(req) result[:foo].should == 1.5 end it "should YAML-load and CGI-decode values that are YAML-encoded" do escaping = CGI.escape(YAML.dump(%w{one two})) req = mk_req("/?foo=#{escaping}") result = @handler.params(req) result[:foo].should == %w{one two} end it "should not allow the client to set the node via the query string" do req = mk_req("/?node=foo") @handler.params(req)[:node].should be_nil end it "should not allow the client to set the IP address via the query string" do req = mk_req("/?ip=foo") @handler.params(req)[:ip].should be_nil end it "should pass the client's ip address to model find" do req = mk_req("/", 'REMOTE_ADDR' => 'ipaddress') @handler.params(req)[:ip].should == "ipaddress" end it "should set 'authenticated' to false if no certificate is present" do req = mk_req('/') @handler.params(req)[:authenticated].should be_false end end describe "with pre-validated certificates" do it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req('/', "myheader" => "/CN=host.domain.com") @handler.params(req) end it "should retrieve the hostname by matching the certificate parameter" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req('/', "myheader" => "/CN=host.domain.com") @handler.params(req)[:node].should == "host.domain.com" end it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") @handler.params(req) end it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") @handler.params(req)[:authenticated].should be_true end it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => "whatever", "certheader" => "/CN=host.domain.com") @handler.params(req)[:authenticated].should be_false end it "should consider the host unauthenticated if no certificate information is present" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => nil, "certheader" => "/CN=host.domain.com") @handler.params(req)[:authenticated].should be_false end it "should resolve the node name with an ip address look-up if no certificate is present" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req('/', "myheader" => nil) @handler.expects(:resolve_node).returns("host.domain.com") @handler.params(req)[:node].should == "host.domain.com" end end end end describe Puppet::Network::HTTP::RackREST::RackFile do before(:each) do stat = stub 'stat', :size => 100 @file = stub 'file', :stat => stat, :path => "/tmp/path" @rackfile = Puppet::Network::HTTP::RackREST::RackFile.new(@file) end it "should have an each method" do @rackfile.should be_respond_to(:each) end it "should yield file chunks by chunks" do @file.expects(:read).times(3).with(8192).returns("1", "2", nil) i = 1 @rackfile.each do |chunk| chunk.to_i.should == i i += 1 end end it "should have a close method" do @rackfile.should be_respond_to(:close) end it "should delegate close to File close" do @file.expects(:close) @rackfile.close end end diff --git a/spec/unit/network/http/rack/xmlrpc_spec.rb b/spec/unit/network/http/rack/xmlrpc_spec.rb index e8ae47bf7..9173438a6 100755 --- a/spec/unit/network/http/rack/xmlrpc_spec.rb +++ b/spec/unit/network/http/rack/xmlrpc_spec.rb @@ -1,156 +1,155 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/handler' require 'puppet/network/http/rack' if Puppet.features.rack? require 'puppet/network/http/rack/xmlrpc' if Puppet.features.rack? describe "Puppet::Network::HTTP::RackXMLRPC", :if => Puppet.features.rack? do describe "when initializing" do it "should create an Puppet::Network::XMLRPCServer" do Puppet::Network::XMLRPCServer.expects(:new).returns stub_everything Puppet::Network::HTTP::RackXMLRPC.new([]) end it "should create each handler" do handler = stub_everything 'handler' Puppet::Network::XMLRPCServer.any_instance.stubs(:add_handler) Puppet::Network::Handler.expects(:handler).returns(handler).times(2) Puppet::Network::HTTP::RackXMLRPC.new([:foo, :bar]) end it "should add each handler to the XMLRPCserver" do handler = stub_everything 'handler' Puppet::Network::Handler.stubs(:handler).returns(handler) Puppet::Network::XMLRPCServer.any_instance.expects(:add_handler).times(2) Puppet::Network::HTTP::RackXMLRPC.new([:foo, :bar]) end end describe "when serving a request" do before :each do foo_handler = stub_everything 'foo_handler' Puppet::Network::Handler.stubs(:handler).with(:foo).returns foo_handler Puppet::Network::XMLRPCServer.any_instance.stubs(:add_handler) Puppet::Network::XMLRPCServer.any_instance.stubs(:process).returns('') @handler = Puppet::Network::HTTP::RackXMLRPC.new([:foo]) end before :each do @response = Rack::Response.new end def mk_req(opts = {}) opts[:method] = 'POST' if !opts[:method] opts['CONTENT_TYPE'] = 'text/xml; foo=bar' if !opts['CONTENT_TYPE'] env = Rack::MockRequest.env_for('/RPC2', opts) Rack::Request.new(env) end it "should reject non-POST requests" do req = mk_req :method => 'PUT' @handler.process(req, @response) @response.status.should == 405 end it "should reject non text/xml requests" do req = mk_req 'CONTENT_TYPE' => 'yadda/plain' end it "should create a ClientRequest" do cr = Puppet::Network::ClientRequest.new(nil, '127.0.0.1', false) Puppet::Network::ClientRequest.expects(:new).returns cr req = mk_req @handler.process(req, @response) end it "should let xmlrpcserver process the request" do Puppet::Network::XMLRPCServer.any_instance.expects(:process).returns('yay') req = mk_req @handler.process(req, @response) end it "should report the response as OK" do req = mk_req @handler.process(req, @response) @response.status.should == 200 end it "should report the response with the correct content type" do req = mk_req @handler.process(req, @response) @response['Content-Type'].should == 'text/xml; charset=utf-8' end it "should set 'authenticated' to false if no certificate is present" do req = mk_req Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| authenticated == false } @handler.process(req, @response) end it "should use the client's ip address" do req = mk_req 'REMOTE_ADDR' => 'ipaddress' Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| ip == 'ipaddress' } @handler.process(req, @response) end describe "with pre-validated certificates" do it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req "myheader" => "/CN=host.domain.com" @handler.process(req, @response) end it "should retrieve the hostname by matching the certificate parameter" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| node == "host.domain.com" } req = mk_req "myheader" => "/CN=host.domain.com" @handler.process(req, @response) end it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" @handler.process(req, @response) end it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| authenticated == true } req = mk_req "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" @handler.process(req, @response) end it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| authenticated == false } req = mk_req "myheader" => "whatever", "certheader" => "/CN=host.domain.com" @handler.process(req, @response) end it "should consider the host unauthenticated if no certificate information is present" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| authenticated == false } req = mk_req "myheader" => nil, "certheader" => "/CN=host.domain.com" @handler.process(req, @response) end it "should resolve the node name with an ip address look-up if no certificate is present" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" Resolv.any_instance.expects(:getname).returns("host.domain.com") Puppet::Network::ClientRequest.expects(:new).with { |node,ip,authenticated| node == "host.domain.com" } req = mk_req "myheader" => nil @handler.process(req, @response) end end end end diff --git a/spec/unit/network/http/rack_spec.rb b/spec/unit/network/http/rack_spec.rb index 75d3fdb90..9e1ee3d1e 100755 --- a/spec/unit/network/http/rack_spec.rb +++ b/spec/unit/network/http/rack_spec.rb @@ -1,101 +1,100 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/handler' require 'puppet/network/http/rack' if Puppet.features.rack? describe "Puppet::Network::HTTP::Rack", :if => Puppet.features.rack? do describe "while initializing" do it "should require a protocol specification" do Proc.new { Puppet::Network::HTTP::Rack.new({}) }.should raise_error(ArgumentError) end it "should not accept imaginary protocols" do Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:foo]}) }.should raise_error(ArgumentError) end it "should accept the REST protocol" do Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) }.should_not raise_error(ArgumentError) end it "should create a RackREST instance" do Puppet::Network::HTTP::RackREST.expects(:new) Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) end describe "with XMLRPC enabled" do it "should require XMLRPC handlers" do Proc.new { Puppet::Network::HTTP::Rack.new({:protocols => [:xmlrpc]}) }.should raise_error(ArgumentError) end it "should create a RackXMLRPC instance" do Puppet::Network::HTTP::RackXMLRPC.expects(:new) Puppet::Network::HTTP::Rack.new({:protocols => [:xmlrpc], :xmlrpc_handlers => [:Status]}) end end end describe "when called" do before :all do @app = Puppet::Network::HTTP::Rack.new({:protocols => [:rest]}) # let's use Rack::Lint to verify that we're OK with the rack specification @linted = Rack::Lint.new(@app) end before :each do @env = Rack::MockRequest.env_for('/') end it "should create a Request object" do request = Rack::Request.new(@env) Rack::Request.expects(:new).returns request @linted.call(@env) end it "should create a Response object" do Rack::Response.expects(:new).returns stub_everything @app.call(@env) # can't lint when Rack::Response is a stub end it "should let RackREST process the request" do Puppet::Network::HTTP::RackREST.any_instance.expects(:process).once @linted.call(@env) end it "should catch unhandled exceptions from RackREST" do Puppet::Network::HTTP::RackREST.any_instance.expects(:process).raises(ArgumentError, 'test error') Proc.new { @linted.call(@env) }.should_not raise_error end it "should finish() the Response" do Rack::Response.any_instance.expects(:finish).once @app.call(@env) # can't lint when finish is a stub end end describe "when serving XMLRPC" do before :all do @app = Puppet::Network::HTTP::Rack.new({:protocols => [:rest, :xmlrpc], :xmlrpc_handlers => [:Status]}) @linted = Rack::Lint.new(@app) end before :each do @env = Rack::MockRequest.env_for('/RPC2', :method => 'POST') end it "should use RackXMLRPC to serve /RPC2 requests" do Puppet::Network::HTTP::RackXMLRPC.any_instance.expects(:process).once @linted.call(@env) end end end diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index b1491e32e..267ddcc72 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -1,181 +1,180 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/http' require 'webrick' require 'puppet/network/http/webrick/rest' describe Puppet::Network::HTTP::WEBrickREST do it "should include the Puppet::Network::HTTP::Handler module" do Puppet::Network::HTTP::WEBrickREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end describe "when initializing" do it "should call the Handler's initialization hook with its provided arguments as the server and handler" do Puppet::Network::HTTP::WEBrickREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") Puppet::Network::HTTP::WEBrickREST.new("my", "arguments") end end describe "when receiving a request" do before do @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil) @response = stub('webrick http response', :status= => true, :body= => true) @model_class = stub('indirected model class') @webrick = stub('webrick http server', :mount => true, :[] => {}) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick, :foo) end it "should delegate its :service method to its :process method" do @handler.expects(:process).with(@request, @response).returns "stuff" @handler.service(@request, @response).should == "stuff" end describe "when using the Handler interface" do it "should use the 'accept' request parameter as the Accept header" do @request.expects(:[]).with("accept").returns "foobar" @handler.accept_header(@request).should == "foobar" end it "should use the 'content-type' request header as the Content-Type header" do @request.expects(:[]).with("content-type").returns "foobar" @handler.content_type_header(@request).should == "foobar" end it "should use the request method as the http method" do @request.expects(:request_method).returns "FOO" @handler.http_method(@request).should == "FOO" end it "should return the request path as the path" do @request.expects(:path).returns "/foo/bar" @handler.path(@request).should == "/foo/bar" end it "should return the request body as the body" do @request.expects(:body).returns "my body" @handler.body(@request).should == "my body" end it "should set the response's 'content-type' header when setting the content type" do @response.expects(:[]=).with("content-type", "text/html") @handler.set_content_type(@response, "text/html") end it "should set the status and body on the response when setting the response for a successful query" do @response.expects(:status=).with 200 @response.expects(:body=).with "mybody" @handler.set_response(@response, "mybody", 200) end describe "when the result is a File" do before(:each) do stat = stub 'stat', :size => 100 @file = stub 'file', :stat => stat, :path => "/tmp/path" @file.stubs(:is_a?).with(File).returns(true) end it "should serve it" do @response.stubs(:[]=) @response.expects(:status=).with 200 @response.expects(:body=).with @file @handler.set_response(@response, @file, 200) end it "should set the Content-Length header" do @response.expects(:[]=).with('content-length', 100) @handler.set_response(@response, @file, 200) end end it "should set the status and message on the response when setting the response for a failed query" do @response.expects(:status=).with 400 @response.expects(:reason_phrase=).with "mybody" @handler.set_response(@response, "mybody", 400) end end describe "and determining the request parameters" do it "should include the HTTP request parameters, with the keys as symbols" do @request.stubs(:query).returns("foo" => "baz", "bar" => "xyzzy") result = @handler.params(@request) result[:foo].should == "baz" result[:bar].should == "xyzzy" end it "should CGI-decode the HTTP parameters" do encoding = CGI.escape("foo bar") @request.expects(:query).returns('foo' => encoding) result = @handler.params(@request) result[:foo].should == "foo bar" end it "should convert the string 'true' to the boolean" do @request.expects(:query).returns('foo' => "true") result = @handler.params(@request) result[:foo].should be_true end it "should convert the string 'false' to the boolean" do @request.expects(:query).returns('foo' => "false") result = @handler.params(@request) result[:foo].should be_false end it "should YAML-load and CGI-decode values that are YAML-encoded" do escaping = CGI.escape(YAML.dump(%w{one two})) @request.expects(:query).returns('foo' => escaping) result = @handler.params(@request) result[:foo].should == %w{one two} end it "should not allow clients to set the node via the request parameters" do @request.stubs(:query).returns("node" => "foo") @handler.stubs(:resolve_node) @handler.params(@request)[:node].should be_nil end it "should not allow clients to set the IP via the request parameters" do @request.stubs(:query).returns("ip" => "foo") @handler.params(@request)[:ip].should_not == "foo" end it "should pass the client's ip address to model find" do @request.stubs(:peeraddr).returns(%w{noidea dunno hostname ipaddress}) @handler.params(@request)[:ip].should == "ipaddress" end it "should set 'authenticated' to true if a certificate is present" do cert = stub 'cert', :subject => [%w{CN host.domain.com}] @request.stubs(:client_cert).returns cert @handler.params(@request)[:authenticated].should be_true end it "should set 'authenticated' to false if no certificate is present" do @request.stubs(:client_cert).returns nil @handler.params(@request)[:authenticated].should be_false end it "should pass the client's certificate name to model method if a certificate is present" do cert = stub 'cert', :subject => [%w{CN host.domain.com}] @request.stubs(:client_cert).returns cert @handler.params(@request)[:node].should == "host.domain.com" end it "should resolve the node name with an ip address look-up if no certificate is present" do @request.stubs(:client_cert).returns nil @handler.expects(:resolve_node).returns(:resolved_node) @handler.params(@request)[:node].should == :resolved_node end end end end diff --git a/spec/unit/network/http/webrick_spec.rb b/spec/unit/network/http/webrick_spec.rb index b27a941ea..be74a1052 100755 --- a/spec/unit/network/http/webrick_spec.rb +++ b/spec/unit/network/http/webrick_spec.rb @@ -1,340 +1,340 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Rick Bradley on 2007-10-15. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/network/handler' require 'puppet/network/http' require 'puppet/network/http/webrick' describe Puppet::Network::HTTP::WEBrick, "after initializing" do it "should not be listening" do Puppet::Network::HTTP::WEBrick.new.should_not be_listening end end describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do before do @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging @listen_params = { :address => "127.0.0.1", :port => 31337, :xmlrpc_handlers => [], :protocols => [ :rest ] } end it "should fail if already listening" do @server.listen(@listen_params) Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) end it "should require at least one protocol" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) end it "should require a listening address to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) end it "should require a listening port to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) end it "should order a webrick server to start in a separate thread" do @mock_webrick.expects(:start) # If you remove this you'll sometimes get race condition problems Thread.expects(:new).yields @server.listen(@listen_params) end it "should tell webrick to listen on the specified address and port" do WEBrick::HTTPServer.expects(:new).with {|args| args[:Port] == 31337 and args[:BindAddress] == "127.0.0.1" }.returns(@mock_webrick) @server.listen(@listen_params) end it "should configure a logger for webrick" do @server.expects(:setup_logger).returns(:Logger => :mylogger) WEBrick::HTTPServer.expects(:new).with {|args| args[:Logger] == :mylogger }.returns(@mock_webrick) @server.listen(@listen_params) end it "should configure SSL for webrick" do @server.expects(:setup_ssl).returns(:Ssl => :testing, :Other => :yay) WEBrick::HTTPServer.expects(:new).with {|args| args[:Ssl] == :testing and args[:Other] == :yay }.returns(@mock_webrick) @server.listen(@listen_params) end it "should be listening" do @server.listen(@listen_params) @server.should be_listening end describe "when the REST protocol is requested" do it "should register the REST handler at /" do # We don't care about the options here. @mock_webrick.expects(:mount).with { |path, klass, options| path == "/" and klass == Puppet::Network::HTTP::WEBrickREST } @server.listen(@listen_params.merge(:protocols => [:rest])) end end describe "when the XMLRPC protocol is requested" do before do @servlet = mock 'servlet' Puppet::Network::XMLRPC::WEBrickServlet.stubs(:new).returns @servlet @master_handler = mock('master_handler') @file_handler = mock('file_handler') @master = mock 'master' @file = mock 'file' @master_handler.stubs(:new).returns @master @file_handler.stubs(:new).returns @file Puppet::Network::Handler.stubs(:handler).with(:master).returns @master_handler Puppet::Network::Handler.stubs(:handler).with(:fileserver).returns @file_handler end it "should do nothing if no xmlrpc handlers have been specified" do Puppet::Network::Handler.expects(:handler).never @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [])) end it "should look the handler classes up via their base class" do Puppet::Network::Handler.expects(:handler).with(:master).returns @master_handler Puppet::Network::Handler.expects(:handler).with(:fileserver).returns @file_handler @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) end it "should create an instance for each requested xmlrpc handler" do @master_handler.expects(:new).returns @master @file_handler.expects(:new).returns @file @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) end it "should create a webrick servlet with the xmlrpc handler instances" do Puppet::Network::XMLRPC::WEBrickServlet.expects(:new).with([@master, @file]).returns @servlet @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) end it "should mount the webrick servlet at /RPC2" do @mock_webrick.stubs(:mount) @mock_webrick.expects(:mount).with("/RPC2", @servlet) @server.listen(@listen_params.merge(:protocols => [:xmlrpc], :xmlrpc_handlers => [:master, :fileserver])) end end end describe Puppet::Network::HTTP::WEBrick, "when looking up the class to handle a protocol" do it "should require a protocol" do lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol }.should raise_error(ArgumentError) end it "should accept a protocol" do lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("bob") }.should_not raise_error(ArgumentError) end it "should use a WEBrick + REST class when a REST protocol is specified" do Puppet::Network::HTTP::WEBrick.class_for_protocol("rest").should == Puppet::Network::HTTP::WEBrickREST end it "should fail when an unknown protocol is specified" do lambda { Puppet::Network::HTTP::WEBrick.class_for_protocol("abcdefg") }.should raise_error end end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } end it "should fail unless listening" do Proc.new { @server.unlisten }.should raise_error(RuntimeError) end it "should order webrick server to stop" do @mock_webrick.expects(:shutdown) @server.listen(@listen_params) @server.unlisten end it "should no longer be listening" do @server.listen(@listen_params) @server.unlisten @server.should_not be_listening end end describe Puppet::Network::HTTP::WEBrick do before do @mock_webrick = stub('webrick', :[] => {}) [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new end describe "when configuring an http logger" do before do Puppet.settings.stubs(:value).returns "something" Puppet.settings.stubs(:use) @filehandle = stub 'handle', :fcntl => nil, :sync => nil File.stubs(:open).returns @filehandle end it "should use the settings for :main, :ssl, and the process name" do Puppet.settings.stubs(:value).with(:name).returns "myname" Puppet.settings.expects(:use).with(:main, :ssl, "myname") @server.setup_logger end it "should use the masterlog if the run_mode is master" do Puppet.run_mode.stubs(:master?).returns(true) Puppet.settings.expects(:value).with(:masterhttplog).returns "/master/log" File.expects(:open).with("/master/log", "a+").returns @filehandle @server.setup_logger end it "should use the httplog if the run_mode is not master" do Puppet.run_mode.stubs(:master?).returns(false) Puppet.settings.expects(:value).with(:httplog).returns "/other/log" File.expects(:open).with("/other/log", "a+").returns @filehandle @server.setup_logger end describe "and creating the logging filehandle" do it "should set fcntl to 'Fcntl::F_SETFD, Fcntl::FD_CLOEXEC'" do @filehandle.expects(:fcntl).with(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) @server.setup_logger end it "should sync the filehandle" do @filehandle.expects(:sync) @server.setup_logger end end it "should create a new WEBrick::Log instance with the open filehandle" do WEBrick::Log.expects(:new).with(@filehandle) @server.setup_logger end it "should set debugging if the current loglevel is :debug" do Puppet::Util::Log.expects(:level).returns :debug WEBrick::Log.expects(:new).with { |handle, debug| debug == WEBrick::Log::DEBUG } @server.setup_logger end it "should return the logger as the main log" do logger = mock 'logger' WEBrick::Log.expects(:new).returns logger @server.setup_logger[:Logger].should == logger end it "should return the logger as the access log using both the Common and Referer log format" do logger = mock 'logger' WEBrick::Log.expects(:new).returns logger @server.setup_logger[:AccessLog].should == [ [logger, WEBrick::AccessLog::COMMON_LOG_FORMAT], [logger, WEBrick::AccessLog::REFERER_LOG_FORMAT] ] end end describe "when configuring ssl" do before do @key = stub 'key', :content => "mykey" @cert = stub 'cert', :content => "mycert" @host = stub 'host', :key => @key, :certificate => @cert, :name => "yay", :ssl_store => "mystore" Puppet::SSL::Certificate.indirection.stubs(:find).with('ca').returns @cert Puppet::SSL::Host.stubs(:localhost).returns @host end it "should use the key from the localhost SSL::Host instance" do Puppet::SSL::Host.expects(:localhost).returns @host @host.expects(:key).returns @key @server.setup_ssl[:SSLPrivateKey].should == "mykey" end it "should configure the certificate" do @server.setup_ssl[:SSLCertificate].should == "mycert" end it "should fail if no CA certificate can be found" do Puppet::SSL::Certificate.indirection.stubs(:find).with('ca').returns nil lambda { @server.setup_ssl }.should raise_error(Puppet::Error) end it "should specify the path to the CA certificate" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:hostcrl).returns 'false' Puppet.settings.stubs(:value).with(:localcacert).returns '/ca/crt' @server.setup_ssl[:SSLCACertificateFile].should == "/ca/crt" end it "should start ssl immediately" do @server.setup_ssl[:SSLStartImmediately].should be_true end it "should enable ssl" do @server.setup_ssl[:SSLEnable].should be_true end it "should configure the verification method as 'OpenSSL::SSL::VERIFY_PEER'" do @server.setup_ssl[:SSLVerifyClient].should == OpenSSL::SSL::VERIFY_PEER end it "should add an x509 store" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:hostcrl).returns '/my/crl' @host.expects(:ssl_store).returns "mystore" @server.setup_ssl[:SSLCertificateStore].should == "mystore" end it "should set the certificate name to 'nil'" do @server.setup_ssl[:SSLCertName].should be_nil end end end diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb index 2cd96cea4..c5d3e0470 100755 --- a/spec/unit/network/http_pool_spec.rb +++ b/spec/unit/network/http_pool_spec.rb @@ -1,206 +1,206 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-26. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/network/http_pool' describe Puppet::Network::HttpPool do after do Puppet::Util::Cacher.expire Puppet::Network::HttpPool.clear_http_instances Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) end it "should have keep-alive disabled" do Puppet::Network::HttpPool::HTTP_KEEP_ALIVE.should be_false end it "should use the global SSL::Host instance to get its certificate information" do host = mock 'host' Puppet::SSL::Host.expects(:localhost).with.returns host Puppet::Network::HttpPool.ssl_host.should equal(host) end describe "when managing http instances" do def stub_settings(settings) settings.each do |param, value| Puppet.settings.stubs(:value).with(param).returns(value) end end before do # All of the cert stuff is tested elsewhere Puppet::Network::HttpPool.stubs(:cert_setup) end it "should return an http instance created with the passed host and port" do http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :started? => false Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) end it "should enable ssl on the http instance" do Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true end it "should set the read timeout" do Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 end it "should set the open timeout" do Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 end it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 end describe "and http keep-alive is enabled" do before do Puppet::Network::HttpPool.stubs(:keep_alive?).returns true end it "should cache http instances" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) end it "should have a mechanism for getting a new http instance instead of the cached instance" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321, true).should_not equal(old) end it "should close existing, open connections when requesting a new connection" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) old.expects(:started?).returns(true) old.expects(:finish) Puppet::Network::HttpPool.http_instance("me", 54321, true) end it "should have a mechanism for clearing the http cache" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.clear_http_instances Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) end it "should close open http connections when clearing the cache" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 one = Puppet::Network::HttpPool.http_instance("me", 54321) one.expects(:started?).returns(true) one.expects(:finish).returns(true) Puppet::Network::HttpPool.clear_http_instances end it "should not close unopened http connections when clearing the cache" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 one = Puppet::Network::HttpPool.http_instance("me", 54321) one.expects(:started?).returns(false) one.expects(:finish).never Puppet::Network::HttpPool.clear_http_instances end end describe "and http keep-alive is disabled" do before do Puppet::Network::HttpPool.stubs(:keep_alive?).returns false end it "should not cache http instances" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) end end after do Puppet::Network::HttpPool.clear_http_instances end end describe "when adding certificate information to http instances" do before do @http = mock 'http' [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |m| @http.stubs(m) } @store = stub 'store' @cert = stub 'cert', :content => "real_cert" @key = stub 'key', :content => "real_key" @host = stub 'host', :certificate => @cert, :key => @key, :ssl_store => @store Puppet[:confdir] = "/sometthing/else" Puppet.settings.stubs(:value).returns "/some/file" Puppet.settings.stubs(:value).with(:hostcert).returns "/host/cert" Puppet.settings.stubs(:value).with(:localcacert).returns "/local/ca/cert" FileTest.stubs(:exist?).with("/host/cert").returns true FileTest.stubs(:exist?).with("/local/ca/cert").returns true Puppet::Network::HttpPool.stubs(:ssl_host).returns @host end after do Puppet.settings.clear end it "should do nothing if no host certificate is on disk" do FileTest.expects(:exist?).with("/host/cert").returns false @http.expects(:cert=).never Puppet::Network::HttpPool.cert_setup(@http) end it "should do nothing if no local certificate is on disk" do FileTest.expects(:exist?).with("/local/ca/cert").returns false @http.expects(:cert=).never Puppet::Network::HttpPool.cert_setup(@http) end it "should add a certificate store from the ssl host" do @http.expects(:cert_store=).with(@store) Puppet::Network::HttpPool.cert_setup(@http) end it "should add the client certificate" do @http.expects(:cert=).with("real_cert") Puppet::Network::HttpPool.cert_setup(@http) end it "should add the client key" do @http.expects(:key=).with("real_key") Puppet::Network::HttpPool.cert_setup(@http) end it "should set the verify mode to OpenSSL::SSL::VERIFY_PEER" do @http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) Puppet::Network::HttpPool.cert_setup(@http) end it "should set the ca file" do Puppet.settings.stubs(:value).returns "/some/file" FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns true Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" FileTest.stubs(:exist?).with("/ca/cert/file").returns true @http.expects(:ca_file=).with("/ca/cert/file") Puppet::Network::HttpPool.cert_setup(@http) end it "should set up certificate information when creating http instances" do Puppet::Network::HttpPool.expects(:cert_setup).with { |i| i.is_a?(Net::HTTP) } Puppet::Network::HttpPool.http_instance("one", "two") end end end diff --git a/spec/unit/network/http_spec.rb b/spec/unit/network/http_spec.rb index 8422e2113..550c15bf7 100755 --- a/spec/unit/network/http_spec.rb +++ b/spec/unit/network/http_spec.rb @@ -1,35 +1,35 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Rick Bradley on 2007-10-03. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/network/http' describe Puppet::Network::HTTP do it "should return the webrick HTTP server class when asked for a webrick server" do Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick) end describe "when asked for a mongrel server" do if Puppet.features.mongrel? it "should return the mongrel server class" do Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) end else it "should fail" do lambda { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) end end end it "should fail to return the mongrel HTTP server class if mongrel is not available " do Puppet.features.expects(:mongrel?).returns(false) Proc.new { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) end it "should return an error when asked for an unknown server" do Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) end end diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb index e0bcb5af0..499a14b78 100755 --- a/spec/unit/network/rest_authconfig_spec.rb +++ b/spec/unit/network/rest_authconfig_spec.rb @@ -1,139 +1,138 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/rest_authconfig' describe Puppet::Network::RestAuthConfig do DEFAULT_ACL = [ { :acl => "~ ^\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true }, # this one will allow all file access, and thus delegate # to fileserver.conf { :acl => "/file" }, { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true }, { :acl => "/report", :method => :save, :authenticated => true }, { :acl => "/certificate/ca", :method => :find, :authenticated => false }, { :acl => "/certificate/", :method => :find, :authenticated => false }, { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, { :acl => "/status", :method => [:find], :authenticated => true }, ] before :each do FileTest.stubs(:exists?).returns(true) File.stubs(:stat).returns(stub('stat', :ctime => :now)) Time.stubs(:now).returns Time.now @authconfig = Puppet::Network::RestAuthConfig.new("dummy", false) @authconfig.stubs(:read) @acl = stub_everything 'rights' @authconfig.rights = @acl end it "should use the puppet default rest authorization file" do Puppet.expects(:[]).with(:rest_authconfig).returns("dummy") Puppet::Network::RestAuthConfig.new(nil, false) end it "should ask for authorization to the ACL subsystem" do params = {:ip => "127.0.0.1", :node => "me", :environment => :env, :authenticated => true} @acl.expects(:is_request_forbidden_and_why?).with("path", :save, "to/resource", params).returns(nil) @authconfig.allowed?("path", :save, "to/resource", params) end describe "when defining an acl with mk_acl" do it "should create a new right for each default acl" do @acl.expects(:newright).with(:path) @authconfig.mk_acl(:acl => :path) end it "should allow everyone for each default right" do @acl.expects(:allow).with(:path, "*") @authconfig.mk_acl(:acl => :path) end it "should restrict the ACL to a method" do @acl.expects(:restrict_method).with(:path, :method) @authconfig.mk_acl(:acl => :path, :method => :method) end it "should restrict the ACL to a specific authentication state" do @acl.expects(:restrict_authenticated).with(:path, :authentication) @authconfig.mk_acl(:acl => :path, :authenticated => :authentication) end end describe "when parsing the configuration file" do it "should check for missing ACL after reading the authconfig file" do File.stubs(:open) @authconfig.expects(:insert_default_acl) @authconfig.parse end end DEFAULT_ACL.each do |acl| it "should insert #{acl[:acl]} if not present" do @authconfig.rights.stubs(:[]).returns(true) @authconfig.rights.stubs(:[]).with(acl[:acl]).returns(nil) @authconfig.expects(:mk_acl).with { |h| h[:acl] == acl[:acl] } @authconfig.insert_default_acl end it "should not insert #{acl[:acl]} if present" do @authconfig.rights.stubs(:[]).returns(true) @authconfig.rights.stubs(:[]).with(acl).returns(true) @authconfig.expects(:mk_acl).never @authconfig.insert_default_acl end end it "should create default ACL entries if no file have been read" do Puppet::Network::RestAuthConfig.any_instance.stubs(:exists?).returns(false) Puppet::Network::RestAuthConfig.any_instance.expects(:insert_default_acl) Puppet::Network::RestAuthConfig.main end describe "when adding default ACLs" do DEFAULT_ACL.each do |acl| it "should create a default right for #{acl[:acl]}" do @authconfig.stubs(:mk_acl) @authconfig.expects(:mk_acl).with(acl) @authconfig.insert_default_acl end end it "should log at info loglevel" do Puppet.expects(:info).at_least_once @authconfig.insert_default_acl end it "should create a last catch-all deny all rule" do @authconfig.stubs(:mk_acl) @acl.expects(:newright).with("/") @authconfig.insert_default_acl end it "should create a last catch-all deny all rule for any authenticated request state" do @authconfig.stubs(:mk_acl) @acl.stubs(:newright).with("/") @acl.expects(:restrict_authenticated).with("/", :any) @authconfig.insert_default_acl end end end diff --git a/spec/unit/network/rights_spec.rb b/spec/unit/network/rights_spec.rb index 8ae03c56d..b709f10fa 100755 --- a/spec/unit/network/rights_spec.rb +++ b/spec/unit/network/rights_spec.rb @@ -1,539 +1,538 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/network/rights' describe Puppet::Network::Rights do before do @right = Puppet::Network::Rights.new end describe "when validating a :head request" do [:find, :save].each do |allowed_method| it "should allow the request if only #{allowed_method} is allowed" do rights = Puppet::Network::Rights.new rights.newright("/") rights.allow("/", "*") rights.restrict_method("/", allowed_method) rights.restrict_authenticated("/", :any) rights.is_request_forbidden_and_why?(:indirection_name, :head, "key", {}).should == nil end end it "should disallow the request if neither :find nor :save is allowed" do rights = Puppet::Network::Rights.new why_forbidden = rights.is_request_forbidden_and_why?(:indirection_name, :head, "key", {}) why_forbidden.should be_instance_of(Puppet::Network::AuthorizationError) why_forbidden.to_s.should == "Forbidden request: access to /indirection_name/key [find]" end end [:allow, :deny, :restrict_method, :restrict_environment, :restrict_authenticated].each do |m| it "should have a #{m} method" do @right.should respond_to(m) end describe "when using #{m}" do it "should delegate to the correct acl" do acl = stub 'acl' @right.stubs(:[]).returns(acl) acl.expects(m).with("me") @right.send(m, 'thisacl', "me") end end end it "should throw an error if type can't be determined" do lambda { @right.newright("name") }.should raise_error end describe "when creating new namespace ACLs" do it "should throw an error if the ACL already exists" do @right.newright("[name]") lambda { @right.newright("[name]") }.should raise_error end it "should create a new ACL with the correct name" do @right.newright("[name]") @right["name"].key.should == :name end it "should create an ACL of type Puppet::Network::AuthStore" do @right.newright("[name]") @right["name"].should be_a_kind_of(Puppet::Network::AuthStore) end end describe "when creating new path ACLs" do it "should not throw an error if the ACL already exists" do @right.newright("/name") lambda { @right.newright("/name")}.should_not raise_error end it "should throw an error if the acl uri path is not absolute" do lambda { @right.newright("name")}.should raise_error end it "should create a new ACL with the correct path" do @right.newright("/name") @right["/name"].should_not be_nil end it "should create an ACL of type Puppet::Network::AuthStore" do @right.newright("/name") @right["/name"].should be_a_kind_of(Puppet::Network::AuthStore) end end describe "when creating new regex ACLs" do it "should not throw an error if the ACL already exists" do @right.newright("~ .rb$") lambda { @right.newright("~ .rb$")}.should_not raise_error end it "should create a new ACL with the correct regex" do @right.newright("~ .rb$") @right.include?(".rb$").should_not be_nil end it "should be able to lookup the regex" do @right.newright("~ .rb$") @right[".rb$"].should_not be_nil end it "should be able to lookup the regex by its full name" do @right.newright("~ .rb$") @right["~ .rb$"].should_not be_nil end it "should create an ACL of type Puppet::Network::AuthStore" do @right.newright("~ .rb$").should be_a_kind_of(Puppet::Network::AuthStore) end end describe "when checking ACLs existence" do it "should return false if there are no matching rights" do @right.include?("name").should be_false end it "should return true if a namespace rights exist" do @right.newright("[name]") @right.include?("name").should be_true end it "should return false if no matching namespace rights exist" do @right.newright("[name]") @right.include?("notname").should be_false end it "should return true if a path right exists" do @right.newright("/name") @right.include?("/name").should be_true end it "should return false if no matching path rights exist" do @right.newright("/name") @right.include?("/differentname").should be_false end it "should return true if a regex right exists" do @right.newright("~ .rb$") @right.include?(".rb$").should be_true end it "should return false if no matching path rights exist" do @right.newright("~ .rb$") @right.include?(".pp$").should be_false end end describe "when checking if right is allowed" do before :each do @right.stubs(:right).returns(nil) @pathacl = stub 'pathacl', :acl_type => :regex, :"<=>" => 1, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).returns(@pathacl) end it "should delegate to is_forbidden_and_why?" do @right.expects(:is_forbidden_and_why?).with("namespace", :node => "host.domain.com", :ip => "127.0.0.1").returns(nil) @right.allowed?("namespace", "host.domain.com", "127.0.0.1") end it "should return true if is_forbidden_and_why? returns nil" do @right.stubs(:is_forbidden_and_why?).returns(nil) @right.allowed?("namespace", :args).should be_true end it "should return false if is_forbidden_and_why? returns an AuthorizationError" do @right.stubs(:is_forbidden_and_why?).returns(Puppet::Network::AuthorizationError.new("forbidden")) @right.allowed?("namespace", :args1, :args2).should be_false end it "should first check namespace rights" do acl = stub 'acl', :acl_type => :name, :key => :namespace Puppet::Network::Rights::Right.stubs(:new).returns(acl) @right.newright("[namespace]") acl.expects(:match?).returns(true) acl.expects(:allowed?).with { |node,ip,h| node == "node" and ip == "ip" }.returns(true) @right.is_forbidden_and_why?("namespace", { :node => "node", :ip => "ip" } ).should == nil end it "should then check for path rights if no namespace match" do acl = stub 'nmacl', :acl_type => :name, :key => :namespace, :"<=>" => -1, :line => 0, :file => 'dummy' acl.stubs(:match?).returns(false) Puppet::Network::Rights::Right.stubs(:new).with("[namespace]").returns(acl) @right.newright("[namespace]") @right.newright("/path/to/there", 0, nil) @pathacl.stubs(:match?).returns(true) acl.expects(:allowed?).never @pathacl.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there", {}).should == nil end it "should pass the match? return to allowed?" do @right.newright("/path/to/there") @pathacl.expects(:match?).returns(:match) @pathacl.expects(:allowed?).with { |node,ip,h| h[:match] == :match }.returns(true) @right.is_forbidden_and_why?("/path/to/there", {}).should == nil end describe "with namespace acls" do it "should return an ArgumentError if this namespace right doesn't exist" do lambda { @right.is_forbidden_and_why?("namespace") }.should raise_error(ArgumentError) end end describe "with path acls" do before :each do @long_acl = stub 'longpathacl', :name => "/path/to/there", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("/path/to/there", 0, nil).returns(@long_acl) @short_acl = stub 'shortpathacl', :name => "/path/to", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("/path/to", 0, nil).returns(@short_acl) @long_acl.stubs(:"<=>").with(@short_acl).returns(0) @short_acl.stubs(:"<=>").with(@long_acl).returns(0) end it "should select the first match" do @right.newright("/path/to/there", 0) @right.newright("/path/to", 0) @long_acl.stubs(:match?).returns(true) @short_acl.stubs(:match?).returns(true) @long_acl.expects(:allowed?).returns(true) @short_acl.expects(:allowed?).never @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should select the first match that doesn't return :dunno" do @right.newright("/path/to/there", 0, nil) @right.newright("/path/to", 0, nil) @long_acl.stubs(:match?).returns(true) @short_acl.stubs(:match?).returns(true) @long_acl.expects(:allowed?).returns(:dunno) @short_acl.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should not select an ACL that doesn't match" do @right.newright("/path/to/there", 0) @right.newright("/path/to", 0) @long_acl.stubs(:match?).returns(false) @short_acl.stubs(:match?).returns(true) @long_acl.expects(:allowed?).never @short_acl.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should not raise an AuthorizationError if allowed" do @right.newright("/path/to/there", 0) @long_acl.stubs(:match?).returns(true) @long_acl.stubs(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should raise an AuthorizationError if the match is denied" do @right.newright("/path/to/there", 0, nil) @long_acl.stubs(:match?).returns(true) @long_acl.stubs(:allowed?).returns(false) @right.is_forbidden_and_why?("/path/to/there", {}).should be_instance_of(Puppet::Network::AuthorizationError) end it "should raise an AuthorizationError if no path match" do @right.is_forbidden_and_why?("/nomatch", {}).should be_instance_of(Puppet::Network::AuthorizationError) end end describe "with regex acls" do before :each do @regex_acl1 = stub 'regex_acl1', :name => "/files/(.*)/myfile", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile", 0, nil).returns(@regex_acl1) @regex_acl2 = stub 'regex_acl2', :name => "/files/(.*)/myfile/", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile/", 0, nil).returns(@regex_acl2) @regex_acl1.stubs(:"<=>").with(@regex_acl2).returns(0) @regex_acl2.stubs(:"<=>").with(@regex_acl1).returns(0) end it "should select the first match" do @right.newright("~ /files/(.*)/myfile", 0) @right.newright("~ /files/(.*)/myfile/", 0) @regex_acl1.stubs(:match?).returns(true) @regex_acl2.stubs(:match?).returns(true) @regex_acl1.expects(:allowed?).returns(true) @regex_acl2.expects(:allowed?).never @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should select the first match that doesn't return :dunno" do @right.newright("~ /files/(.*)/myfile", 0) @right.newright("~ /files/(.*)/myfile/", 0) @regex_acl1.stubs(:match?).returns(true) @regex_acl2.stubs(:match?).returns(true) @regex_acl1.expects(:allowed?).returns(:dunno) @regex_acl2.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should not select an ACL that doesn't match" do @right.newright("~ /files/(.*)/myfile", 0) @right.newright("~ /files/(.*)/myfile/", 0) @regex_acl1.stubs(:match?).returns(false) @regex_acl2.stubs(:match?).returns(true) @regex_acl1.expects(:allowed?).never @regex_acl2.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should not raise an AuthorizationError if allowed" do @right.newright("~ /files/(.*)/myfile", 0) @regex_acl1.stubs(:match?).returns(true) @regex_acl1.stubs(:allowed?).returns(true) @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should raise an error if no regex acl match" do @right.is_forbidden_and_why?("/path", {}).should be_instance_of(Puppet::Network::AuthorizationError) end it "should raise an AuthorizedError on deny" do @right.is_forbidden_and_why?("/path", {}).should be_instance_of(Puppet::Network::AuthorizationError) end end end describe Puppet::Network::Rights::Right do before :each do @acl = Puppet::Network::Rights::Right.new("/path",0, nil) end describe "with path" do it "should say it's a regex ACL" do @acl.acl_type.should == :regex end it "should match up to its path length" do @acl.match?("/path/that/works").should_not be_nil end it "should match up to its path length" do @acl.match?("/paththatalsoworks").should_not be_nil end it "should return nil if no match" do @acl.match?("/notpath").should be_nil end end describe "with regex" do before :each do @acl = Puppet::Network::Rights::Right.new("~ .rb$",0, nil) end it "should say it's a regex ACL" do @acl.acl_type.should == :regex end it "should match as a regex" do @acl.match?("this should work.rb").should_not be_nil end it "should return nil if no match" do @acl.match?("do not match").should be_nil end end it "should allow all rest methods by default" do @acl.methods.should == Puppet::Network::Rights::Right::ALL end it "should allow only authenticated request by default" do @acl.authentication.should be_true end it "should allow modification of the methods filters" do @acl.restrict_method(:save) @acl.methods.should == [:save] end it "should stack methods filters" do @acl.restrict_method(:save) @acl.restrict_method(:destroy) @acl.methods.should == [:save, :destroy] end it "should raise an error if the method is already filtered" do @acl.restrict_method(:save) lambda { @acl.restrict_method(:save) }.should raise_error end it "should allow setting an environment filters" do Puppet::Node::Environment.stubs(:new).with(:environment).returns(:env) @acl.restrict_environment(:environment) @acl.environment.should == [:env] end ["on", "yes", "true", true].each do |auth| it "should allow filtering on authenticated requests with '#{auth}'" do @acl.restrict_authenticated(auth) @acl.authentication.should be_true end end ["off", "no", "false", false].each do |auth| it "should allow filtering on unauthenticated requests with '#{auth}'" do @acl.restrict_authenticated(auth) @acl.authentication.should be_false end end ["all", "any", :all, :any].each do |auth| it "should not use request authenticated state filtering with '#{auth}'" do @acl.restrict_authenticated(auth) @acl.authentication.should be_nil end end describe "when checking right authorization" do it "should return :dunno if this right is not restricted to the given method" do @acl.restrict_method(:destroy) @acl.allowed?("me","127.0.0.1", { :method => :save } ).should == :dunno end it "should return allow/deny if this right is restricted to the given method" do @acl.restrict_method(:save) @acl.allow("127.0.0.1") @acl.allowed?("me","127.0.0.1", { :method => :save }).should be_true end it "should return :dunno if this right is not restricted to the given environment" do Puppet::Node::Environment.stubs(:new).returns(:production) @acl.restrict_environment(:production) @acl.allowed?("me","127.0.0.1", { :method => :save, :environment => :development }).should == :dunno end it "should return :dunno if this right is not restricted to the given request authentication state" do @acl.restrict_authenticated(true) @acl.allowed?("me","127.0.0.1", { :method => :save, :authenticated => false }).should == :dunno end it "should return allow/deny if this right is restricted to the given request authentication state" do @acl.restrict_authenticated(false) @acl.allow("127.0.0.1") @acl.allowed?("me","127.0.0.1", { :authenticated => false }).should be_true end it "should interpolate allow/deny patterns with the given match" do @acl.expects(:interpolate).with(:match) @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) end it "should reset interpolation after the match" do @acl.expects(:reset_interpolation) @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) end # mocha doesn't allow testing super... # it "should delegate to the AuthStore for the result" do # @acl.method(:save) # # @acl.expects(:allowed?).with("me","127.0.0.1") # # @acl.allowed?("me","127.0.0.1", :save) # end end end end diff --git a/spec/unit/network/server_spec.rb b/spec/unit/network/server_spec.rb index 2a63cd4ec..912275a20 100755 --- a/spec/unit/network/server_spec.rb +++ b/spec/unit/network/server_spec.rb @@ -1,527 +1,527 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Rick Bradley on 2007-10-03. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/network/server' require 'puppet/network/handler' describe Puppet::Network::Server do before do @mock_http_server_class = mock('http server class') Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).with(:name).returns("me") Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) Puppet.settings.stubs(:value).with(:bindaddress).returns("") Puppet.settings.stubs(:value).with(:masterport).returns(8140) Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) @server = Puppet::Network::Server.new(:port => 31337) end describe "when initializing" do before do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') Puppet.settings.stubs(:value).with(:bindaddress).returns("") Puppet.settings.stubs(:value).with(:masterport).returns('') end it 'should fail if an unknown option is provided' do lambda { Puppet::Network::Server.new(:foo => 31337) }.should raise_error(ArgumentError) end it "should allow specifying a listening port" do Puppet.settings.stubs(:value).with(:bindaddress).returns('') @server = Puppet::Network::Server.new(:port => 31337) @server.port.should == 31337 end it "should use the :bindaddress setting to determine the default listening address" do Puppet.settings.stubs(:value).with(:masterport).returns('') Puppet.settings.expects(:value).with(:bindaddress).returns("10.0.0.1") @server = Puppet::Network::Server.new @server.address.should == "10.0.0.1" end it "should set the bind address to '127.0.0.1' if the default address is an empty string and the server type is mongrel" do Puppet.settings.stubs(:value).with(:servertype).returns("mongrel") Puppet.settings.expects(:value).with(:bindaddress).returns("") @server = Puppet::Network::Server.new @server.address.should == '127.0.0.1' end it "should set the bind address to '0.0.0.0' if the default address is an empty string and the server type is webrick" do Puppet.settings.stubs(:value).with(:servertype).returns("webrick") Puppet.settings.expects(:value).with(:bindaddress).returns("") @server = Puppet::Network::Server.new @server.address.should == '0.0.0.0' end it "should use the Puppet configurator to find a default listening port" do Puppet.settings.stubs(:value).with(:bindaddress).returns('') Puppet.settings.expects(:value).with(:masterport).returns(6667) @server = Puppet::Network::Server.new @server.port.should == 6667 end it "should fail to initialize if no listening port can be found" do Puppet.settings.stubs(:value).with(:bindaddress).returns("127.0.0.1") Puppet.settings.stubs(:value).with(:masterport).returns(nil) lambda { Puppet::Network::Server.new }.should raise_error(ArgumentError) end it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do Puppet.settings.expects(:value).with(:servertype).returns(:suparserver) @server = Puppet::Network::Server.new(:port => 31337) @server.server_type.should == :suparserver end it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do Puppet.settings.expects(:value).with(:servertype).returns(nil) lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error end it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(@mock_http_server_class) @server = Puppet::Network::Server.new(:port => 31337) end it "should fail if the HTTP server class is unknown" do Puppet::Network::HTTP.stubs(:server_class_by_type).returns(nil) lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error(ArgumentError) end it "should allow registering REST handlers" do @server = Puppet::Network::Server.new(:port => 31337, :handlers => [ :foo, :bar, :baz]) lambda { @server.unregister(:foo, :bar, :baz) }.should_not raise_error end it "should allow registering XMLRPC handlers" do @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) lambda { @server.unregister_xmlrpc(:foo, :bar, :baz) }.should_not raise_error end it "should not be listening after initialization" do Puppet::Network::Server.new(:port => 31337).should_not be_listening end it "should use the :main setting section" do Puppet.settings.expects(:use).with { |*args| args.include?(:main) } @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) end it "should use the Puppet[:name] setting section" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet.settings.expects(:use).with { |*args| args.include?("me") } @server = Puppet::Network::Server.new(:port => 31337, :xmlrpc_handlers => [ :foo, :bar, :baz]) end end # We don't test the method, because it's too much of a Unix-y pain. it "should be able to daemonize" do @server.should respond_to(:daemonize) end describe "when being started" do before do @server.stubs(:listen) @server.stubs(:create_pidfile) end it "should listen" do @server.expects(:listen) @server.start end it "should create its PID file" do @server.expects(:create_pidfile) @server.start end end describe "when being stopped" do before do @server.stubs(:unlisten) @server.stubs(:remove_pidfile) end it "should unlisten" do @server.expects(:unlisten) @server.stop end it "should remove its PID file" do @server.expects(:remove_pidfile) @server.stop end end describe "when creating its pidfile" do it "should use an exclusive mutex" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @server.create_pidfile end it "should lock the pidfile using the Pidlock class" do pidfile = mock 'pidfile' Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns true @server.create_pidfile end it "should fail if it cannot lock" do pidfile = mock 'pidfile' Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns false lambda { @server.create_pidfile }.should raise_error end end describe "when removing its pidfile" do it "should use an exclusive mutex" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @server.remove_pidfile end it "should do nothing if the pidfile is not present" do pidfile = mock 'pidfile', :locked? => false Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" pidfile.expects(:unlock).never @server.remove_pidfile end it "should unlock the pidfile using the Pidlock class" do pidfile = mock 'pidfile', :locked? => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:unlock).returns true Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" @server.remove_pidfile end it "should warn if it cannot remove the pidfile" do pidfile = mock 'pidfile', :locked? => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:unlock).returns false Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet.expects :err @server.remove_pidfile end end describe "when managing indirection registrations" do before do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') end it "should allow registering an indirection for client access by specifying its indirection name" do lambda { @server.register(:foo) }.should_not raise_error end it "should require that the indirection be valid" do Puppet::Indirector::Indirection.expects(:model).with(:foo).returns nil lambda { @server.register(:foo) }.should raise_error(ArgumentError) end it "should require at least one indirection name when registering indirections for client access" do lambda { @server.register }.should raise_error(ArgumentError) end it "should allow for numerous indirections to be registered at once for client access" do lambda { @server.register(:foo, :bar, :baz) }.should_not raise_error end it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do @server.register(:foo) lambda { @server.unregister(:foo) }.should_not raise_error end it "should leave other indirections accessible to clients when turning off indirections" do @server.register(:foo, :bar) @server.unregister(:foo) lambda { @server.unregister(:bar)}.should_not raise_error end it "should allow specifying numerous indirections which are to be no longer accessible to clients" do @server.register(:foo, :bar) lambda { @server.unregister(:foo, :bar) }.should_not raise_error end it "should not turn off any indirections if given unknown indirection names to turn off" do @server.register(:foo, :bar) lambda { @server.unregister(:foo, :bar, :baz) }.should raise_error(ArgumentError) lambda { @server.unregister(:foo, :bar) }.should_not raise_error end it "should not allow turning off unknown indirection names" do @server.register(:foo, :bar) lambda { @server.unregister(:baz) }.should raise_error(ArgumentError) end it "should disable client access immediately when turning off indirections" do @server.register(:foo, :bar) @server.unregister(:foo) lambda { @server.unregister(:foo) }.should raise_error(ArgumentError) end it "should allow turning off all indirections at once" do @server.register(:foo, :bar) @server.unregister [ :foo, :bar, :baz].each do |indirection| lambda { @server.unregister(indirection) }.should raise_error(ArgumentError) end end end it "should provide a means of determining whether it is listening" do @server.should respond_to(:listening?) end it "should provide a means of determining which HTTP server will be used to provide access to clients" do @server.server_type.should == :suparserver end it "should provide a means of determining which protocols are in use" do @server.should respond_to(:protocols) end it "should set the protocols to :rest and :xmlrpc" do @server.protocols.should == [ :rest, :xmlrpc ] end it "should provide a means of determining the listening address" do @server.address.should == "127.0.0.1" end it "should provide a means of determining the listening port" do @server.port.should == 31337 end it "should allow for multiple configurations, each handling different indirections" do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') @server2 = Puppet::Network::Server.new(:port => 31337) @server.register(:foo, :bar) @server2.register(:foo, :xyzzy) @server.unregister(:foo, :bar) @server2.unregister(:foo, :xyzzy) lambda { @server.unregister(:xyzzy) }.should raise_error(ArgumentError) lambda { @server2.unregister(:bar) }.should raise_error(ArgumentError) end describe "when managing xmlrpc registrations" do before do Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') end it "should allow registering an xmlrpc handler by specifying its namespace" do lambda { @server.register_xmlrpc(:foo) }.should_not raise_error end it "should require that the xmlrpc namespace be valid" do Puppet::Network::Handler.stubs(:handler).returns nil lambda { @server.register_xmlrpc(:foo) }.should raise_error(ArgumentError) end it "should require at least one namespace" do lambda { @server.register_xmlrpc }.should raise_error(ArgumentError) end it "should allow multiple namespaces to be registered at once" do lambda { @server.register_xmlrpc(:foo, :bar) }.should_not raise_error end it "should allow the use of namespaces to specify which are no longer accessible to clients" do @server.register_xmlrpc(:foo, :bar) end it "should leave other namespaces accessible to clients when turning off xmlrpc namespaces" do @server.register_xmlrpc(:foo, :bar) @server.unregister_xmlrpc(:foo) lambda { @server.unregister_xmlrpc(:bar)}.should_not raise_error end it "should allow specifying numerous namespaces which are to be no longer accessible to clients" do @server.register_xmlrpc(:foo, :bar) lambda { @server.unregister_xmlrpc(:foo, :bar) }.should_not raise_error end it "should not turn off any indirections if given unknown namespaces to turn off" do @server.register_xmlrpc(:foo, :bar) lambda { @server.unregister_xmlrpc(:foo, :bar, :baz) }.should raise_error(ArgumentError) lambda { @server.unregister_xmlrpc(:foo, :bar) }.should_not raise_error end it "should not allow turning off unknown namespaces" do @server.register_xmlrpc(:foo, :bar) lambda { @server.unregister_xmlrpc(:baz) }.should raise_error(ArgumentError) end it "should disable client access immediately when turning off namespaces" do @server.register_xmlrpc(:foo, :bar) @server.unregister_xmlrpc(:foo) lambda { @server.unregister_xmlrpc(:foo) }.should raise_error(ArgumentError) end it "should allow turning off all namespaces at once" do @server.register_xmlrpc(:foo, :bar) @server.unregister_xmlrpc [ :foo, :bar, :baz].each do |indirection| lambda { @server.unregister_xmlrpc(indirection) }.should raise_error(ArgumentError) end end end describe "when listening is off" do before do @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @server.stubs(:http_server).returns(@mock_http_server) end it "should indicate that it is not listening" do @server.should_not be_listening end it "should not allow listening to be turned off" do lambda { @server.unlisten }.should raise_error(RuntimeError) end it "should allow listening to be turned on" do lambda { @server.listen }.should_not raise_error end end describe "when listening is on" do before do @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @mock_http_server.stubs(:unlisten) @server.stubs(:http_server).returns(@mock_http_server) @server.listen end it "should indicate that it is listening" do @server.should be_listening end it "should not allow listening to be turned on" do lambda { @server.listen }.should raise_error(RuntimeError) end it "should allow listening to be turned off" do lambda { @server.unlisten }.should_not raise_error end end describe "when listening is being turned on" do before do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') Puppet::Network::Handler.stubs(:handler).returns mock('xmlrpc_handler') @server = Puppet::Network::Server.new(:port => 31337, :handlers => [:node], :xmlrpc_handlers => [:master]) @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) end it "should fetch an instance of an HTTP server" do @server.stubs(:http_server_class).returns(@mock_http_server_class) @mock_http_server_class.expects(:new).returns(@mock_http_server) @server.listen end it "should cause the HTTP server to listen" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen) @server.listen end it "should pass the listening address to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:address] == '127.0.0.1' end @server.listen end it "should pass the listening port to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:port] == 31337 end @server.listen end it "should pass a list of REST handlers to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:handlers] == [ :node ] end @server.listen end it "should pass a list of XMLRPC handlers to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:xmlrpc_handlers] == [ :master ] end @server.listen end it "should pass a list of protocols to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:protocols] == [ :rest, :xmlrpc ] end @server.listen end end describe "when listening is being turned off" do before do @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @server.stubs(:http_server).returns(@mock_http_server) @server.listen end it "should cause the HTTP server to stop listening" do @mock_http_server.expects(:unlisten) @server.unlisten end it "should not allow for indirections to be turned off" do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') @server.register(:foo) lambda { @server.unregister(:foo) }.should raise_error(RuntimeError) end end end diff --git a/spec/unit/network/xmlrpc/client_spec.rb b/spec/unit/network/xmlrpc/client_spec.rb index 7498ca4cf..b9be0a906 100755 --- a/spec/unit/network/xmlrpc/client_spec.rb +++ b/spec/unit/network/xmlrpc/client_spec.rb @@ -1,172 +1,172 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec require 'puppet/network/client' -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' describe Puppet::Network::XMLRPCClient do describe "when performing the rpc call" do before do Puppet::SSL::Host.any_instance.stubs(:certificate_matches_key?).returns true @client = Puppet::Network::Client.report.xmlrpc_client.new @client.stubs(:call).returns "foo" end it "should call the specified namespace and method, with the specified arguments" do @client.expects(:call).with("puppetreports.report", "eh").returns "foo" @client.report("eh") end it "should return the results from the call" do @client.expects(:call).returns "foo" @client.report("eh").should == "foo" end it "should always close the http connection if it is still open after the call" do http = mock 'http' @client.stubs(:http).returns http http.expects(:started?).returns true http.expects(:finish) @client.report("eh").should == "foo" end it "should always close the http connection if it is still open after a call that raises an exception" do http = mock 'http' @client.stubs(:http).returns http @client.expects(:call).raises RuntimeError http.expects(:started?).returns true http.expects(:finish) lambda { @client.report("eh") }.should raise_error end describe "when returning the http instance" do it "should use the http pool to create the instance" do @client.instance_variable_set("@http", nil) @client.expects(:host).returns "myhost" @client.expects(:port).returns "myport" Puppet::Network::HttpPool.expects(:http_instance).with("myhost", "myport", true).returns "http" @client.http.should == "http" end it "should reuse existing instances" do @client.http.should equal(@client.http) end end describe "when recycling the connection" do it "should close the existing instance if it's open" do http = mock 'http' @client.stubs(:http).returns http http.expects(:started?).returns true http.expects(:finish) @client.recycle_connection end it "should force creation of a new instance" do Puppet::Network::HttpPool.expects(:http_instance).returns "second_http" @client.recycle_connection @client.http.should == "second_http" end end describe "and an exception is raised" do it "should raise XMLRPCClientError if XMLRPC::FaultException is raised" do error = XMLRPC::FaultException.new("foo", "bar") @client.expects(:call).raises(error) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end it "should raise XMLRPCClientError if Errno::ECONNREFUSED is raised" do @client.expects(:call).raises(Errno::ECONNREFUSED) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end it "should log and raise XMLRPCClientError if Timeout::Error is raised" do Puppet.expects(:err) @client.expects(:call).raises(Timeout::Error) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end it "should log and raise XMLRPCClientError if SocketError is raised" do Puppet.expects(:err) @client.expects(:call).raises(SocketError) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end it "should log, recycle the connection, and retry if Errno::EPIPE is raised" do @client.expects(:call).times(2).raises(Errno::EPIPE).then.returns "eh" Puppet.expects(:info) @client.expects(:recycle_connection) @client.report("eh") end it "should log, recycle the connection, and retry if EOFError is raised" do @client.expects(:call).times(2).raises(EOFError).then.returns "eh" Puppet.expects(:info) @client.expects(:recycle_connection) @client.report("eh") end it "should log and retry if an exception containing 'Wrong size' is raised" do error = RuntimeError.new("Wrong size. Was 15, should be 30") @client.expects(:call).times(2).raises(error).then.returns "eh" Puppet.expects(:warning) @client.report("eh") end it "should raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised" do @client.expects(:call).raises(OpenSSL::SSL::SSLError) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end it "should log and raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised with certificate issues" do error = OpenSSL::SSL::SSLError.new("hostname was not match") @client.expects(:call).raises(error) Puppet.expects(:warning) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end it "should log, recycle the connection, and retry if OpenSSL::SSL::SSLError is raised containing 'bad write retry'" do error = OpenSSL::SSL::SSLError.new("bad write retry") @client.expects(:call).times(2).raises(error).then.returns "eh" @client.expects(:recycle_connection) Puppet.expects(:warning) @client.report("eh") end it "should log and raise XMLRPCClientError if any other exception is raised" do @client.expects(:call).raises(RuntimeError) Puppet.expects(:err) lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError) end end end end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index d34bdb000..d1badfa3a 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -1,335 +1,334 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node/environment' require 'puppet/util/execution' describe Puppet::Node::Environment do include PuppetSpec::Files after do Puppet::Node::Environment.clear end it "should include the Cacher module" do Puppet::Node::Environment.ancestors.should be_include(Puppet::Util::Cacher) end it "should use the filetimeout for the ttl for the modulepath" do Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the module list" do Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the manifestdir" do Puppet::Node::Environment.attr_ttl(:manifestdir).should == Integer(Puppet[:filetimeout]) end it "should use the default environment if no name is provided while initializing an environment" do Puppet.settings.expects(:value).with(:environment).returns("one") Puppet::Node::Environment.new.name.should == :one end it "should treat environment instances as singletons" do Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) end it "should treat an environment specified as names or strings as equivalent" do Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) end it "should return its name when converted to a string" do Puppet::Node::Environment.new(:one).to_s.should == "one" end it "should just return any provided environment if an environment is provided as the name" do one = Puppet::Node::Environment.new(:one) Puppet::Node::Environment.new(one).should equal(one) end describe "when managing known resource types" do before do @env = Puppet::Node::Environment.new("dev") @collection = Puppet::Resource::TypeCollection.new(@env) @env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) Thread.current[:known_resource_types] = nil end it "should create a resource type collection if none exists" do Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection @env.known_resource_types.should equal(@collection) end it "should reuse any existing resource type collection" do @env.known_resource_types.should equal(@env.known_resource_types) end it "should perform the initial import when creating a new collection" do @env = Puppet::Node::Environment.new("dev") @env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) @env.known_resource_types end it "should return the same collection even if stale if it's the same thread" do Puppet::Resource::TypeCollection.stubs(:new).returns @collection @env.known_resource_types.stubs(:stale?).returns true @env.known_resource_types.should equal(@collection) end it "should return the current thread associated collection if there is one" do Thread.current[:known_resource_types] = @collection @env.known_resource_types.should equal(@collection) end it "should give to all threads using the same environment the same collection if the collection isn't stale" do original_thread_type_collection = Puppet::Resource::TypeCollection.new(@env) Puppet::Resource::TypeCollection.expects(:new).with(@env).returns original_thread_type_collection @env.known_resource_types.should equal(original_thread_type_collection) original_thread_type_collection.expects(:require_reparse?).returns(false) Puppet::Resource::TypeCollection.stubs(:new).with(@env).returns @collection t = Thread.new { @env.known_resource_types.should equal(original_thread_type_collection) } t.join end it "should generate a new TypeCollection if the current one requires reparsing" do old_type_collection = @env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true Thread.current[:known_resource_types] = nil new_type_collection = @env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) end end [:modulepath, :manifestdir].each do |setting| it "should validate the #{setting} directories" do path = %w{/one /two}.join(File::PATH_SEPARATOR) env = Puppet::Node::Environment.new("testing") env.stubs(:[]).with(setting).returns path env.expects(:validate_dirs).with(%w{/one /two}) env.send(setting) end it "should return the validated dirs for #{setting}" do path = %w{/one /two}.join(File::PATH_SEPARATOR) env = Puppet::Node::Environment.new("testing") env.stubs(:[]).with(setting).returns path env.stubs(:validate_dirs).returns %w{/one /two} env.send(setting).should == %w{/one /two} end end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do env = Puppet::Node::Environment.new("testing") module_path = %w{/one /two}.join(File::PATH_SEPARATOR) env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} env.expects(:[]).with(:modulepath).returns module_path env.modulepath.should == %w{/l1 /l2 /one /two} end end describe "when validating modulepath or manifestdir directories" do it "should not return non-directories" do env = Puppet::Node::Environment.new("testing") FileTest.expects(:directory?).with("/one").returns true FileTest.expects(:directory?).with("/two").returns false env.validate_dirs(%w{/one /two}).should == %w{/one} end it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true env = Puppet::Node::Environment.new("testing") two = File.join(Dir.getwd, "two") env.validate_dirs(%w{/one two}).should == ["/one", two] end end describe "when modeling a specific environment" do it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing end it "should provide an array-like accessor method for returning any environment-specific setting" do env = Puppet::Node::Environment.new("testing") env.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") env = Puppet::Node::Environment.new("testing") env["myvar"].should == "myval" end it "should be able to return an individual module that exists in its module path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns true env.module("one").should equal(mod) end it "should return nil if asked for a module that does not exist in its path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns false env.module("one").should be_nil end it "should be able to return its modules" do Puppet::Node::Environment.new("testing").should respond_to(:modules) end describe ".modules" do it "should return a module named for every directory in each module path" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a /b} Dir.expects(:entries).with("/a").returns %w{foo bar} Dir.expects(:entries).with("/b").returns %w{bee baz} env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort end it "should remove duplicates" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).returns( %w{/a /b} ).at_least_once Dir.expects(:entries).with("/a").returns %w{foo} Dir.expects(:entries).with("/b").returns %w{foo} env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should ignore invalid modules" do env = Puppet::Node::Environment.new("testing") env.stubs(:modulepath).returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo bar} Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" ) env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should create modules with the correct environment" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo} env.modules.each {|mod| mod.environment.should == env } end it "should cache the module list" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).once.with("/a").returns %w{foo} env.modules env.modules end end end describe Puppet::Node::Environment::Helper do before do @helper = Object.new @helper.extend(Puppet::Node::Environment::Helper) end it "should be able to set and retrieve the environment" do @helper.environment = :foo @helper.environment.name.should == :foo end it "should accept an environment directly" do env = Puppet::Node::Environment.new :foo @helper.environment = env @helper.environment.name.should == :foo end it "should accept an environment as a string" do env = Puppet::Node::Environment.new "foo" @helper.environment = env @helper.environment.name.should == :foo end end describe "when performing initial import" do before do @parser = Puppet::Parser::Parser.new("test") Puppet::Parser::Parser.stubs(:new).returns @parser @env = Puppet::Node::Environment.new("env") end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet.settings[:code] = "my code" @parser.expects(:string=).with "my code" @parser.expects(:parse) @env.instance_eval { perform_initial_import } end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do filename = tmpfile('myfile') File.open(filename, 'w'){|f| } Puppet.settings[:manifest] = filename @parser.expects(:file=).with filename @parser.expects(:parse) @env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do filename = tmpfile('myfile') Puppet.settings[:code] = "" Puppet.settings[:manifest] = filename @parser.expects(:file=).with(filename).once @parser.expects(:parse).once @env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do File.stubs(:exist?).returns true @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) @parser.expects(:file=).once @parser.expects(:parse).raises ArgumentError lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do Puppet.settings[:ignoreimport] = true @parser.expects(:string=).never @parser.expects(:file=).never @parser.expects(:parse).never @env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) @env.known_resource_types.require_reparse?.should be_true end end end diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb index 797009ae4..a130ae3f8 100755 --- a/spec/unit/node/facts_spec.rb +++ b/spec/unit/node/facts_spec.rb @@ -1,131 +1,130 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/node/facts' describe Puppet::Node::Facts, "when indirecting" do before do @facts = Puppet::Node::Facts.new("me") end it "should be able to convert all fact values to strings" do @facts.values["one"] = 1 @facts.stringify @facts.values["one"].should == "1" end it "should add the node's certificate name as the 'clientcert' fact when adding local facts" do @facts.add_local_facts @facts.values["clientcert"].should == Puppet.settings[:certname] end it "should add the Puppet version as a 'clientversion' fact when adding local facts" do @facts.add_local_facts @facts.values["clientversion"].should == Puppet.version.to_s end it "should add the current environment as a fact if one is not set when adding local facts" do @facts.add_local_facts @facts.values["environment"].should == Puppet[:environment] end it "should not replace any existing environment fact when adding local facts" do @facts.values["environment"] = "foo" @facts.add_local_facts @facts.values["environment"].should == "foo" end it "should be able to downcase fact values" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:downcasefacts).returns true @facts.values["one"] = "Two" @facts.downcase_if_necessary @facts.values["one"].should == "two" end it "should only try to downcase strings" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:downcasefacts).returns true @facts.values["now"] = Time.now @facts.downcase_if_necessary @facts.values["now"].should be_instance_of(Time) end it "should not downcase facts if not configured to do so" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:downcasefacts).returns false @facts.values["one"] = "Two" @facts.downcase_if_necessary @facts.values["one"].should == "Two" end describe "when indirecting" do before do @indirection = stub 'indirection', :request => mock('request'), :name => :facts # We have to clear the cache so that the facts ask for our indirection stub, # instead of anything that might be cached. Puppet::Util::Cacher.expire @facts = Puppet::Node::Facts.new("me", "one" => "two") end it "should redirect to the specified fact store for storage" do Puppet::Node::Facts.stubs(:indirection).returns(@indirection) @indirection.expects(:save) Puppet::Node::Facts.indirection.save(@facts) end describe "when the Puppet application is 'master'" do it "should default to the 'yaml' terminus" do pending "Cannot test the behavior of defaults in defaults.rb" # Puppet::Node::Facts.indirection.terminus_class.should == :yaml end end describe "when the Puppet application is not 'master'" do it "should default to the 'facter' terminus" do pending "Cannot test the behavior of defaults in defaults.rb" # Puppet::Node::Facts.indirection.terminus_class.should == :facter end end end describe "when storing and retrieving" do it "should add metadata to the facts" do facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four") facts.values[:_timestamp].should be_instance_of(Time) end describe "using pson" do before :each do @timestamp = Time.parse("Thu Oct 28 11:16:31 -0700 2010") @expiration = Time.parse("Thu Oct 28 11:21:31 -0700 2010") end it "should accept properly formatted pson" do pson = %Q({"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}) format = Puppet::Network::FormatHandler.format('pson') facts = format.intern(Puppet::Node::Facts,pson) facts.name.should == 'foo' facts.expiration.should == @expiration facts.values.should == {'a' => '1', 'b' => '2', 'c' => '3', :_timestamp => @timestamp} end it "should generate properly formatted pson" do Time.stubs(:now).returns(@timestamp) facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) facts.expiration = @expiration pson = PSON.parse(facts.to_pson) pson.should == {"name"=>"foo", "timestamp"=>@timestamp.to_s, "expiration"=>@expiration.to_s, "values"=>{"a"=>1, "b"=>2, "c"=>3}} end end end end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index ec1b65ee1..7d813ba30 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1,203 +1,202 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Node do describe "when managing its environment" do it "should use any set environment" do Puppet::Node.new("foo", :environment => "bar").environment.name.should == :bar end it "should support providing an actual environment instance" do Puppet::Node.new("foo", :environment => Puppet::Node::Environment.new(:bar)).environment.name.should == :bar end it "should determine its environment from its parameters if no environment is set" do Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.name.should == :bar end it "should use the default environment if no environment is provided" do Puppet::Node.new("foo").environment.name.should == Puppet::Node::Environment.new.name end it "should always return an environment instance rather than a string" do Puppet::Node.new("foo").environment.should be_instance_of(Puppet::Node::Environment) end it "should allow the environment to be set after initialization" do node = Puppet::Node.new("foo") node.environment = :bar node.environment.name.should == :bar end it "should allow its environment to be set by parameters after initialization" do node = Puppet::Node.new("foo") node.parameters["environment"] = :bar node.environment.name.should == :bar end end end describe Puppet::Node, "when initializing" do before do @node = Puppet::Node.new("testnode") end it "should set the node name" do @node.name.should == "testnode" end it "should not allow nil node names" do proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError) end it "should default to an empty parameter hash" do @node.parameters.should == {} end it "should default to an empty class array" do @node.classes.should == [] end it "should note its creation time" do @node.time.should be_instance_of(Time) end it "should accept parameters passed in during initialization" do params = {"a" => "b"} @node = Puppet::Node.new("testing", :parameters => params) @node.parameters.should == params end it "should accept classes passed in during initialization" do classes = %w{one two} @node = Puppet::Node.new("testing", :classes => classes) @node.classes.should == classes end it "should always return classes as an array" do @node = Puppet::Node.new("testing", :classes => "myclass") @node.classes.should == ["myclass"] end end describe Puppet::Node, "when merging facts" do before do @node = Puppet::Node.new("testnode") Puppet::Node::Facts.indirection.stubs(:find).with(@node.name).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b")) end it "should fail intelligently if it cannot find facts" do Puppet::Node::Facts.indirection.expects(:find).with(@node.name).raises "foo" lambda { @node.fact_merge }.should raise_error(Puppet::Error) end it "should prefer parameters already set on the node over facts from the node" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["one"].should == "a" end it "should add passed parameters to the parameter list" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["two"].should == "b" end it "should accept arbitrary parameters to merge into its parameters" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.merge "two" => "three" @node.parameters["two"].should == "three" end it "should add the environment to the list of parameters" do Puppet.settings.stubs(:value).with(:environments).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "two" => "three" @node.parameters["environment"].should == "one" end it "should not set the environment if it is already set in the parameters" do Puppet.settings.stubs(:value).with(:environments).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "environment" => "two" @node.parameters["environment"].should == "two" end end describe Puppet::Node, "when indirecting" do it "should default to the 'plain' node terminus" do Puppet::Node.indirection.terminus_class.should == :plain end it "should not have a cache class defined" do Puppet::Node.indirection.cache_class.should be_nil end after do Puppet::Util::Cacher.expire end end describe Puppet::Node, "when generating the list of names to search through" do before do @node = Puppet::Node.new("foo.domain.com", :parameters => {"hostname" => "yay", "domain" => "domain.com"}) end it "should return an array of names" do @node.names.should be_instance_of(Array) end describe "and the node name is fully qualified" do it "should contain an entry for each part of the node name" do @node.names.should be_include("foo.domain.com") @node.names.should be_include("foo.domain") @node.names.should be_include("foo") end end it "should include the node's fqdn" do @node.names.should be_include("yay.domain.com") end it "should combine and include the node's hostname and domain if no fqdn is available" do @node.names.should be_include("yay.domain.com") end it "should contain an entry for each name available by stripping a segment of the fqdn" do @node.parameters["fqdn"] = "foo.deep.sub.domain.com" @node.names.should be_include("foo.deep.sub.domain") @node.names.should be_include("foo.deep.sub") end describe "and :node_name is set to 'cert'" do before do Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false Puppet.settings.stubs(:value).with(:node_name).returns "cert" end it "should use the passed-in key as the first value" do @node.names[0].should == "foo.domain.com" end describe "and strict hostname checking is enabled" do it "should only use the passed-in key" do Puppet.settings.expects(:value).with(:strict_hostname_checking).returns true @node.names.should == ["foo.domain.com"] end end end describe "and :node_name is set to 'facter'" do before do Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false Puppet.settings.stubs(:value).with(:node_name).returns "facter" end it "should use the node's 'hostname' fact as the first value" do @node.names[0].should == "yay" end end end diff --git a/spec/unit/other/selinux_spec.rb b/spec/unit/other/selinux_spec.rb old mode 100644 new mode 100755 index 7f908885f..216feaf1f --- a/spec/unit/other/selinux_spec.rb +++ b/spec/unit/other/selinux_spec.rb @@ -1,94 +1,93 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/type/selboolean' require 'puppet/type/selmodule' describe Puppet::Type.type(:file), " when manipulating file contexts" do before :each do @file = Puppet::Type::File.new( :name => "/tmp/foo", :ensure => "file", :seluser => "user_u", :selrole => "role_r", :seltype => "type_t" ) end it "should use :seluser to get/set an SELinux user file context attribute" do @file.property(:seluser).should == "user_u" end it "should use :selrole to get/set an SELinux role file context attribute" do @file.property(:selrole).should == "role_r" end it "should use :seltype to get/set an SELinux user file context attribute" do @file.property(:seltype).should == "type_t" end end describe Puppet::Type.type(:selboolean), " when manipulating booleans" do before :each do provider_class = Puppet::Type::Selboolean.provider(Puppet::Type::Selboolean.providers[0]) Puppet::Type::Selboolean.stubs(:defaultprovider).returns provider_class @bool = Puppet::Type::Selboolean.new( :name => "foo", :value => "on", :persistent => true ) end it "should be able to access :name" do @bool[:name].should == "foo" end it "should be able to access :value" do @bool.property(:value).should == :on end it "should set :value to off" do @bool[:value] = :off @bool.property(:value).should == :off end it "should be able to access :persistent" do @bool[:persistent].should == :true end it "should set :persistent to false" do @bool[:persistent] = false @bool[:persistent].should == :false end end describe Puppet::Type.type(:selmodule), " when checking policy modules" do before :each do provider_class = Puppet::Type::Selmodule.provider(Puppet::Type::Selmodule.providers[0]) Puppet::Type::Selmodule.stubs(:defaultprovider).returns provider_class @module = Puppet::Type::Selmodule.new( :name => "foo", :selmoduledir => "/some/path", :selmodulepath => "/some/path/foo.pp", :syncversion => true) end it "should be able to access :name" do @module[:name].should == "foo" end it "should be able to access :selmoduledir" do @module[:selmoduledir].should == "/some/path" end it "should be able to access :selmodulepath" do @module[:selmodulepath].should == "/some/path/foo.pp" end it "should be able to access :syncversion" do @module.property(:syncversion).should == :true end it "should set the syncversion value to false" do @module[:syncversion] = :false @module.property(:syncversion).should == :false end end diff --git a/spec/unit/other/transbucket_spec.rb b/spec/unit/other/transbucket_spec.rb index 6bc257897..b95a4abca 100755 --- a/spec/unit/other/transbucket_spec.rb +++ b/spec/unit/other/transbucket_spec.rb @@ -1,188 +1,187 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::TransBucket do before do @bucket = Puppet::TransBucket.new end it "should be able to produce a RAL component" do @bucket.name = "luke" @bucket.type = "foo" resource = nil proc { resource = @bucket.to_ral }.should_not raise_error resource.should be_instance_of(Puppet::Type::Component) resource.title.should == "Foo[luke]" end it "should accept TransObjects into its children list" do object = Puppet::TransObject.new("luke", "user") proc { @bucket.push(object) }.should_not raise_error @bucket.each do |o| o.should equal(object) end end it "should accept TransBuckets into its children list" do object = Puppet::TransBucket.new proc { @bucket.push(object) }.should_not raise_error @bucket.each do |o| o.should equal(object) end end it "should refuse to accept any children that are not TransObjects or TransBuckets" do proc { @bucket.push "a test" }.should raise_error end it "should return use 'node' as the type and the provided name as the title if only a type is provided" do @bucket.type = "mystuff" @bucket.to_ref.should == "Node[mystuff]" end it "should return use 'component' as the type and the provided type as the title if only a name is provided" do @bucket.name = "mystuff" @bucket.to_ref.should == "Class[Mystuff]" end it "should return nil as its reference when type and name are missing" do @bucket.to_ref.should be_nil end it "should return the title as its reference" do @bucket.name = "luke" @bucket.type = "user" @bucket.to_ref.should == "User[luke]" end it "should canonize resource references when the type is 'component'" do @bucket.name = 'something' @bucket.type = 'foo::bar' @bucket.to_ref.should == "Foo::Bar[something]" end end describe Puppet::TransBucket, " when generating a catalog" do before do @bottom = Puppet::TransBucket.new @bottom.type = "fake" @bottom.name = "bottom" @bottomobj = Puppet::TransObject.new("bottom", "notify") @bottom.push @bottomobj @middle = Puppet::TransBucket.new @middle.type = "fake" @middle.name = "middle" @middleobj = Puppet::TransObject.new("middle", "notify") @middle.push(@middleobj) @middle.push(@bottom) @top = Puppet::TransBucket.new @top.type = "fake" @top.name = "top" @topobj = Puppet::TransObject.new("top", "notify") @top.push(@topobj) @top.push(@middle) @users = %w{top middle bottom} @fakes = %w{Fake[bottom] Fake[middle] Fake[top]} end it "should convert all transportable objects to RAL resources" do @catalog = @top.to_catalog @users.each do |name| @catalog.vertices.find { |r| r.class.name == :notify and r.title == name }.should be_instance_of(Puppet::Type.type(:notify)) end end it "should fail if any transportable resources fail to convert to RAL resources" do @bottomobj.expects(:to_ral).raises ArgumentError lambda { @bottom.to_catalog }.should raise_error(ArgumentError) end it "should convert all transportable buckets to RAL components" do @catalog = @top.to_catalog @fakes.each do |name| @catalog.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component)) end end it "should add all resources to the graph's resource table" do @catalog = @top.to_catalog @catalog.resource("fake[top]").should equal(@top) end it "should finalize all resources" do @catalog = @top.to_catalog @catalog.vertices.each do |vertex| vertex.should be_finalized end end it "should only call to_ral on each resource once" do # We just raise exceptions here because we're not interested in # what happens with the result, only that the method only # gets called once. resource = @topobj.to_ral @topobj.expects(:to_ral).once.returns resource @top.to_catalog end it "should set each TransObject's catalog before converting to a RAL resource" do @middleobj.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) } @top.to_catalog end it "should set each TransBucket's catalog before converting to a RAL resource" do # each bucket is seen twice in the loop, so we have to handle the case where the config # is set twice @bottom.expects(:catalog=).with { |c| c.is_a?(Puppet::Resource::Catalog) }.at_least_once @top.to_catalog end end describe Puppet::TransBucket, " when serializing" do before do @bucket = Puppet::TransBucket.new(%w{one two}) @bucket.name = "one" @bucket.type = "two" end it "should be able to be dumped to yaml" do proc { YAML.dump(@bucket) }.should_not raise_error end it "should dump YAML that produces an equivalent object" do result = YAML.dump(@bucket) newobj = YAML.load(result) newobj.name.should == "one" newobj.type.should == "two" children = [] newobj.each do |o| children << o end children.should == %w{one two} end end describe Puppet::TransBucket, " when converting to a Puppet::Resource" do before do @trans = Puppet::TransBucket.new @trans.name = "foo" @trans.type = "bar" @trans.param(:noop, true) end it "should create a resource with the correct type and title" do result = @trans.to_resource result.type.should == "Bar" result.title.should == "foo" end it "should add all of its parameters to the created resource" do @trans.param(:noop, true) @trans.to_resource[:noop].should be_true end end diff --git a/spec/unit/other/transobject_spec.rb b/spec/unit/other/transobject_spec.rb index c2652a386..4715e2fa1 100755 --- a/spec/unit/other/transobject_spec.rb +++ b/spec/unit/other/transobject_spec.rb @@ -1,112 +1,111 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transportable' describe Puppet::TransObject do it "should canonize resource references" do resource = Puppet::TransObject.new("me", "foo::bar") resource.ref.should == 'Foo::Bar[me]' end it "should lower-case resource types for backward compatibility with 0.23.2" do resource = Puppet::TransObject.new("me", "Foo") resource.type.should == 'foo' end end describe Puppet::TransObject, " when serializing" do before do @resource = Puppet::TransObject.new("/my/file", "file") @resource["one"] = "test" @resource["two"] = "other" end it "should be able to be dumped to yaml" do proc { YAML.dump(@resource) }.should_not raise_error end it "should produce an equivalent yaml object" do text = YAML.dump(@resource) newresource = YAML.load(text) newresource.name.should == "/my/file" newresource.type.should == "file" %w{one two}.each do |param| newresource[param].should == @resource[param] end end end describe Puppet::TransObject, " when converting to a Puppet::Resource" do before do @trans = Puppet::TransObject.new("/my/file", "file") @trans["one"] = "test" @trans["two"] = "other" end it "should create a resource with the correct type and title" do result = @trans.to_resource result.type.should == "File" result.title.should == "/my/file" end it "should add all of its parameters to the created resource" do @trans[:noop] = true @trans.to_resource[:noop].should be_true end it "should copy over the tags" do @trans.tags = %w{foo bar} result = @trans.to_resource result.should be_tagged("foo") result.should be_tagged("bar") end end describe Puppet::TransObject, " when converting to a RAL resource" do before do @resource = Puppet::TransObject.new("/my/file", "file") @resource["one"] = "test" @resource["two"] = "other" end it "should use a Puppet::Resource to create the resource" do resource = mock 'resource' @resource.expects(:to_resource).returns resource resource.expects(:to_ral).returns "myral" @resource.to_ral.should == "myral" end end describe Puppet::TransObject, " when converting to a RAL component instance" do before do @resource = Puppet::TransObject.new("/my/file", "one::two") @resource["one"] = "test" @resource["noop"] = "other" end it "should use a new TransObject whose name is a resource reference of the type and title of the original TransObject" do Puppet::Type::Component.expects(:new).with { |resource| resource.type == "component" and resource.name == "One::Two[/my/file]" }.returns(:yay) @resource.to_component.should == :yay end it "should pass the resource parameters on to the newly created TransObject" do Puppet::Type::Component.expects(:new).with { |resource| resource["noop"] == "other" }.returns(:yay) @resource.to_component.should == :yay end it "should copy over the catalog" do @resource.catalog = "mycat" Puppet::Type::Component.expects(:new).with { |resource| resource.catalog == "mycat" }.returns(:yay) @resource.to_component end # LAK:FIXME This really isn't the design we want going forward, but it's # good enough for now. it "should not pass resource parameters that are not metaparams" do Puppet::Type::Component.expects(:new).with { |resource| resource["one"].nil? }.returns(:yay) @resource.to_component.should == :yay end end diff --git a/spec/unit/parameter/path_spec.rb b/spec/unit/parameter/path_spec.rb old mode 100644 new mode 100755 index 08a26de33..d113a1581 --- a/spec/unit/parameter/path_spec.rb +++ b/spec/unit/parameter/path_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env ruby -require File.expand_path(File.join(File.dirname(__FILE__), '../../spec_helper')) +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parameter/path' [false, true].each do |arrays| describe "Puppet::Parameter::Path with arrays #{arrays}" do it_should_behave_like "all path parameters", :path, :array => arrays do # The new type allows us a test that is guaranteed to go direct to our # validation code, without passing through any "real type" overrides or # whatever on the way. Puppet::newtype(:test_puppet_parameter_path) do newparam(:path, :parent => Puppet::Parameter::Path, :arrays => arrays) do isnamevar accept_arrays arrays end end def instance(path) Puppet::Type.type(:test_puppet_parameter_path).new(:path => path) end end end end diff --git a/spec/unit/parameter/value_collection_spec.rb b/spec/unit/parameter/value_collection_spec.rb index 99e4302bc..af70160c1 100755 --- a/spec/unit/parameter/value_collection_spec.rb +++ b/spec/unit/parameter/value_collection_spec.rb @@ -1,167 +1,166 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parameter' describe Puppet::Parameter::ValueCollection do before do @collection = Puppet::Parameter::ValueCollection.new end it "should have a method for defining new values" do @collection.should respond_to(:newvalues) end it "should have a method for adding individual values" do @collection.should respond_to(:newvalue) end it "should be able to retrieve individual values" do value = @collection.newvalue(:foo) @collection.value(:foo).should equal(value) end it "should be able to add an individual value with a block" do @collection.newvalue(:foo) { raise "testing" } @collection.value(:foo).block.should be_instance_of(Proc) end it "should be able to add values that are empty strings" do lambda { @collection.newvalue('') }.should_not raise_error end it "should be able to add values that are empty strings" do value = @collection.newvalue('') @collection.match?('').should equal(value) end it "should set :call to :none when adding a value with no block" do value = @collection.newvalue(:foo) value.call.should == :none end describe "when adding a value with a block" do it "should set the method name to 'set_' plus the value name" do value = @collection.newvalue(:myval) { raise "testing" } value.method.should == "set_myval" end end it "should be able to add an individual value with options" do value = @collection.newvalue(:foo, :call => :bar) value.call.should == :bar end it "should have a method for validating a value" do @collection.should respond_to(:validate) end it "should have a method for munging a value" do @collection.should respond_to(:munge) end it "should be able to generate documentation when it has both values and regexes" do @collection.newvalues :foo, "bar", %r{test} @collection.doc.should be_instance_of(String) end it "should correctly generate documentation for values" do @collection.newvalues :foo @collection.doc.should be_include("Valid values are `foo`") end it "should correctly generate documentation for regexes" do @collection.newvalues %r{\w+} @collection.doc.should be_include("Values can match `/\\w+/`") end it "should be able to find the first matching value" do @collection.newvalues :foo, :bar @collection.match?("foo").should be_instance_of(Puppet::Parameter::Value) end it "should be able to match symbols" do @collection.newvalues :foo, :bar @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) end it "should be able to match symbols when a regex is provided" do @collection.newvalues %r{.} @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) end it "should be able to match values using regexes" do @collection.newvalues %r{.} @collection.match?("foo").should_not be_nil end it "should prefer value matches to regex matches" do @collection.newvalues %r{.}, :foo @collection.match?("foo").name.should == :foo end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do @collection.validate("foo") end it "should fail if the value is not a defined value or alias and does not match a regex" do @collection.newvalues :foo lambda { @collection.validate("bar") }.should raise_error(ArgumentError) end it "should succeed if the value is one of the defined values" do @collection.newvalues :foo lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do @collection.newvalues :foo lambda { @collection.validate("foo") }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do @collection.newvalues "foo" lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined aliases" do @collection.newvalues :foo @collection.aliasvalue :bar, :foo lambda { @collection.validate("bar") }.should_not raise_error(ArgumentError) end it "should succeed if the value matches one of the regexes" do @collection.newvalues %r{\d} lambda { @collection.validate("10") }.should_not raise_error(ArgumentError) end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do @collection.munge("foo").should == "foo" end it "should return return any matching defined values" do @collection.newvalues :foo, :bar @collection.munge("foo").should == :foo end it "should return any matching aliases" do @collection.newvalues :foo @collection.aliasvalue :bar, :foo @collection.munge("bar").should == :foo end it "should return the value if it matches a regex" do @collection.newvalues %r{\w} @collection.munge("bar").should == "bar" end it "should return the value if no other option is matched" do @collection.newvalues :foo @collection.munge("bar").should == "bar" end end end diff --git a/spec/unit/parameter/value_spec.rb b/spec/unit/parameter/value_spec.rb index cb44770b4..f3414e4e0 100755 --- a/spec/unit/parameter/value_spec.rb +++ b/spec/unit/parameter/value_spec.rb @@ -1,88 +1,87 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parameter' describe Puppet::Parameter::Value do it "should require a name" do lambda { Puppet::Parameter::Value.new }.should raise_error(ArgumentError) end it "should set its name" do Puppet::Parameter::Value.new(:foo).name.should == :foo end it "should support regexes as names" do lambda { Puppet::Parameter::Value.new(%r{foo}) }.should_not raise_error end it "should mark itself as a regex if its name is a regex" do Puppet::Parameter::Value.new(%r{foo}).should be_regex end it "should always convert its name to a symbol if it is not a regex" do Puppet::Parameter::Value.new("foo").name.should == :foo Puppet::Parameter::Value.new(true).name.should == :true end it "should support adding aliases" do Puppet::Parameter::Value.new("foo").should respond_to(:alias) end it "should be able to return its aliases" do value = Puppet::Parameter::Value.new("foo") value.alias("bar") value.alias("baz") value.aliases.should == [:bar, :baz] end [:block, :call, :method, :event, :required_features].each do |attr| it "should support a #{attr} attribute" do value = Puppet::Parameter::Value.new("foo") value.should respond_to(attr.to_s + "=") value.should respond_to(attr) end end it "should default to :instead for :call if a block is provided" do Puppet::Parameter::Value.new("foo").call.should == :instead end it "should always return events as symbols" do value = Puppet::Parameter::Value.new("foo") value.event = "foo_test" value.event.should == :foo_test end describe "when matching" do describe "a regex" do it "should return true if the regex matches the value" do Puppet::Parameter::Value.new(/\w/).should be_match("foo") end it "should return false if the regex does not match the value" do Puppet::Parameter::Value.new(/\d/).should_not be_match("foo") end end describe "a non-regex" do it "should return true if the value, converted to a symbol, matches the name" do Puppet::Parameter::Value.new("foo").should be_match("foo") Puppet::Parameter::Value.new(:foo).should be_match(:foo) Puppet::Parameter::Value.new(:foo).should be_match("foo") Puppet::Parameter::Value.new("foo").should be_match(:foo) end it "should return false if the value, converted to a symbol, does not match the name" do Puppet::Parameter::Value.new(:foo).should_not be_match(:bar) end it "should return true if any of its aliases match" do value = Puppet::Parameter::Value.new("foo") value.alias("bar") value.should be_match("bar") end end end end diff --git a/spec/unit/parameter_spec.rb b/spec/unit/parameter_spec.rb index c9ef32faf..04556c013 100755 --- a/spec/unit/parameter_spec.rb +++ b/spec/unit/parameter_spec.rb @@ -1,171 +1,170 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parameter' describe Puppet::Parameter do before do @class = Class.new(Puppet::Parameter) do @name = :foo end @class.initvars @resource = mock 'resource' @resource.stub_everything @parameter = @class.new :resource => @resource end it "should create a value collection" do @class = Class.new(Puppet::Parameter) @class.value_collection.should be_nil @class.initvars @class.value_collection.should be_instance_of(Puppet::Parameter::ValueCollection) end it "should return its name as a string when converted to a string" do @parameter.to_s.should == @parameter.name.to_s end it "should be able to use cached attributes" do Puppet::Parameter.ancestors.should be_include(Puppet::Util::Cacher) end it "should use the resource catalog for expiration" do catalog = mock 'catalog' @resource.stubs(:catalog).returns catalog @parameter.expirer.should equal(catalog) end [:line, :file, :version].each do |data| it "should return its resource's #{data} as its #{data}" do @resource.expects(data).returns "foo" @parameter.send(data).should == "foo" end end it "should return the resource's tags plus its name as its tags" do @resource.expects(:tags).returns %w{one two} @parameter.tags.should == %w{one two foo} end it "should provide source_descriptors" do @resource.expects(:line).returns 10 @resource.expects(:file).returns "file" @resource.expects(:tags).returns %w{one two} @parameter.source_descriptors.should == {:tags=>["one", "two", "foo"], :path=>"//foo", :file => "file", :line => 10} end describe "when returning the value" do it "should return nil if no value is set" do @parameter.value.should be_nil end it "should validate the value" do @parameter.expects(:validate).with("foo") @parameter.value = "foo" end it "should munge the value and use any result as the actual value" do @parameter.expects(:munge).with("foo").returns "bar" @parameter.value = "foo" @parameter.value.should == "bar" end it "should unmunge the value when accessing the actual value" do @parameter.class.unmunge do |value| value.to_sym end @parameter.value = "foo" @parameter.value.should == :foo end it "should return the actual value by default when unmunging" do @parameter.unmunge("bar").should == "bar" end it "should return any set value" do @parameter.value = "foo" @parameter.value.should == "foo" end end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do @parameter.validate("foo") end it "should catch abnormal failures thrown during validation" do @class.validate { |v| raise "This is broken" } lambda { @parameter.validate("eh") }.should raise_error(Puppet::DevError) end it "should fail if the value is not a defined value or alias and does not match a regex" do @class.newvalues :foo lambda { @parameter.validate("bar") }.should raise_error(Puppet::Error) end it "should succeed if the value is one of the defined values" do @class.newvalues :foo lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do @class.newvalues :foo lambda { @parameter.validate("foo") }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do @class.newvalues "foo" lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined aliases" do @class.newvalues :foo @class.aliasvalue :bar, :foo lambda { @parameter.validate("bar") }.should_not raise_error(ArgumentError) end it "should succeed if the value matches one of the regexes" do @class.newvalues %r{\d} lambda { @parameter.validate("10") }.should_not raise_error(ArgumentError) end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do @parameter.munge("foo").should == "foo" end it "should catch abnormal failures thrown during munging" do @class.munge { |v| raise "This is broken" } lambda { @parameter.munge("eh") }.should raise_error(Puppet::DevError) end it "should return return any matching defined values" do @class.newvalues :foo, :bar @parameter.munge("foo").should == :foo end it "should return any matching aliases" do @class.newvalues :foo @class.aliasvalue :bar, :foo @parameter.munge("bar").should == :foo end it "should return the value if it matches a regex" do @class.newvalues %r{\w} @parameter.munge("bar").should == "bar" end it "should return the value if no other option is matched" do @class.newvalues :foo @parameter.munge("bar").should == "bar" end end describe "when logging" do it "should use its resource's log level and the provided message" do @resource.expects(:[]).with(:loglevel).returns :notice @parameter.expects(:send_log).with(:notice, "mymessage") @parameter.log "mymessage" end end end diff --git a/spec/unit/parser/ast/arithmetic_operator_spec.rb b/spec/unit/parser/ast/arithmetic_operator_spec.rb index 381c5c629..144ebd78c 100755 --- a/spec/unit/parser/ast/arithmetic_operator_spec.rb +++ b/spec/unit/parser/ast/arithmetic_operator_spec.rb @@ -1,73 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ArithmeticOperator do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new @one = stub 'lval', :safeevaluate => 1 @two = stub 'rval', :safeevaluate => 2 end it "should evaluate both branches" do lval = stub "lval" lval.expects(:safeevaluate).with(@scope).returns(1) rval = stub "rval" rval.expects(:safeevaluate).with(@scope).returns(2) operator = ast::ArithmeticOperator.new :rval => rval, :operator => "+", :lval => lval operator.evaluate(@scope) end it "should fail for an unknown operator" do lambda { operator = ast::ArithmeticOperator.new :lval => @one, :operator => "%", :rval => @two }.should raise_error end it "should call Puppet::Parser::Scope.number?" do Puppet::Parser::Scope.expects(:number?).with(1).returns(1) Puppet::Parser::Scope.expects(:number?).with(2).returns(2) ast::ArithmeticOperator.new(:lval => @one, :operator => "+", :rval => @two).evaluate(@scope) end %w{ + - * / << >>}.each do |op| it "should call ruby Numeric '#{op}'" do one = stub 'one' two = stub 'two' operator = ast::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two Puppet::Parser::Scope.stubs(:number?).with(1).returns(one) Puppet::Parser::Scope.stubs(:number?).with(2).returns(two) one.expects(:send).with(op,two) operator.evaluate(@scope) end end it "should work even with numbers embedded in strings" do two = stub 'two', :safeevaluate => "2" one = stub 'one', :safeevaluate => "1" operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one operator.evaluate(@scope).should == 3 end it "should work even with floats" do two = stub 'two', :safeevaluate => 2.53 one = stub 'one', :safeevaluate => 1.80 operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one operator.evaluate(@scope).should == 4.33 end - it "should work for variables too" do - @scope.expects(:lookupvar).with("one", false).returns(1) - @scope.expects(:lookupvar).with("two", false).returns(2) - one = ast::Variable.new( :value => "one" ) - two = ast::Variable.new( :value => "two" ) - - operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two - operator.evaluate(@scope).should == 3 - end - end diff --git a/spec/unit/parser/ast/astarray_spec.rb b/spec/unit/parser/ast/astarray_spec.rb index 01082b3f0..a3f56052a 100755 --- a/spec/unit/parser/ast/astarray_spec.rb +++ b/spec/unit/parser/ast/astarray_spec.rb @@ -1,55 +1,54 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ASTArray do before :each do @scope = Puppet::Parser::Scope.new end it "should have a [] accessor" do array = Puppet::Parser::AST::ASTArray.new :children => [] array.should respond_to(:[]) end it "should evaluate all its children" do item1 = stub "item1", :is_a? => true item2 = stub "item2", :is_a? => true item1.expects(:safeevaluate).with(@scope).returns(123) item2.expects(:safeevaluate).with(@scope).returns(246) operator = Puppet::Parser::AST::ASTArray.new :children => [item1,item2] operator.evaluate(@scope) end it "should not flatten children coming from children ASTArray" do item = Puppet::Parser::AST::String.new :value => 'foo' inner_array = Puppet::Parser::AST::ASTArray.new :children => [item, item] operator = Puppet::Parser::AST::ASTArray.new :children => [inner_array, inner_array] operator.evaluate(@scope).should == [['foo', 'foo'], ['foo', 'foo']] end it "should not flatten the results of children evaluation" do item = Puppet::Parser::AST::String.new :value => 'foo' item.stubs(:evaluate).returns(['foo']) operator = Puppet::Parser::AST::ASTArray.new :children => [item, item] operator.evaluate(@scope).should == [['foo'], ['foo']] end it "should discard nil results from children evaluation" do item1 = Puppet::Parser::AST::String.new :value => 'foo' item2 = Puppet::Parser::AST::String.new :value => 'foo' item2.stubs(:evaluate).returns(nil) operator = Puppet::Parser::AST::ASTArray.new :children => [item1, item2] operator.evaluate(@scope).should == ['foo'] end it "should return a valid string with to_s" do a = stub 'a', :is_a? => true, :to_s => "a" b = stub 'b', :is_a? => true, :to_s => "b" array = Puppet::Parser::AST::ASTArray.new :children => [a,b] array.to_s.should == "[a, b]" end end diff --git a/spec/unit/parser/ast/asthash_spec.rb b/spec/unit/parser/ast/asthash_spec.rb old mode 100644 new mode 100755 index 83f604545..d7fbbfae9 --- a/spec/unit/parser/ast/asthash_spec.rb +++ b/spec/unit/parser/ast/asthash_spec.rb @@ -1,98 +1,97 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ASTHash do before :each do @scope = Puppet::Parser::Scope.new end it "should have a merge functionality" do hash = Puppet::Parser::AST::ASTHash.new(:value => {}) hash.should respond_to(:merge) end it "should be able to merge 2 AST hashes" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) hash.merge(Puppet::Parser::AST::ASTHash.new(:value => {"c" => "d"})) hash.value.should == { "a" => "b", "c" => "d" } end it "should be able to merge with a ruby Hash" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) hash.merge({"c" => "d"}) hash.value.should == { "a" => "b", "c" => "d" } end it "should evaluate each hash value" do key1 = stub "key1" value1 = stub "value1" key2 = stub "key2" value2 = stub "value2" value1.expects(:safeevaluate).with(@scope).returns("b") value2.expects(:safeevaluate).with(@scope).returns("d") operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) operator.evaluate(@scope) end it "should evaluate the hash keys if they are AST instances" do key1 = stub "key1" value1 = stub "value1", :safeevaluate => "one" key2 = stub "key2" value2 = stub "value2", :safeevaluate => "two" key1.expects(:safeevaluate).with(@scope).returns("1") key2.expects(:safeevaluate).with(@scope).returns("2") operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) hash = operator.evaluate(@scope) hash["1"].should == "one" hash["2"].should == "two" end it "should evaluate the hash keys if they are not AST instances" do key1 = "1" value1 = stub "value1", :safeevaluate => "one" key2 = "2" value2 = stub "value2", :safeevaluate => "two" operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) hash = operator.evaluate(@scope) hash["1"].should == "one" hash["2"].should == "two" end it "should return an evaluated hash" do key1 = stub "key1" value1 = stub "value1", :safeevaluate => "b" key2 = stub "key2" value2 = stub "value2", :safeevaluate => "d" operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) operator.evaluate(@scope).should == { key1 => "b", key2 => "d" } end describe "when being initialized without arguments" do it "should evaluate to an empty hash" do hash = Puppet::Parser::AST::ASTHash.new({}) hash.evaluate(@scope).should == {} end it "should support merging" do hash = Puppet::Parser::AST::ASTHash.new({}) hash.merge({"a" => "b"}).should == {"a" => "b"} end end it "should return a valid string with to_s" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b", "c" => "d" }) hash.to_s.should == '{a => b, c => d}' end end diff --git a/spec/unit/parser/ast/boolean_operator_spec.rb b/spec/unit/parser/ast/boolean_operator_spec.rb index 529946694..287f466a7 100755 --- a/spec/unit/parser/ast/boolean_operator_spec.rb +++ b/spec/unit/parser/ast/boolean_operator_spec.rb @@ -1,53 +1,52 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::BooleanOperator do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new @true_ast = ast::Boolean.new( :value => true) @false_ast = ast::Boolean.new( :value => false) end it "should evaluate left operand inconditionally" do lval = stub "lval" lval.expects(:safeevaluate).with(@scope).returns("true") rval = stub "rval", :safeevaluate => false rval.expects(:safeevaluate).never operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval operator.evaluate(@scope) end it "should evaluate right 'and' operand only if left operand is true" do lval = stub "lval", :safeevaluate => true rval = stub "rval", :safeevaluate => false rval.expects(:safeevaluate).with(@scope).returns(false) operator = ast::BooleanOperator.new :rval => rval, :operator => "and", :lval => lval operator.evaluate(@scope) end it "should evaluate right 'or' operand only if left operand is false" do lval = stub "lval", :safeevaluate => false rval = stub "rval", :safeevaluate => false rval.expects(:safeevaluate).with(@scope).returns(false) operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval operator.evaluate(@scope) end it "should return true for false OR true" do ast::BooleanOperator.new(:rval => @true_ast, :operator => "or", :lval => @false_ast).evaluate(@scope).should be_true end it "should return false for true AND false" do ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @false_ast ).evaluate(@scope).should be_false end it "should return true for true AND true" do ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @true_ast ).evaluate(@scope).should be_true end end diff --git a/spec/unit/parser/ast/casestatement_spec.rb b/spec/unit/parser/ast/casestatement_spec.rb index bce3ad801..e21190706 100755 --- a/spec/unit/parser/ast/casestatement_spec.rb +++ b/spec/unit/parser/ast/casestatement_spec.rb @@ -1,165 +1,164 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::CaseStatement do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do before :each do @test = stub 'test' @test.stubs(:safeevaluate).with(@scope).returns("value") @option1 = Puppet::Parser::AST::CaseOpt.new({}) @option1.stubs(:eachopt) @option1.stubs(:default?).returns false @option2 = Puppet::Parser::AST::CaseOpt.new({}) @option2.stubs(:eachopt) @option2.stubs(:default?).returns false @options = Puppet::Parser::AST::ASTArray.new(:children => [@option1, @option2]) @casestmt = Puppet::Parser::AST::CaseStatement.new :test => @test, :options => @options end it "should evaluate test" do @test.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should scan each option" do @casestmt.evaluate(@scope) end describe "when scanning options" do before :each do @opval1 = stub_everything 'opval1' @option1.stubs(:eachopt).yields(@opval1) @opval2 = stub_everything 'opval2' @option2.stubs(:eachopt).yields(@opval2) end it "should evaluate each sub-option" do @option1.expects(:eachopt) @option2.expects(:eachopt) @casestmt.evaluate(@scope) end it "should evaluate first matching option" do @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @option2.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return the first matching evaluated option" do @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @option2.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should evaluate the default option if none matched" do @option1.stubs(:default?).returns(true) @option1.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return the default evaluated option if none matched" do @option1.stubs(:default?).returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should return nil if nothing matched" do @casestmt.evaluate(@scope).should be_nil end it "should match and set scope ephemeral variables" do @opval1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } @casestmt.evaluate(@scope) end it "should evaluate this regex option if it matches" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return this evaluated regex option if it matches" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should unset scope ephemeral variables after option evaluation" do @scope.stubs(:ephemeral_level).returns(:level) @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @scope.expects(:unset_ephemeral_var).with(:level) @casestmt.evaluate(@scope) end it "should not leak ephemeral variables even if evaluation fails" do @scope.stubs(:ephemeral_level).returns(:level) @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).raises @scope.expects(:unset_ephemeral_var).with(:level) lambda { @casestmt.evaluate(@scope) }.should raise_error end end end it "should match if any of the provided options evaluate as true" do ast = nil AST = Puppet::Parser::AST tests = { "one" => %w{a b c}, "two" => %w{e f g} } options = tests.collect do |result, values| values = values.collect { |v| AST::Leaf.new :value => v } AST::CaseOpt.new( :value => AST::ASTArray.new(:children => values), :statements => AST::Leaf.new(:value => result) ) end options << AST::CaseOpt.new( :value => AST::Default.new(:value => "default"), :statements => AST::Leaf.new(:value => "default") ) ast = nil param = AST::Variable.new(:value => "testparam") ast = AST::CaseStatement.new(:test => param, :options => options) tests.each do |should, values| values.each do |value| @scope = Puppet::Parser::Scope.new @scope.setvar("testparam", value) result = ast.evaluate(@scope) result.should == should end end end end diff --git a/spec/unit/parser/ast/collection_spec.rb b/spec/unit/parser/ast/collection_spec.rb index 99abc998d..78094e68d 100755 --- a/spec/unit/parser/ast/collection_spec.rb +++ b/spec/unit/parser/ast/collection_spec.rb @@ -1,71 +1,70 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Collection do before :each do @mytype = Puppet::Resource::Type.new(:definition, "mytype") @environment = Puppet::Node::Environment.new @environment.known_resource_types.add @mytype @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode", :environment => @environment)) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @overrides = stub_everything 'overrides' @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) end it "should evaluate its query" do query = mock 'query' collection = Puppet::Parser::AST::Collection.new :query => query, :form => :virtual collection.type = 'mytype' query.expects(:safeevaluate).with(@scope) collection.evaluate(@scope) end it "should instantiate a Collector for this type" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" @test_type = Puppet::Resource::Type.new(:definition, "test") @environment.known_resource_types.add @test_type Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, :virtual) collection.evaluate(@scope) end it "should tell the compiler about this collector" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype" Puppet::Parser::Collector.stubs(:new).returns("whatever") @compiler.expects(:add_collection).with("whatever") collection.evaluate(@scope) end it "should evaluate overriden paramaters" do collector = stub_everything 'collector' collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) @overrides.expects(:safeevaluate).with(@scope) collection.evaluate(@scope) end it "should tell the collector about overrides" do collector = mock 'collector' collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) collector.expects(:add_override) collection.evaluate(@scope) end it "should fail when evaluating undefined resource types" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "bogus" lambda { collection.evaluate(@scope) }.should raise_error "Resource type bogus doesn't exist" end end diff --git a/spec/unit/parser/ast/collexpr_spec.rb b/spec/unit/parser/ast/collexpr_spec.rb index 04ce69cd3..454e7481b 100755 --- a/spec/unit/parser/ast/collexpr_spec.rb +++ b/spec/unit/parser/ast/collexpr_spec.rb @@ -1,115 +1,114 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::CollExpr do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating with two operands" do before :each do @test1 = mock 'test1' @test1.expects(:safeevaluate).with(@scope).returns("test1") @test2 = mock 'test2' @test2.expects(:safeevaluate).with(@scope).returns("test2") end it "should evaluate both" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") collexpr.evaluate(@scope) end it "should produce a textual representation and code of the expression" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==") result = collexpr.evaluate(@scope) result[0].should == "param_values.value = 'test2' and param_names.name = 'test1'" result[1].should be_an_instance_of(Proc) end it "should propagate expression type and form to child if expression themselves" do [@test1, @test2].each do |t| t.expects(:is_a?).returns(true) t.expects(:form).returns(false) t.expects(:type).returns(false) t.expects(:type=) t.expects(:form=) end collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :form => true, :type => true) result = collexpr.evaluate(@scope) end describe "and when evaluating the produced code" do before :each do @resource = mock 'resource' @resource.expects(:[]).with("test1").at_least(1).returns("test2") end it "should evaluate like the original expression for ==" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] == "test2") end it "should evaluate like the original expression for !=" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "!=") collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] != "test2") end end it "should warn if this is an exported collection containing parenthesis (unsupported)" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :parens => true, :form => :exported) Puppet.expects(:warning) collexpr.evaluate(@scope) end %w{and or}.each do |op| it "should raise an error if this is an exported collection with #{op} operator (unsupported)" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=> op, :form => :exported) lambda { collexpr.evaluate(@scope) }.should raise_error(Puppet::ParseError) end end end describe "when evaluating with tags" do before :each do @tag = stub 'tag', :safeevaluate => 'tag' @value = stub 'value', :safeevaluate => 'value' @resource = stub 'resource' @resource.stubs(:tagged?).with("value").returns(true) end it "should produce a textual representation of the expression" do collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper=>"==") result = collexpr.evaluate(@scope) result[0].should == "puppet_tags.name = 'value'" end it "should inspect resource tags if the query term is on tags" do collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper => "==") collexpr.evaluate(@scope)[1].call(@resource).should be_true end end [:exported,:virtual].each do |mode| it "should check for array member equality if resource parameter is an array for == in mode #{mode}" do array = mock 'array', :safeevaluate => "array" test1 = mock 'test1' test1.expects(:safeevaluate).with(@scope).returns("test1") resource = mock 'resource' resource.expects(:[]).with("array").at_least(1).returns(["test1","test2","test3"]) collexpr = ast::CollExpr.new(:test1 => array, :test2 => test1, :oper => "==", :form => mode) collexpr.evaluate(@scope)[1].call(resource).should be_true end end it "should raise an error for invalid operator" do lambda { collexpr = ast::CollExpr.new(:oper=>">") }.should raise_error end end diff --git a/spec/unit/parser/ast/comparison_operator_spec.rb b/spec/unit/parser/ast/comparison_operator_spec.rb index 03c9069cb..96f4562e9 100755 --- a/spec/unit/parser/ast/comparison_operator_spec.rb +++ b/spec/unit/parser/ast/comparison_operator_spec.rb @@ -1,116 +1,115 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ComparisonOperator do before :each do @scope = Puppet::Parser::Scope.new @one = Puppet::Parser::AST::Leaf.new(:value => "1") @two = Puppet::Parser::AST::Leaf.new(:value => "2") @lval = Puppet::Parser::AST::Leaf.new(:value => "one") @rval = Puppet::Parser::AST::Leaf.new(:value => "two") end it "should evaluate both values" do @lval.expects(:safeevaluate).with(@scope) @rval.expects(:safeevaluate).with(@scope) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "==", :rval => @rval operator.evaluate(@scope) end it "should convert the arguments to numbers if they are numbers in string" do Puppet::Parser::Scope.expects(:number?).with("1").returns(1) Puppet::Parser::Scope.expects(:number?).with("2").returns(2) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "==", :rval => @two operator.evaluate(@scope) end %w{< > <= >=}.each do |oper| it "should use string comparison #{oper} if operands are strings" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => oper, :rval => @rval operator.evaluate(@scope).should == "one".send(oper,"two") end end describe "with string comparison" do it "should use matching" do @rval.expects(:evaluate_match).with("one", @scope) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "==", :rval => @rval operator.evaluate(@scope) end it "should return true for :undef to '' equality" do astundef = Puppet::Parser::AST::Leaf.new(:value => :undef) empty = Puppet::Parser::AST::Leaf.new(:value => '') operator = Puppet::Parser::AST::ComparisonOperator.new :lval => astundef, :operator => "==", :rval => empty operator.evaluate(@scope).should be_true end [true, false].each do |result| it "should return #{(result).inspect} with '==' when matching return #{result.inspect}" do @rval.expects(:evaluate_match).with("one", @scope).returns result operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "==", :rval => @rval operator.evaluate(@scope).should == result end it "should return #{(!result).inspect} with '!=' when matching return #{result.inspect}" do @rval.expects(:evaluate_match).with("one", @scope).returns result operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "!=", :rval => @rval operator.evaluate(@scope).should == !result end end end it "should fail with arguments of different types" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => ">", :rval => @rval lambda { operator.evaluate(@scope) }.should raise_error(ArgumentError) end it "should fail for an unknown operator" do lambda { operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "or", :rval => @two }.should raise_error end %w{< > <= >= ==}.each do |oper| it "should return the result of using '#{oper}' to compare the left and right sides" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => oper, :rval => @two operator.evaluate(@scope).should == 1.send(oper,2) end end it "should return the result of using '!=' to compare the left and right sides" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => '!=', :rval => @two operator.evaluate(@scope).should == true end it "should work for variables too" do one = Puppet::Parser::AST::Variable.new( :value => "one" ) two = Puppet::Parser::AST::Variable.new( :value => "two" ) - @scope.expects(:lookupvar).with("one", false).returns(1) - @scope.expects(:lookupvar).with("two", false).returns(2) + one.expects(:safeevaluate).with(@scope).returns(1) + two.expects(:safeevaluate).with(@scope).returns(2) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => "<", :rval => two operator.evaluate(@scope).should == true end # see ticket #1759 %w{< > <= >=}.each do |oper| it "should return the correct result of using '#{oper}' to compare 10 and 9" do ten = Puppet::Parser::AST::Leaf.new(:value => "10") nine = Puppet::Parser::AST::Leaf.new(:value => "9") operator = Puppet::Parser::AST::ComparisonOperator.new :lval => ten, :operator => oper, :rval => nine operator.evaluate(@scope).should == 10.send(oper,9) end end end diff --git a/spec/unit/parser/ast/definition_spec.rb b/spec/unit/parser/ast/definition_spec.rb old mode 100644 new mode 100755 index e7f55d258..8b2f7f26f --- a/spec/unit/parser/ast/definition_spec.rb +++ b/spec/unit/parser/ast/definition_spec.rb @@ -1,22 +1,21 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Definition do it "should make its context available through an accessor" do definition = Puppet::Parser::AST::Definition.new('foo', :line => 5) definition.context.should == {:line => 5} end describe "when instantiated" do it "should create a definition with the proper type, name, context, and module name" do definition = Puppet::Parser::AST::Definition.new('foo', :line => 5) instantiated_definitions = definition.instantiate('modname') instantiated_definitions.length.should == 1 instantiated_definitions[0].type.should == :definition instantiated_definitions[0].name.should == 'foo' instantiated_definitions[0].line.should == 5 instantiated_definitions[0].module_name.should == 'modname' end end end diff --git a/spec/unit/parser/ast/function_spec.rb b/spec/unit/parser/ast/function_spec.rb old mode 100644 new mode 100755 index cd4b0f94e..c52e806e9 --- a/spec/unit/parser/ast/function_spec.rb +++ b/spec/unit/parser/ast/function_spec.rb @@ -1,93 +1,92 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Function do before :each do @scope = mock 'scope' end describe "when initializing" do it "should not fail if the function doesn't exist" do Puppet::Parser::Functions.stubs(:function).returns(false) lambda{ Puppet::Parser::AST::Function.new :name => "dontexist" }.should_not raise_error(Puppet::ParseError) end end it "should return its representation with to_s" do args = stub 'args', :is_a? => true, :to_s => "[a, b]" Puppet::Parser::AST::Function.new(:name => "func", :arguments => args).to_s.should == "func(a, b)" end describe "when evaluating" do it "should fail if the function doesn't exist" do Puppet::Parser::Functions.stubs(:function).returns(false) func = Puppet::Parser::AST::Function.new :name => "dontexist" lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError) end it "should fail if the function is a statement used as rvalue" do Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(false) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :rvalue lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError, "Function 'exist' does not return a value") end it "should fail if the function is an rvalue used as statement" do Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError,"Function 'exist' must be the value of a statement") end it "should evaluate its arguments" do argument = stub 'arg' Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.stubs(:function_exist) argument.expects(:safeevaluate).with(@scope).returns("argument") func.evaluate(@scope) end it "should call the underlying ruby function" do argument = stub 'arg', :safeevaluate => ["nothing"] Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.expects(:function_exist).with(["nothing"]) func.evaluate(@scope) end it "should convert :undef to '' in arguments" do argument = stub 'arg', :safeevaluate => ["foo", :undef, "bar"] Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.expects(:function_exist).with(["foo", "", "bar"]) func.evaluate(@scope) end it "should return the ruby function return for rvalue functions" do argument = stub 'arg', :safeevaluate => ["nothing"] Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.stubs(:function_exist).with(["nothing"]).returns("returning") func.evaluate(@scope).should == "returning" end end end diff --git a/spec/unit/parser/ast/hostclass_spec.rb b/spec/unit/parser/ast/hostclass_spec.rb old mode 100644 new mode 100755 index 390490788..ee154fac8 --- a/spec/unit/parser/ast/hostclass_spec.rb +++ b/spec/unit/parser/ast/hostclass_spec.rb @@ -1,73 +1,72 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Hostclass do def ast Puppet::Parser::AST end def newarray(*elems) ast::ASTArray.new({}).push(*elems) end it "should make its name and context available through accessors" do hostclass = ast::Hostclass.new('foo', :line => 5) hostclass.name.should == 'foo' hostclass.context.should == {:line => 5} end it "should make its code available through an accessor" do code = newarray hostclass = ast::Hostclass.new('foo', :code => code) hostclass.code.should be_equal(code) end describe "when instantiated" do it "should create a class with the proper type, code, name, context, and module name" do code = newarray hostclass = ast::Hostclass.new('foo', :code => code, :line => 5) instantiated_class = hostclass.instantiate('modname')[0] instantiated_class.type.should == :hostclass instantiated_class.name.should == 'foo' instantiated_class.code.should be_equal(code) instantiated_class.line.should == 5 instantiated_class.module_name.should == 'modname' end it "should instantiate all nested classes, defines, and nodes with the same module name." do nested_objects = newarray(ast::Hostclass.new('foo::child1'), ast::Definition.new('foo::child2'), ast::Definition.new('child3')) hostclass = ast::Hostclass.new('foo', :code => nested_objects) instantiated_classes = hostclass.instantiate('modname') instantiated_classes.length.should == 4 instantiated_classes[0].name.should == 'foo' instantiated_classes[1].name.should == 'foo::child1' instantiated_classes[2].name.should == 'foo::child2' instantiated_classes[3].name.should == 'child3' instantiated_classes.each { |cls| cls.module_name.should == 'modname' } end it "should handle a nested class that contains its own nested classes." do foo_bar_baz = ast::Hostclass.new('foo::bar::baz') foo_bar = ast::Hostclass.new('foo::bar', :code => newarray(foo_bar_baz)) foo = ast::Hostclass.new('foo', :code => newarray(foo_bar)) instantiated_classes = foo.instantiate('') instantiated_classes.length.should == 3 instantiated_classes[0].name.should == 'foo' instantiated_classes[1].name.should == 'foo::bar' instantiated_classes[2].name.should == 'foo::bar::baz' end it "should skip nested elements that are not classes, definitions, or nodes." do func = ast::Function.new(:name => 'biz', :arguments => newarray(ast::Name.new(:value => 'baz'))) foo = ast::Hostclass.new('foo', :code => newarray(func)) instantiated_classes = foo.instantiate('') instantiated_classes.length.should == 1 instantiated_classes[0].should be_a(Puppet::Resource::Type) instantiated_classes[0].name.should == 'foo' end end end diff --git a/spec/unit/parser/ast/ifstatement_spec.rb b/spec/unit/parser/ast/ifstatement_spec.rb index 6a2fed22c..4b6e0b8e5 100755 --- a/spec/unit/parser/ast/ifstatement_spec.rb +++ b/spec/unit/parser/ast/ifstatement_spec.rb @@ -1,76 +1,75 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::IfStatement do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do before :each do @test = stub 'test' @test.stubs(:safeevaluate).with(@scope) @stmt = stub 'stmt' @stmt.stubs(:safeevaluate).with(@scope) @else = stub 'else' @else.stubs(:safeevaluate).with(@scope) @ifstmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt @ifelsestmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt, :else => @else end it "should evaluate test" do Puppet::Parser::Scope.stubs(:true?).returns(false) @test.expects(:safeevaluate).with(@scope) @ifstmt.evaluate(@scope) end it "should evaluate if statements if test is true" do Puppet::Parser::Scope.stubs(:true?).returns(true) @stmt.expects(:safeevaluate).with(@scope) @ifstmt.evaluate(@scope) end it "should not evaluate if statements if test is false" do Puppet::Parser::Scope.stubs(:true?).returns(false) @stmt.expects(:safeevaluate).with(@scope).never @ifstmt.evaluate(@scope) end it "should evaluate the else branch if test is false" do Puppet::Parser::Scope.stubs(:true?).returns(false) @else.expects(:safeevaluate).with(@scope) @ifelsestmt.evaluate(@scope) end it "should not evaluate the else branch if test is true" do Puppet::Parser::Scope.stubs(:true?).returns(true) @else.expects(:safeevaluate).with(@scope).never @ifelsestmt.evaluate(@scope) end it "should reset ephemeral statements after evaluation" do @scope.expects(:ephemeral_level).returns(:level) Puppet::Parser::Scope.stubs(:true?).returns(true) @stmt.expects(:safeevaluate).with(@scope) @scope.expects(:unset_ephemeral_var).with(:level) @ifstmt.evaluate(@scope) end end end diff --git a/spec/unit/parser/ast/in_operator_spec.rb b/spec/unit/parser/ast/in_operator_spec.rb old mode 100644 new mode 100755 index 08c01582e..b6b6fbb89 --- a/spec/unit/parser/ast/in_operator_spec.rb +++ b/spec/unit/parser/ast/in_operator_spec.rb @@ -1,60 +1,59 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/ast/in_operator' describe Puppet::Parser::AST::InOperator do before :each do @scope = Puppet::Parser::Scope.new @lval = stub 'lval' @lval.stubs(:safeevaluate).with(@scope).returns("left") @rval = stub 'rval' @rval.stubs(:safeevaluate).with(@scope).returns("right") @operator = Puppet::Parser::AST::InOperator.new :lval => @lval, :rval => @rval end it "should evaluate the left operand" do @lval.expects(:safeevaluate).with(@scope).returns("string") @operator.evaluate(@scope) end it "should evaluate the right operand" do @rval.expects(:safeevaluate).with(@scope).returns("string") @operator.evaluate(@scope) end it "should raise an argument error if lval is not a string" do @lval.expects(:safeevaluate).with(@scope).returns([12,13]) lambda { @operator.evaluate(@scope) }.should raise_error end it "should raise an argument error if rval doesn't support the include? method" do @rval.expects(:safeevaluate).with(@scope).returns(stub('value')) lambda { @operator.evaluate(@scope) }.should raise_error end it "should not raise an argument error if rval supports the include? method" do @rval.expects(:safeevaluate).with(@scope).returns(stub('value', :include? => true)) lambda { @operator.evaluate(@scope) }.should_not raise_error end it "should return rval.include?(lval)" do lval = stub 'lvalue', :is_a? => true @lval.stubs(:safeevaluate).with(@scope).returns(lval) rval = stub 'rvalue' @rval.stubs(:safeevaluate).with(@scope).returns(rval) rval.expects(:include?).with(lval).returns(:result) @operator.evaluate(@scope).should == :result end end diff --git a/spec/unit/parser/ast/leaf_spec.rb b/spec/unit/parser/ast/leaf_spec.rb index 2119a27a8..ff3fed5e9 100755 --- a/spec/unit/parser/ast/leaf_spec.rb +++ b/spec/unit/parser/ast/leaf_spec.rb @@ -1,408 +1,412 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Leaf do before :each do @scope = stub 'scope' @value = stub 'value' @leaf = Puppet::Parser::AST::Leaf.new(:value => @value) end it "should have a evaluate_match method" do Puppet::Parser::AST::Leaf.new(:value => "value").should respond_to(:evaluate_match) end describe "when converting to string" do it "should transform its value to string" do value = stub 'value', :is_a? => true value.expects(:to_s) Puppet::Parser::AST::Leaf.new( :value => value ).to_s end end it "should have a match method" do @leaf.should respond_to(:match) end it "should delegate match to ==" do @value.expects(:==).with("value") @leaf.match("value") end end describe Puppet::Parser::AST::FlatString do describe "when converting to string" do it "should transform its value to a quoted string" do value = stub 'value', :is_a? => true, :to_s => "ab" Puppet::Parser::AST::FlatString.new( :value => value ).to_s.should == "\"ab\"" end end end describe Puppet::Parser::AST::String do describe "when converting to string" do it "should transform its value to a quoted string" do value = stub 'value', :is_a? => true, :to_s => "ab" Puppet::Parser::AST::String.new( :value => value ).to_s.should == "\"ab\"" end it "should return a dup of its value" do value = "" Puppet::Parser::AST::String.new( :value => value ).evaluate(stub('scope')).should_not be_equal(value) end end end describe Puppet::Parser::AST::Concat do describe "when evaluating" do before :each do @scope = stub_everything 'scope' end it "should interpolate variables and concatenate their values" do one = Puppet::Parser::AST::String.new(:value => "one") one.stubs(:evaluate).returns("one ") two = Puppet::Parser::AST::String.new(:value => "two") two.stubs(:evaluate).returns(" two ") three = Puppet::Parser::AST::String.new(:value => "three") three.stubs(:evaluate).returns(" three") var = Puppet::Parser::AST::Variable.new(:value => "myvar") var.stubs(:evaluate).returns("foo") array = Puppet::Parser::AST::Variable.new(:value => "array") array.stubs(:evaluate).returns(["bar","baz"]) concat = Puppet::Parser::AST::Concat.new(:value => [one,var,two,array,three]) concat.evaluate(@scope).should == 'one foo two barbaz three' end it "should transform undef variables to empty string" do var = Puppet::Parser::AST::Variable.new(:value => "myvar") var.stubs(:evaluate).returns(:undef) concat = Puppet::Parser::AST::Concat.new(:value => [var]) concat.evaluate(@scope).should == '' end end end describe Puppet::Parser::AST::Undef do before :each do @scope = stub 'scope' @undef = Puppet::Parser::AST::Undef.new(:value => :undef) end it "should match undef with undef" do @undef.evaluate_match(:undef, @scope).should be_true end it "should not match undef with an empty string" do @undef.evaluate_match("", @scope).should be_false end end describe Puppet::Parser::AST::HashOrArrayAccess do before :each do @scope = stub 'scope' end describe "when evaluating" do it "should evaluate the variable part if necessary" do - @scope.stubs(:lookupvar).with("a").returns(["b"]) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns(["b"]) variable = stub 'variable', :evaluate => "a" access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => variable, :key => 0 ) variable.expects(:safeevaluate).with(@scope).returns("a") access.evaluate(@scope).should == "b" end it "should evaluate the access key part if necessary" do - @scope.stubs(:lookupvar).with("a").returns(["b"]) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns(["b"]) index = stub 'index', :evaluate => 0 access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => index ) index.expects(:safeevaluate).with(@scope).returns(0) access.evaluate(@scope).should == "b" end it "should be able to return an array member" do - @scope.stubs(:lookupvar).with("a").returns(["val1", "val2", "val3"]) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns(["val1", "val2", "val3"]) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => 1 ) access.evaluate(@scope).should == "val2" end it "should be able to return an array member when index is a stringified number" do - @scope.stubs(:lookupvar).with("a").returns(["val1", "val2", "val3"]) + @scope.stubs(:lookupvar).with { |name,options| name == "a" }.returns(["val1", "val2", "val3"]) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "1" ) access.evaluate(@scope).should == "val2" end it "should raise an error when accessing an array with a key" do - @scope.stubs(:lookupvar).with("a").returns(["val1", "val2", "val3"]) + @scope.stubs(:lookupvar).with { |name,options| name == "a"}.returns(["val1", "val2", "val3"]) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "get_me_the_second_element_please" ) lambda { access.evaluate(@scope) }.should raise_error end it "should be able to return an hash value" do - @scope.stubs(:lookupvar).with("a").returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns({ "key1" => "val1", "key2" => "val2", "key3" => "val3" }) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) access.evaluate(@scope).should == "val2" end it "should be able to return an hash value with a numerical key" do - @scope.stubs(:lookupvar).with("a").returns({ "key1" => "val1", "key2" => "val2", "45" => "45", "key3" => "val3" }) + @scope.stubs(:lookupvar).with { |name,options| name == "a"}.returns({ "key1" => "val1", "key2" => "val2", "45" => "45", "key3" => "val3" }) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "45" ) access.evaluate(@scope).should == "45" end it "should raise an error if the variable lookup didn't return an hash or an array" do - @scope.stubs(:lookupvar).with("a").returns("I'm a string") + @scope.stubs(:lookupvar).with { |name,options| name == "a"}.returns("I'm a string") access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) lambda { access.evaluate(@scope) }.should raise_error end it "should raise an error if the variable wasn't in the scope" do - @scope.stubs(:lookupvar).with("a").returns(nil) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns(nil) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) lambda { access.evaluate(@scope) }.should raise_error end it "should return a correct string representation" do access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key2" ) access.to_s.should == '$a[key2]' end it "should work with recursive hash access" do - @scope.stubs(:lookupvar).with("a").returns({ "key" => { "subkey" => "b" }}) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns({ "key" => { "subkey" => "b" }}) access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => "subkey") access2.evaluate(@scope).should == 'b' end it "should work with interleaved array and hash access" do - @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) + @scope.stubs(:lookupvar).with { |name,options| name == 'a'}.returns({ "key" => [ "a" , "b" ]}) access1 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") access2 = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => access1, :key => 1) access2.evaluate(@scope).should == 'b' end end describe "when assigning" do it "should add a new key and value" do scope = Puppet::Parser::Scope.new scope.setvar("a", { 'a' => 'b' }) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "b") access.assign(scope, "c" ) scope.lookupvar("a").should be_include("b") end it "should raise an error when assigning an array element with a key" do - @scope.stubs(:lookupvar).with("a").returns([]) + @scope.stubs(:lookupvar).with { |name,options| name == "a"}.returns([]) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "get_me_the_second_element_please" ) lambda { access.assign(@scope, "test") }.should raise_error end it "should be able to return an array member when index is a stringified number" do scope = Puppet::Parser::Scope.new scope.setvar("a", []) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "0" ) access.assign(scope, "val2") scope.lookupvar("a").should == ["val2"] end it "should raise an error when trying to overwrite an hash value" do - @scope.stubs(:lookupvar).with("a").returns({ "key" => [ "a" , "b" ]}) + @scope.stubs(:lookupvar).with { |name,options| name == "a" }.returns({ "key" => [ "a" , "b" ]}) access = Puppet::Parser::AST::HashOrArrayAccess.new(:variable => "a", :key => "key") lambda { access.assign(@scope, "test") }.should raise_error end end end describe Puppet::Parser::AST::Regex do before :each do @scope = stub 'scope' end describe "when initializing" do it "should create a Regexp with its content when value is not a Regexp" do Regexp.expects(:new).with("/ab/") Puppet::Parser::AST::Regex.new :value => "/ab/" end it "should not create a Regexp with its content when value is a Regexp" do value = Regexp.new("/ab/") Regexp.expects(:new).with("/ab/").never Puppet::Parser::AST::Regex.new :value => value end end describe "when evaluating" do it "should return self" do val = Puppet::Parser::AST::Regex.new :value => "/ab/" val.evaluate(@scope).should === val end end describe "when evaluate_match" do before :each do @value = stub 'regex' @value.stubs(:match).with("value").returns(true) Regexp.stubs(:new).returns(@value) @regex = Puppet::Parser::AST::Regex.new :value => "/ab/" end it "should issue the regexp match" do @value.expects(:match).with("value") @regex.evaluate_match("value", @scope) end it "should not downcase the paramater value" do @value.expects(:match).with("VaLuE") @regex.evaluate_match("VaLuE", @scope) end it "should set ephemeral scope vars if there is a match" do @scope.expects(:ephemeral_from).with(true, nil, nil) @regex.evaluate_match("value", @scope) end it "should return the match to the caller" do @value.stubs(:match).with("value").returns(:match) @scope.stubs(:ephemeral_from) @regex.evaluate_match("value", @scope) end end it "should return the regex source with to_s" do regex = stub 'regex' Regexp.stubs(:new).returns(regex) val = Puppet::Parser::AST::Regex.new :value => "/ab/" regex.expects(:source) val.to_s end it "should delegate match to the underlying regexp match method" do regex = Regexp.new("/ab/") val = Puppet::Parser::AST::Regex.new :value => regex regex.expects(:match).with("value") val.match("value") end end describe Puppet::Parser::AST::Variable do before :each do @scope = stub 'scope' - @var = Puppet::Parser::AST::Variable.new(:value => "myvar") + @var = Puppet::Parser::AST::Variable.new(:value => "myvar", :file => 'my.pp', :line => 222) end it "should lookup the variable in scope" do - @scope.expects(:lookupvar).with("myvar", false).returns(:myvalue) + @scope.expects(:lookupvar).with { |name,options| name == "myvar" }.returns(:myvalue) + @var.safeevaluate(@scope).should == :myvalue + end + + it "should pass the source location to lookupvar" do + @scope.expects(:lookupvar).with { |name,options| name == "myvar" and options[:file] == 'my.pp' and options[:line] == 222 }.returns(:myvalue) @var.safeevaluate(@scope).should == :myvalue end it "should return undef if the variable wasn't set" do - @scope.expects(:lookupvar).with("myvar", false).returns(:undefined) + @scope.expects(:lookupvar).with { |name,options| name == "myvar" }.returns(:undefined) @var.safeevaluate(@scope).should == :undef end describe "when converting to string" do it "should transform its value to a variable" do value = stub 'value', :is_a? => true, :to_s => "myvar" Puppet::Parser::AST::Variable.new( :value => value ).to_s.should == "\$myvar" end end end describe Puppet::Parser::AST::HostName do before :each do @scope = stub 'scope' @value = stub 'value', :=~ => false @value.stubs(:to_s).returns(@value) @value.stubs(:downcase).returns(@value) @host = Puppet::Parser::AST::HostName.new( :value => @value) end it "should raise an error if hostname is not valid" do lambda { Puppet::Parser::AST::HostName.new( :value => "not an hostname!" ) }.should raise_error end it "should not raise an error if hostname is a regex" do lambda { Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/test/") ) }.should_not raise_error end it "should stringify the value" do value = stub 'value', :=~ => false value.expects(:to_s).returns("test") Puppet::Parser::AST::HostName.new(:value => value) end it "should downcase the value" do value = stub 'value', :=~ => false value.stubs(:to_s).returns("UPCASED") host = Puppet::Parser::AST::HostName.new(:value => value) host.value == "upcased" end it "should evaluate to its value" do @host.evaluate(@scope).should == @value end it "should delegate eql? to the underlying value if it is an HostName" do @value.expects(:eql?).with("value") @host.eql?("value") end it "should delegate eql? to the underlying value if it is not an HostName" do value = stub 'compared', :is_a? => true, :value => "value" @value.expects(:eql?).with("value") @host.eql?(value) end it "should delegate hash to the underlying value" do @value.expects(:hash) @host.hash end end diff --git a/spec/unit/parser/ast/match_operator_spec.rb b/spec/unit/parser/ast/match_operator_spec.rb index f2a68b676..0f9235aeb 100755 --- a/spec/unit/parser/ast/match_operator_spec.rb +++ b/spec/unit/parser/ast/match_operator_spec.rb @@ -1,50 +1,49 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::MatchOperator do before :each do @scope = Puppet::Parser::Scope.new @lval = stub 'lval' @lval.stubs(:safeevaluate).with(@scope).returns("this is a string") @rval = stub 'rval' @rval.stubs(:evaluate_match) @operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => "=~" end it "should evaluate the left operand" do @lval.expects(:safeevaluate).with(@scope) @operator.evaluate(@scope) end it "should fail for an unknown operator" do lambda { operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :operator => "unknown", :rval => @rval }.should raise_error end it "should evaluate_match the left operand" do @rval.expects(:evaluate_match).with("this is a string", @scope).returns(:match) @operator.evaluate(@scope) end { "=~" => true, "!~" => false }.each do |op, res| it "should return #{res} if the regexp matches with #{op}" do match = stub 'match' @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(match) operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op operator.evaluate(@scope).should == res end it "should return #{!res} if the regexp doesn't match" do @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(nil) operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op operator.evaluate(@scope).should == !res end end end diff --git a/spec/unit/parser/ast/minus_spec.rb b/spec/unit/parser/ast/minus_spec.rb index 339087e80..8ebd14e80 100755 --- a/spec/unit/parser/ast/minus_spec.rb +++ b/spec/unit/parser/ast/minus_spec.rb @@ -1,36 +1,35 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Minus do before :each do @scope = Puppet::Parser::Scope.new end it "should evaluate its argument" do value = stub "value" value.expects(:safeevaluate).with(@scope).returns(123) operator = Puppet::Parser::AST::Minus.new :value => value operator.evaluate(@scope) end it "should fail if argument is not a string or integer" do array_ast = stub 'array_ast', :safeevaluate => [2] operator = Puppet::Parser::AST::Minus.new :value => array_ast lambda { operator.evaluate(@scope) }.should raise_error end it "should work with integer as string" do string = stub 'string', :safeevaluate => "123" operator = Puppet::Parser::AST::Minus.new :value => string operator.evaluate(@scope).should == -123 end it "should work with integers" do int = stub 'int', :safeevaluate => 123 operator = Puppet::Parser::AST::Minus.new :value => int operator.evaluate(@scope).should == -123 end end diff --git a/spec/unit/parser/ast/node_spec.rb b/spec/unit/parser/ast/node_spec.rb old mode 100644 new mode 100755 index c4e20ca67..c2e187184 --- a/spec/unit/parser/ast/node_spec.rb +++ b/spec/unit/parser/ast/node_spec.rb @@ -1,31 +1,30 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Node do describe "when instantiated" do it "should make its names and context available through accessors" do node = Puppet::Parser::AST::Node.new(['foo', 'bar'], :line => 5) node.names.should == ['foo', 'bar'] node.context.should == {:line => 5} end it "should create a node with the proper type, name, context, and module name" do node = Puppet::Parser::AST::Node.new(['foo'], :line => 5) instantiated_nodes = node.instantiate('modname') instantiated_nodes.length.should == 1 instantiated_nodes[0].type.should == :node instantiated_nodes[0].name.should == 'foo' instantiated_nodes[0].line.should == 5 instantiated_nodes[0].module_name.should == 'modname' end it "should handle multiple names" do node = Puppet::Parser::AST::Node.new(['foo', 'bar']) instantiated_nodes = node.instantiate('modname') instantiated_nodes.length.should == 2 instantiated_nodes[0].name.should == 'foo' instantiated_nodes[1].name.should == 'bar' end end end diff --git a/spec/unit/parser/ast/nop_spec.rb b/spec/unit/parser/ast/nop_spec.rb index 3fa2fc0ee..81302fa55 100755 --- a/spec/unit/parser/ast/nop_spec.rb +++ b/spec/unit/parser/ast/nop_spec.rb @@ -1,20 +1,19 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Nop do before do @scope = mock 'scope' end it "should do nothing on evaluation" do Puppet::Parser::AST.expects(:safeevaluate).never Puppet::Parser::AST::Nop.new({}).evaluate(@scope) end it "should not return anything" do Puppet::Parser::AST::Nop.new({}).evaluate(@scope).should be_nil end end diff --git a/spec/unit/parser/ast/not_spec.rb b/spec/unit/parser/ast/not_spec.rb index 31a425132..6569af699 100755 --- a/spec/unit/parser/ast/not_spec.rb +++ b/spec/unit/parser/ast/not_spec.rb @@ -1,30 +1,29 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Not do before :each do @scope = Puppet::Parser::Scope.new @true_ast = Puppet::Parser::AST::Boolean.new( :value => true) @false_ast = Puppet::Parser::AST::Boolean.new( :value => false) end it "should evaluate its child expression" do val = stub "val" val.expects(:safeevaluate).with(@scope) operator = Puppet::Parser::AST::Not.new :value => val operator.evaluate(@scope) end it "should return true for ! false" do operator = Puppet::Parser::AST::Not.new :value => @false_ast operator.evaluate(@scope).should == true end it "should return false for ! true" do operator = Puppet::Parser::AST::Not.new :value => @true_ast operator.evaluate(@scope).should == false end end diff --git a/spec/unit/parser/ast/relationship_spec.rb b/spec/unit/parser/ast/relationship_spec.rb old mode 100644 new mode 100755 index d8bc3a535..441ac45b1 --- a/spec/unit/parser/ast/relationship_spec.rb +++ b/spec/unit/parser/ast/relationship_spec.rb @@ -1,88 +1,87 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Relationship do before do @class = Puppet::Parser::AST::Relationship end it "should set its 'left' and 'right' arguments accordingly" do dep = @class.new(:left, :right, '->') dep.left.should == :left dep.right.should == :right end it "should set its arrow to whatever arrow is passed" do @class.new(:left, :right, '->').arrow.should == '->' end it "should set its type to :relationship if the relationship type is '<-'" do @class.new(:left, :right, '<-').type.should == :relationship end it "should set its type to :relationship if the relationship type is '->'" do @class.new(:left, :right, '->').type.should == :relationship end it "should set its type to :subscription if the relationship type is '~>'" do @class.new(:left, :right, '~>').type.should == :subscription end it "should set its type to :subscription if the relationship type is '<~'" do @class.new(:left, :right, '<~').type.should == :subscription end it "should set its line and file if provided" do dep = @class.new(:left, :right, '->', :line => 50, :file => "/foo") dep.line.should == 50 dep.file.should == "/foo" end describe "when evaluating" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should create a relationship with the evaluated source and target and add it to the scope" do source = stub 'source', :safeevaluate => :left target = stub 'target', :safeevaluate => :right @class.new(source, target, '->').evaluate(@scope) @compiler.relationships[0].source.should == :left @compiler.relationships[0].target.should == :right end describe "a chained relationship" do before do @left = stub 'left', :safeevaluate => :left @middle = stub 'middle', :safeevaluate => :middle @right = stub 'right', :safeevaluate => :right @first = @class.new(@left, @middle, '->') @second = @class.new(@first, @right, '->') end it "should evaluate the relationship to the left" do @first.expects(:evaluate).with(@scope).returns Puppet::Parser::Relationship.new(:left, :right, :relationship) @second.evaluate(@scope) end it "should use the right side of the left relationship as its source" do @second.evaluate(@scope) @compiler.relationships[0].source.should == :left @compiler.relationships[0].target.should == :middle @compiler.relationships[1].source.should == :middle @compiler.relationships[1].target.should == :right end it "should only evaluate a given AST node once" do @left.expects(:safeevaluate).once.returns :left @middle.expects(:safeevaluate).once.returns :middle @right.expects(:safeevaluate).once.returns :right @second.evaluate(@scope) end end end end diff --git a/spec/unit/parser/ast/resource_defaults_spec.rb b/spec/unit/parser/ast/resource_defaults_spec.rb index 36eb8df5f..8164828e1 100755 --- a/spec/unit/parser/ast/resource_defaults_spec.rb +++ b/spec/unit/parser/ast/resource_defaults_spec.rb @@ -1,22 +1,21 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ResourceDefaults do ast = Puppet::Parser::AST before :each do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @params = Puppet::Parser::AST::ASTArray.new({}) @compiler.stubs(:add_override) end it "should add defaults when evaluated" do default = Puppet::Parser::AST::ResourceDefaults.new :type => "file", :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) default.evaluate @scope @scope.lookupdefaults("file").should_not be_nil end end diff --git a/spec/unit/parser/ast/resource_override_spec.rb b/spec/unit/parser/ast/resource_override_spec.rb index 7ca3a7e8c..458d9a4bf 100755 --- a/spec/unit/parser/ast/resource_override_spec.rb +++ b/spec/unit/parser/ast/resource_override_spec.rb @@ -1,51 +1,50 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ResourceOverride do ast = Puppet::Parser::AST before :each do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @params = ast::ASTArray.new({}) @compiler.stubs(:add_override) end it "should evaluate the overriden object" do klass = stub 'klass', :title => "title", :type => "type" object = mock 'object' object.expects(:safeevaluate).with(@scope).returns(klass) ast::ResourceOverride.new(:object => object, :parameters => @params ).evaluate(@scope) end it "should tell the compiler to override the resource with our own" do @compiler.expects(:add_override) klass = stub 'klass', :title => "title", :type => "one" object = mock 'object', :safeevaluate => klass ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) end it "should return the overriden resource directly when called with one item" do klass = stub 'klass', :title => "title", :type => "one" object = mock 'object', :safeevaluate => klass override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) override.should be_an_instance_of(Puppet::Parser::Resource) override.title.should == "title" override.type.should == "One" end it "should return an array of overriden resources when called with an array of titles" do klass1 = stub 'klass1', :title => "title1", :type => "one" klass2 = stub 'klass2', :title => "title2", :type => "one" object = mock 'object', :safeevaluate => [klass1,klass2] override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) override.should have(2).elements override.each {|o| o.should be_an_instance_of(Puppet::Parser::Resource) } end end diff --git a/spec/unit/parser/ast/resource_reference_spec.rb b/spec/unit/parser/ast/resource_reference_spec.rb index 492c25490..627754dd1 100755 --- a/spec/unit/parser/ast/resource_reference_spec.rb +++ b/spec/unit/parser/ast/resource_reference_spec.rb @@ -1,41 +1,40 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::ResourceReference do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new end def newref(type, title) title = stub 'title', :safeevaluate => title ref = Puppet::Parser::AST::ResourceReference.new(:type => type, :title => title) end it "should correctly produce reference strings" do newref("File", "/tmp/yay").evaluate(@scope).to_s.should == "File[/tmp/yay]" end it "should produce a single resource when the title evaluates to a string" do newref("File", "/tmp/yay").evaluate(@scope).should == Puppet::Resource.new("file", "/tmp/yay") end it "should return an array of resources if given an array of titles" do titles = mock 'titles', :safeevaluate => ["title1","title2"] ref = ast::ResourceReference.new( :title => titles, :type => "File" ) ref.evaluate(@scope).should == [ Puppet::Resource.new("file", "title1"), Puppet::Resource.new("file", "title2") ] end it "should return a correct representation when converting to string" do type = stub 'type', :is_a? => true, :to_s => "file" title = stub 'title', :is_a? => true, :to_s => "[/tmp/a, /tmp/b]" ast::ResourceReference.new( :type => type, :title => title ).to_s.should == "File[/tmp/a, /tmp/b]" end end diff --git a/spec/unit/parser/ast/resource_spec.rb b/spec/unit/parser/ast/resource_spec.rb index b00fee587..68ad9c229 100755 --- a/spec/unit/parser/ast/resource_spec.rb +++ b/spec/unit/parser/ast/resource_spec.rb @@ -1,184 +1,183 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Resource do ast = Puppet::Parser::AST describe "for builtin types" do before :each do @title = Puppet::Parser::AST::String.new(:value => "mytitle") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @scope.stubs(:resource).returns(stub_everything) @instance = ast::ResourceInstance.new(:title => @title, :parameters => ast::ASTArray.new(:children => [])) @resource = ast::Resource.new(:type => "file", :instances => ast::ASTArray.new(:children => [@instance])) @resource.stubs(:qualified_type).returns("Resource") end it "should evaluate all its parameters" do param = stub 'param' param.expects(:safeevaluate).with(@scope).returns Puppet::Parser::Resource::Param.new(:name => "myparam", :value => "myvalue", :source => stub("source")) @instance.stubs(:parameters).returns [param] @resource.evaluate(@scope) end it "should evaluate its title" do @resource.evaluate(@scope)[0].title.should == "mytitle" end it "should flatten the titles array" do titles = [] %w{one two}.each do |title| titles << Puppet::Parser::AST::String.new(:value => title) end array = Puppet::Parser::AST::ASTArray.new(:children => titles) @instance.title = array result = @resource.evaluate(@scope).collect { |r| r.title } result.should be_include("one") result.should be_include("two") end it "should create and return one resource objects per title" do titles = [] %w{one two}.each do |title| titles << Puppet::Parser::AST::String.new(:value => title) end array = Puppet::Parser::AST::ASTArray.new(:children => titles) @instance.title = array result = @resource.evaluate(@scope).collect { |r| r.title } result.should be_include("one") result.should be_include("two") end it "should implicitly iterate over instances" do new_title = Puppet::Parser::AST::String.new(:value => "other_title") new_instance = ast::ResourceInstance.new(:title => new_title, :parameters => ast::ASTArray.new(:children => [])) @resource.instances.push(new_instance) @resource.evaluate(@scope).collect { |r| r.title }.should == ["mytitle", "other_title"] end it "should handover resources to the compiler" do titles = [] %w{one two}.each do |title| titles << Puppet::Parser::AST::String.new(:value => title) end array = Puppet::Parser::AST::ASTArray.new(:children => titles) @instance.title = array result = @resource.evaluate(@scope) result.each do |res| @compiler.catalog.resource(res.ref).should be_instance_of(Puppet::Parser::Resource) end end it "should generate virtual resources if it is virtual" do @resource.virtual = true result = @resource.evaluate(@scope) result[0].should be_virtual end it "should generate virtual and exported resources if it is exported" do @resource.exported = true result = @resource.evaluate(@scope) result[0].should be_virtual result[0].should be_exported end # Related to #806, make sure resources always look up the full path to the resource. describe "when generating qualified resources" do before do @scope = Puppet::Parser::Scope.new :compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) ["one", "one::two", "three"].each do |name| @parser.environment.known_resource_types.add(Puppet::Resource::Type.new(:definition, name, {})) end @twoscope = @scope.newscope(:namespace => "one") @twoscope.resource = @scope.resource end def resource(type, params = nil) params ||= Puppet::Parser::AST::ASTArray.new(:children => []) instance = Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::String.new(:value => "myresource"), :parameters => params) Puppet::Parser::AST::Resource.new(:type => type, :instances => Puppet::Parser::AST::ASTArray.new(:children => [instance])) end it "should be able to generate resources with fully qualified type information" do resource("two").evaluate(@twoscope)[0].type.should == "One::Two" end it "should be able to generate resources with unqualified type information" do resource("one").evaluate(@twoscope)[0].type.should == "One" end it "should correctly generate resources that can look up builtin types" do resource("file").evaluate(@twoscope)[0].type.should == "File" end it "should correctly generate resources that can look up defined classes by title" do @scope.known_resource_types.add_hostclass Puppet::Resource::Type.new(:hostclass, "Myresource", {}) @scope.compiler.stubs(:evaluate_classes) res = resource("class").evaluate(@twoscope)[0] res.type.should == "Class" res.title.should == "Myresource" end it "should evaluate parameterized classes when they are instantiated" do @scope.known_resource_types.add_hostclass Puppet::Resource::Type.new(:hostclass, "Myresource", {}) @scope.compiler.expects(:evaluate_classes).with(['myresource'],@twoscope,false) resource("class").evaluate(@twoscope)[0] end it "should fail for resource types that do not exist" do lambda { resource("nosuchtype").evaluate(@twoscope) }.should raise_error(Puppet::ParseError) end end end describe "for class resources" do before do @title = Puppet::Parser::AST::String.new(:value => "classname") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @scope.stubs(:resource).returns(stub_everything) @instance = ast::ResourceInstance.new(:title => @title, :parameters => ast::ASTArray.new(:children => [])) @resource = ast::Resource.new(:type => "Class", :instances => ast::ASTArray.new(:children => [@instance])) @resource.stubs(:qualified_type).returns("Resource") @type = Puppet::Resource::Type.new(:hostclass, "classname") @compiler.known_resource_types.add(@type) end it "should instantiate the class" do @compiler.stubs(:evaluate_classes) result = @resource.evaluate(@scope) result.length.should == 1 result.first.ref.should == "Class[Classname]" @compiler.catalog.resource("Class[Classname]").should equal(result.first) end it "should cause its parent to be evaluated" do parent_type = Puppet::Resource::Type.new(:hostclass, "parentname") @compiler.stubs(:evaluate_classes) @compiler.known_resource_types.add(parent_type) @type.parent = "parentname" result = @resource.evaluate(@scope) result.length.should == 1 result.first.ref.should == "Class[Classname]" @compiler.catalog.resource("Class[Classname]").should equal(result.first) @compiler.catalog.resource("Class[Parentname]").should be_instance_of(Puppet::Parser::Resource) end end end diff --git a/spec/unit/parser/ast/selector_spec.rb b/spec/unit/parser/ast/selector_spec.rb index 4c13aa7ba..1bf5f6757 100755 --- a/spec/unit/parser/ast/selector_spec.rb +++ b/spec/unit/parser/ast/selector_spec.rb @@ -1,139 +1,138 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::Selector do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do before :each do @param = stub 'param' @param.stubs(:safeevaluate).with(@scope).returns("value") @value1 = stub 'value1' @param1 = stub_everything 'param1' @param1.stubs(:safeevaluate).with(@scope).returns(@param1) @param1.stubs(:respond_to?).with(:downcase).returns(false) @value1.stubs(:param).returns(@param1) @value1.stubs(:value).returns(@value1) @value2 = stub 'value2' @param2 = stub_everything 'param2' @param2.stubs(:safeevaluate).with(@scope).returns(@param2) @param2.stubs(:respond_to?).with(:downcase).returns(false) @value2.stubs(:param).returns(@param2) @value2.stubs(:value).returns(@value2) @values = stub 'values', :instance_of? => true @values.stubs(:each).multiple_yields(@value1, @value2) @selector = Puppet::Parser::AST::Selector.new :param => @param, :values => @values @selector.stubs(:fail) end it "should evaluate param" do @param.expects(:safeevaluate).with(@scope) @selector.evaluate(@scope) end it "should scan each option" do @values.expects(:each).multiple_yields(@value1, @value2) @selector.evaluate(@scope) end describe "when scanning values" do it "should evaluate first matching option" do @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @value2.expects(:safeevaluate).with(@scope) @selector.evaluate(@scope) end it "should return the first matching evaluated option" do @param2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @value2.stubs(:safeevaluate).with(@scope).returns(:result) @selector.evaluate(@scope).should == :result end it "should evaluate the default option if none matched" do @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) @value1.expects(:safeevaluate).with(@scope).returns(@param1) @selector.evaluate(@scope) end it "should return the default evaluated option if none matched" do result = stub 'result' @param1.stubs(:is_a?).with(Puppet::Parser::AST::Default).returns(true) @value1.stubs(:safeevaluate).returns(result) @selector.evaluate(@scope).should == result end it "should return nil if nothing matched" do @selector.evaluate(@scope).should be_nil end it "should delegate matching to evaluate_match" do @param1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } @selector.evaluate(@scope) end it "should evaluate the matching param" do @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.expects(:safeevaluate).with(@scope) @selector.evaluate(@scope) end it "should return this evaluated option if it matches" do @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.stubs(:safeevaluate).with(@scope).returns(:result) @selector.evaluate(@scope).should == :result end it "should unset scope ephemeral variables after option evaluation" do @scope.stubs(:ephemeral_level).returns(:level) @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.stubs(:safeevaluate).with(@scope).returns(:result) @scope.expects(:unset_ephemeral_var).with(:level) @selector.evaluate(@scope) end it "should not leak ephemeral variables even if evaluation fails" do @scope.stubs(:ephemeral_level).returns(:level) @param1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @value1.stubs(:safeevaluate).with(@scope).raises @scope.expects(:unset_ephemeral_var).with(:level) lambda { @selector.evaluate(@scope) }.should raise_error end it "should fail if there is no default" do @selector.expects(:fail) @selector.evaluate(@scope) end end end describe "when converting to string" do it "should produce a string version of this selector" do values = Puppet::Parser::AST::ASTArray.new :children => [ Puppet::Parser::AST::ResourceParam.new(:param => "type", :value => "value", :add => false) ] param = Puppet::Parser::AST::Variable.new :value => "myvar" selector = Puppet::Parser::AST::Selector.new :param => param, :values => values selector.to_s.should == "$myvar ? { type => value }" end end end diff --git a/spec/unit/parser/ast/vardef_spec.rb b/spec/unit/parser/ast/vardef_spec.rb index 9c8b77905..7dd2b31e7 100755 --- a/spec/unit/parser/ast/vardef_spec.rb +++ b/spec/unit/parser/ast/vardef_spec.rb @@ -1,60 +1,66 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::AST::VarDef do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do it "should evaluate arguments" do name = mock 'name' value = mock 'value' name.expects(:safeevaluate).with(@scope) value.expects(:safeevaluate).with(@scope) - vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, - :line => nil + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, :line => nil vardef.evaluate(@scope) end it "should be in append=false mode if called without append" do name = stub 'name', :safeevaluate => "var" value = stub 'value', :safeevaluate => "1" @scope.expects(:setvar).with { |name,value,options| options[:append] == nil } - vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, - :line => nil + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, :line => nil vardef.evaluate(@scope) end it "should call scope in append mode if append is true" do name = stub 'name', :safeevaluate => "var" value = stub 'value', :safeevaluate => "1" @scope.expects(:setvar).with { |name,value,options| options[:append] == true } - vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, - :line => nil, :append => true + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, :line => nil, :append => true + vardef.evaluate(@scope) + end + + it "should call pass the source location to setvar" do + name = stub 'name', :safeevaluate => "var" + value = stub 'value', :safeevaluate => "1" + + @scope.expects(:setvar).with { |name,value,options| options[:file] == 'setvar.pp' and options[:line] == 917 } + + vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => 'setvar.pp', :line => 917 vardef.evaluate(@scope) end describe "when dealing with hash" do it "should delegate to the HashOrArrayAccess assign" do access = stub 'name' access.stubs(:is_a?).with(Puppet::Parser::AST::HashOrArrayAccess).returns(true) value = stub 'value', :safeevaluate => "1" vardef = Puppet::Parser::AST::VarDef.new :name => access, :value => value, :file => nil, :line => nil access.expects(:assign).with(@scope, '1') vardef.evaluate(@scope) end end end end diff --git a/spec/unit/parser/ast_spec.rb b/spec/unit/parser/ast_spec.rb old mode 100644 new mode 100755 index cdfb51869..4d4871219 --- a/spec/unit/parser/ast_spec.rb +++ b/spec/unit/parser/ast_spec.rb @@ -1,111 +1,110 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/ast' describe Puppet::Parser::AST do it "should use the file lookup module" do Puppet::Parser::AST.ancestors.should be_include(Puppet::FileCollection::Lookup) end it "should have a doc accessor" do ast = Puppet::Parser::AST.new({}) ast.should respond_to(:doc) end it "should have a use_docs accessor to indicate it wants documentation" do ast = Puppet::Parser::AST.new({}) ast.should respond_to(:use_docs) end [ Puppet::Parser::AST::Collection, Puppet::Parser::AST::Else, Puppet::Parser::AST::Function, Puppet::Parser::AST::IfStatement, Puppet::Parser::AST::Resource, Puppet::Parser::AST::ResourceDefaults, Puppet::Parser::AST::ResourceOverride, Puppet::Parser::AST::VarDef ].each do |k| it "#{k}.use_docs should return true" do ast = k.new({}) ast.use_docs.should be_true end end describe "when initializing" do it "should store the doc argument if passed" do ast = Puppet::Parser::AST.new(:doc => "documentation") ast.doc.should == "documentation" end end end describe 'AST Generic Child' do before :each do @value = stub 'value' class Evaluateable < Puppet::Parser::AST attr_accessor :value def safeevaluate(*options) return value end end @evaluateable = Evaluateable.new(:value => @value) @scope = stubs 'scope' end describe "when evaluate_match is called" do it "should evaluate itself" do @evaluateable.expects(:safeevaluate).with(@scope) @evaluateable.evaluate_match("value", @scope) end it "should match values by equality" do @value.expects(:==).with("value").returns(true) @evaluateable.evaluate_match("value", @scope) end it "should downcase the evaluated value if wanted" do @value.expects(:downcase).returns("value") @evaluateable.evaluate_match("value", @scope) end it "should convert values to number" do Puppet::Parser::Scope.expects(:number?).with(@value).returns(2) Puppet::Parser::Scope.expects(:number?).with("23").returns(23) @evaluateable.evaluate_match("23", @scope) end it "should compare 'numberized' values" do two = stub_everything 'two' one = stub_everything 'one' Puppet::Parser::Scope.stubs(:number?).with(@value).returns(one) Puppet::Parser::Scope.stubs(:number?).with("2").returns(two) one.expects(:==).with(two) @evaluateable.evaluate_match("2", @scope) end it "should match undef if value is an empty string" do @evaluateable.value = '' @evaluateable.evaluate_match(:undef, @scope).should be_true end it "should downcase the parameter value if wanted" do parameter = stub 'parameter' parameter.expects(:downcase).returns("value") @evaluateable.evaluate_match(parameter, @scope) end it "should not match '' if value is undef" do @evaluateable.value = :undef @evaluateable.evaluate_match('', @scope).should be_false end end end diff --git a/spec/unit/parser/collector_spec.rb b/spec/unit/parser/collector_spec.rb index 100a04daf..01918d2a0 100755 --- a/spec/unit/parser/collector_spec.rb +++ b/spec/unit/parser/collector_spec.rb @@ -1,556 +1,555 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/rails' require 'puppet/parser/collector' describe Puppet::Parser::Collector, "when initializing" do before do @scope = mock 'scope' @resource_type = 'resource_type' @form = :exported @vquery = mock 'vquery' @equery = mock 'equery' @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, @form) end it "should require a scope" do @collector.scope.should equal(@scope) end it "should require a resource type" do @collector.type.should == 'Resource_type' end it "should only accept :virtual or :exported as the collector form" do proc { @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @vquery, @equery, :other) }.should raise_error(ArgumentError) end it "should accept an optional virtual query" do @collector.vquery.should equal(@vquery) end it "should accept an optional exported query" do @collector.equery.should equal(@equery) end it "should canonize the type name" do @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) @collector.type.should == "Resource::Type" end it "should accept an optional resource override" do @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) override = { :parameters => "whatever" } @collector.add_override(override) @collector.overrides.should equal(override) end end describe Puppet::Parser::Collector, "when collecting specific virtual resources" do before do @scope = mock 'scope' @vquery = mock 'vquery' @equery = mock 'equery' @collector = Puppet::Parser::Collector.new(@scope, "resource_type", @equery, @vquery, :virtual) end it "should not fail when it does not find any resources to collect" do @collector.resources = ["File[virtual1]", "File[virtual2]"] @scope.stubs(:findresource).returns(false) proc { @collector.evaluate }.should_not raise_error end it "should mark matched resources as non-virtual" do @collector.resources = ["File[virtual1]", "File[virtual2]"] one = stub_everything 'one' one.expects(:virtual=).with(false) @scope.stubs(:findresource).with("File[virtual1]").returns(one) @scope.stubs(:findresource).with("File[virtual2]").returns(nil) @collector.evaluate end it "should return matched resources" do @collector.resources = ["File[virtual1]", "File[virtual2]"] one = stub_everything 'one' @scope.stubs(:findresource).with("File[virtual1]").returns(one) @scope.stubs(:findresource).with("File[virtual2]").returns(nil) @collector.evaluate.should == [one] end it "should delete itself from the compile's collection list if it has found all of its resources" do @collector.resources = ["File[virtual1]"] one = stub_everything 'one' @compiler.expects(:delete_collection).with(@collector) @scope.expects(:compiler).returns(@compiler) @scope.stubs(:findresource).with("File[virtual1]").returns(one) @collector.evaluate end it "should not delete itself from the compile's collection list if it has unfound resources" do @collector.resources = ["File[virtual1]"] one = stub_everything 'one' @compiler.expects(:delete_collection).never @scope.stubs(:findresource).with("File[virtual1]").returns(nil) @collector.evaluate end end describe Puppet::Parser::Collector, "when collecting virtual and catalog resources" do before do @scope = mock 'scope' @compiler = mock 'compile' @scope.stubs(:compiler).returns(@compiler) @resource_type = "Mytype" @vquery = proc { |res| true } @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, @vquery, :virtual) end it "should find all virtual resources matching the vquery" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => "Mytype", :virtual? => true @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should find all non-virtual resources matching the vquery" do one = stub_everything 'one', :type => "Mytype", :virtual? => false two = stub_everything 'two', :type => "Mytype", :virtual? => false @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should mark all matched resources as non-virtual" do one = stub_everything 'one', :type => "Mytype", :virtual? => true one.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one]) @collector.evaluate end it "should return matched resources" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => "Mytype", :virtual? => true @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should return all resources of the correct type if there is no virtual query" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => "Mytype", :virtual? => true one.expects(:virtual=).with(false) two.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one, two]) @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, nil, :virtual) @collector.evaluate.should == [one, two] end it "should not return or mark resources of a different type" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => :other, :virtual? => true one.expects(:virtual=).with(false) two.expects(:virtual=).never @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one] end it "should create a resource with overridden parameters" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' @compiler.stubs(:add_override) @compiler.expects(:resources).returns([one]) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.expects(:new).with { |type, title, h| h[:parameters] == param } @collector.evaluate end it "should define a new allow all child_of? on overriden resource" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' source = stub 'source' @compiler.stubs(:add_override) @compiler.expects(:resources).returns([one]) @collector.add_override(:parameters => param, :source => source ) Puppet::Parser::Resource.stubs(:new) source.expects(:meta_def).with { |name,block| name == :child_of? } @collector.evaluate end it "should not override already overriden resources for this same collection in a previous run" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' @compiler.stubs(:add_override) @compiler.expects(:resources).at_least(2).returns([one]) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| h[:parameters] == param } @collector.evaluate @collector.evaluate end it "should not return resources that were collected in a previous run of this collector" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" @compiler.stubs(:resources).returns([one]) @collector.evaluate @collector.evaluate.should be_false end it "should tell the compiler about the overriden resources" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' one.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one]) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.stubs(:new).returns("whatever") @compiler.expects(:add_override).with("whatever") @collector.evaluate end it "should not return or mark non-matching resources" do @collector.vquery = proc { |res| res.name == :one } one = stub_everything 'one', :name => :one, :type => "Mytype", :virtual? => true two = stub_everything 'two', :name => :two, :type => "Mytype", :virtual? => true one.expects(:virtual=).with(false) two.expects(:virtual=).never @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one] end end describe Puppet::Parser::Collector, "when collecting exported resources", :if => Puppet.features.rails? do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new :compiler => @compiler @resource_type = "Mytype" @equery = "test = true" @vquery = proc { |r| true } res = stub("resource 1") res.stubs(:type).returns @resource_type Puppet::Resource.stubs(:new).returns res Puppet.settings.stubs(:value).with(:storeconfigs).returns true Puppet.settings.stubs(:value).with(:environment).returns "production" @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) end # Stub most of our interface to Rails. def stub_rails(everything = false) ActiveRecord::Base.stubs(:connected?).returns(false) Puppet::Rails.stubs(:init) if everything Puppet::Rails::Host.stubs(:find_by_name).returns(nil) Puppet::Rails::Resource.stubs(:find).returns([]) end end it "should just return false if :storeconfigs is not enabled" do Puppet.settings.expects(:value).with(:storeconfigs).returns false @collector.evaluate.should be_false end it "should use initialize the Rails support if ActiveRecord is not connected" do @compiler.stubs(:resources).returns([]) ActiveRecord::Base.expects(:connected?).returns(false) Puppet::Rails.expects(:init) Puppet::Rails::Host.stubs(:find_by_name).returns(nil) Puppet::Rails::Resource.stubs(:find).returns([]) @collector.evaluate end it "should return all matching resources from the current compile and mark them non-virtual and non-exported" do stub_rails(true) one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "one" two = stub 'two', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "two" one.stubs(:exported=) one.stubs(:virtual=) two.stubs(:exported=) two.stubs(:virtual=) @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should mark all returned resources as not virtual" do stub_rails(true) one = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :ref => "one" one.stubs(:exported=) one.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one]) @collector.evaluate.should == [one] end it "should convert all found resources into parser resources" do stub_rails Puppet::Rails::Host.stubs(:find_by_name).returns(nil) one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" Puppet::Rails::Resource.stubs(:find).returns([one]) resource = mock 'resource' one.expects(:to_resource).with(@scope).returns(resource) resource.stubs(:exported=) resource.stubs(:virtual=) resource.stubs(:ref) @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) @compiler.stubs(:add_resource) @collector.evaluate.should == [resource] end it "should override all exported collected resources if collector has an override" do stub_rails Puppet::Rails::Host.stubs(:find_by_name).returns(nil) one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" Puppet::Rails::Resource.stubs(:find).returns([one]) resource = mock 'resource', :type => "Mytype" one.expects(:to_resource).with(@scope).returns(resource) resource.stubs(:exported=) resource.stubs(:virtual=) resource.stubs(:ref) resource.stubs(:title) @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) param = stub 'param' @compiler.stubs(:add_override) @compiler.stubs(:add_resource) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| h[:parameters] == param } @collector.evaluate end it "should store converted resources in the compile's resource list" do stub_rails Puppet::Rails::Host.stubs(:find_by_name).returns(nil) one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" Puppet::Rails::Resource.stubs(:find).returns([one]) resource = mock 'resource' one.expects(:to_resource).with(@scope).returns(resource) resource.stubs(:exported=) resource.stubs(:virtual=) resource.stubs(:ref) @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) @compiler.expects(:add_resource).with(@scope, resource) @collector.evaluate.should == [resource] end # This way one host doesn't store another host's resources as exported. it "should mark resources collected from the database as not exported" do stub_rails Puppet::Rails::Host.stubs(:find_by_name).returns(nil) one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :ref => "one" Puppet::Rails::Resource.stubs(:find).returns([one]) resource = mock 'resource' one.expects(:to_resource).with(@scope).returns(resource) resource.expects(:exported=).with(false) resource.stubs(:virtual=) resource.stubs(:ref) @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) @compiler.stubs(:add_resource) @collector.evaluate end it "should fail if an equivalent resource already exists in the compile" do stub_rails Puppet::Rails::Host.stubs(:find_by_name).returns(nil) rails = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :id => 1, :ref => "yay" inmemory = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :rails_id => 2 Puppet::Rails::Resource.stubs(:find).returns([rails]) resource = mock 'resource' @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(inmemory) @compiler.stubs(:add_resource) proc { @collector.evaluate }.should raise_error(Puppet::ParseError) end it "should ignore exported resources that match already-collected resources" do stub_rails Puppet::Rails::Host.stubs(:find_by_name).returns(nil) rails = stub 'one', :restype => "Mytype", :title => "one", :virtual? => true, :exported? => true, :id => 1, :ref => "yay" inmemory = stub 'one', :type => "Mytype", :virtual? => true, :exported? => true, :rails_id => 1 Puppet::Rails::Resource.stubs(:find).returns([rails]) resource = mock 'resource' @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(inmemory) @compiler.stubs(:add_resource) proc { @collector.evaluate }.should_not raise_error(Puppet::ParseError) end end describe Puppet::Parser::Collector, "when building its ActiveRecord query for collecting exported resources", :if => Puppet.features.rails? do before do @scope = stub 'scope', :host => "myhost", :debug => nil @compiler = mock 'compile' @scope.stubs(:compiler).returns(@compiler) @resource_type = "Mytype" @equery = nil @vquery = proc { |r| true } @resource = stub_everything 'collected' @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) @collector.stubs(:exported_resource).with(@resource).returns(@resource) @compiler.stubs(:resources).returns([]) ActiveRecord::Base.stubs(:connected?).returns(false) Puppet::Rails.stubs(:init) Puppet::Rails::Host.stubs(:find_by_name).returns(nil) Puppet::Rails::Resource.stubs(:find).returns([]) Puppet.settings.stubs(:value).with(:storeconfigs).returns true end it "should exclude all resources from the host if ActiveRecord contains information for this host" do @host = mock 'host' @host.stubs(:id).returns 5 Puppet::Rails::Host.expects(:find_by_name).with(@scope.host).returns(@host) Puppet::Rails::Resource.stubs(:find).with { |*arguments| options = arguments[1] options[:conditions][0] =~ /^host_id != \?/ and options[:conditions][1] == 5 }.returns([@resource]) @collector.evaluate.should == [@resource] end it "should join with parameter names, parameter values when querying ActiveRecord" do @collector.equery = "param_names.name = title" Puppet::Rails::Resource.stubs(:find).with { |*arguments| options = arguments[1] options[:joins] == {:param_values => :param_name} }.returns([@resource]) @collector.evaluate.should == [@resource] end it "should join with tag tables when querying ActiveRecord with a tag exported query" do @collector.equery = "puppet_tags.name = test" Puppet::Rails::Resource.stubs(:find).with { |*arguments| options = arguments[1] options[:joins] == {:resource_tags => :puppet_tag} }.returns([@resource]) @collector.evaluate.should == [@resource] end it "should not join parameters when querying ActiveRecord with a tag exported query" do @collector.equery = "puppet_tags.name = test" Puppet::Rails::Resource.stubs(:find).with { |*arguments| options = arguments[1] options[:joins] == {:param_values => :param_name} }.returns([@resource]) @collector.evaluate.should be_false end it "should only search for exported resources with the matching type" do Puppet::Rails::Resource.stubs(:find).with { |*arguments| options = arguments[1] options[:conditions][0].include?("(exported=? AND restype=?)") and options[:conditions][1] == true and options[:conditions][2] == "Mytype" }.returns([@resource]) @collector.evaluate.should == [@resource] end it "should include the export query if one is provided" do @collector.equery = "test = true" Puppet::Rails::Resource.stubs(:find).with { |*arguments| options = arguments[1] options[:conditions][0].include?("test = true") }.returns([@resource]) @collector.evaluate.should == [@resource] end end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index e4b18e14b..ced760b76 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -1,836 +1,831 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' class CompilerTestResource attr_accessor :builtin, :virtual, :evaluated, :type, :title def initialize(type, title) @type = type @title = title end def [](attr) return nil if attr == :stage :main end def ref "#{type.to_s.capitalize}[#{title}]" end def evaluated? @evaluated end def builtin_type? @builtin end def virtual? @virtual end def evaluate end end describe Puppet::Parser::Compiler do def resource(type, title) Puppet::Parser::Resource.new(type, title, :scope => @scope) end before :each do @node = Puppet::Node.new "testnode" @known_resource_types = Puppet::Resource::TypeCollection.new "development" @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => stub('source')) @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) @scope.resource = @scope_resource @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end it "should have a class method that compiles, converts, and returns a catalog" do compiler = stub 'compiler' Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler catalog = stub 'catalog' compiler.expects(:compile).returns catalog converted_catalog = stub 'converted_catalog' catalog.expects(:to_resource).returns converted_catalog Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) end it "should fail intelligently when a class-level compile fails" do Puppet::Parser::Compiler.expects(:new).raises ArgumentError lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) end it "should use the node's environment as its environment" do @compiler.environment.should equal(@node.environment) end it "should include the resource type collection helper" do Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should be able to return a class list containing all added classes" do @compiler.add_class "" @compiler.add_class "one" @compiler.add_class "two" @compiler.classlist.sort.should == %w{one two}.sort end describe "when initializing" do it "should set its node attribute" do @compiler.node.should equal(@node) end it "should detect when ast nodes are absent" do @compiler.ast_nodes?.should be_false end it "should detect when ast nodes are present" do @known_resource_types.expects(:nodes?).returns true @compiler.ast_nodes?.should be_true end it "should copy the known_resource_types version to the catalog" do @compiler.catalog.version.should == @known_resource_types.version end it "should copy any node classes into the class list" do node = Puppet::Node.new("mynode") node.classes = %w{foo bar} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should transform node class hashes into a class list" do node = Puppet::Node.new("mynode") node.classes = {'foo'=>{'one'=>'1'}, 'bar'=>{'two'=>'2'}} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should add a 'main' stage to the catalog" do @compiler.catalog.resource(:stage, :main).should be_instance_of(Puppet::Parser::Resource) end end describe "when managing scopes" do it "should create a top scope" do @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) end it "should be able to create new scopes" do @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) end - it "should correctly set the level of newly created scopes" do - @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5 - end - it "should set the parent scope of the new scope to be the passed-in parent" do scope = mock 'scope' newscope = @compiler.newscope(scope) newscope.parent.should equal(scope) end it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do scope = mock 'scope' newscope = @compiler.newscope(nil) newscope.parent.should equal(@compiler.topscope) end end describe "when compiling" do def compile_methods [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, :finish, :store, :extract, :evaluate_relationships] end # Stub all of the main compile methods except the ones we're specifically interested in. def compile_stub(*except) (compile_methods - except).each { |m| @compiler.stubs(m) } end it "should set node parameters as variables in the top scope" do params = {"a" => "b", "c" => "d"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.topscope.lookupvar("a").should == "b" @compiler.topscope.lookupvar("c").should == "d" end it "should set the client and server versions on the catalog" do params = {"clientversion" => "2", "serverversion" => "3"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.catalog.client_version.should == "2" @compiler.catalog.server_version.should == "3" end it "should evaluate any existing classes named in the node" do classes = %w{one two three four} main = stub 'main' one = stub 'one', :name => "one" three = stub 'three', :name => "three" @node.stubs(:name).returns("whatever") @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } end it "should evaluate any parameterized classes named in the node" do classes = {'foo'=>{'1'=>'one'}, 'bar'=>{'2'=>'two'}} @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.compile end it "should evaluate the main class if it exists" do compile_stub(:evaluate_main) main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } @compiler.topscope.expects(:source=).with(main_class) @compiler.compile end it "should create a new, empty 'main' if no main class exists" do compile_stub(:evaluate_main) @compiler.compile @known_resource_types.find_hostclass([""], "").should be_instance_of(Puppet::Resource::Type) end it "should add an edge between the main stage and main class" do @compiler.compile (stage = @compiler.catalog.resource(:stage, "main")).should be_instance_of(Puppet::Parser::Resource) (klass = @compiler.catalog.resource(:class, "")).should be_instance_of(Puppet::Parser::Resource) @compiler.catalog.edge?(stage, klass).should be_true end it "should evaluate any node classes" do @node.stubs(:classes).returns(%w{one two three four}) @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) @compiler.send(:evaluate_node_classes) end it "should evaluate all added collections" do colls = [] # And when the collections fail to evaluate. colls << mock("coll1-false") colls << mock("coll2-false") colls.each { |c| c.expects(:evaluate).returns(false) } @compiler.add_collection(colls[0]) @compiler.add_collection(colls[1]) compile_stub(:evaluate_generators) @compiler.compile end it "should ignore builtin resources" do resource = resource(:file, "testing") @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } @compiler.compile end it "should not evaluate already-evaluated resources" do resource = resource(:file, "testing") resource.stubs(:evaluated?).returns true @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources created by evaluating other resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) resource2 = CompilerTestResource.new(:file, "other") # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } @compiler.compile end describe "when finishing" do before do @compiler.send(:evaluate_main) @catalog = @compiler.catalog end def add_resource(name, parent = nil) resource = Puppet::Parser::Resource.new "file", name, :scope => @scope @compiler.add_resource(@scope, resource) @catalog.add_edge(parent, resource) if parent resource end it "should call finish() on all resources" do # Add a resource that does respond to :finish resource = Puppet::Parser::Resource.new "file", "finish", :scope => @scope resource.expects(:finish) @compiler.add_resource(@scope, resource) # And one that does not dnf_resource = stub_everything "dnf", :ref => "File[dnf]", :type => "file" @compiler.add_resource(@scope, dnf_resource) @compiler.send(:finish) end it "should call finish() in add_resource order" do resources = sequence('resources') resource1 = add_resource("finish1") resource1.expects(:finish).in_sequence(resources) resource2 = add_resource("finish2") resource2.expects(:finish).in_sequence(resources) @compiler.send(:finish) end it "should add each container's metaparams to its contained resources" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) @compiler.send(:finish) resource1[:noop].should be_true end it "should add metaparams recursively" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end it "should prefer metaparams from immediate parents" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) resource1[:noop] = false @compiler.send(:finish) resource2[:noop].should be_false end it "should merge tags downward" do main = @catalog.resource(:class, :main) main.tag("one") resource1 = add_resource("meh", main) resource1.tag "two" resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2.tags.should be_include("one") resource2.tags.should be_include("two") end it "should work if only middle resources have metaparams set" do main = @catalog.resource(:class, :main) resource1 = add_resource("meh", main) resource1[:noop] = true resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end end it "should return added resources in add order" do resource1 = resource(:file, "yay") @compiler.add_resource(@scope, resource1) resource2 = resource(:file, "youpi") @compiler.add_resource(@scope, resource2) @compiler.resources.should == [resource1, resource2] end it "should add resources that do not conflict with existing resources" do resource = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_vertex(resource) end it "should fail to add resources that conflict with existing resources" do path = Puppet.features.posix? ? "/foo" : "C:/foo" file1 = Puppet::Type.type(:file).new :path => path file2 = Puppet::Type.type(:file).new :path => path @compiler.add_resource(@scope, file1) lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should add an edge from the scope resource to the added resource" do resource = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should add an edge to any specified stage for class resources" do other_stage = resource(:stage, "other") @compiler.add_resource(@scope, other_stage) resource = resource(:class, "foo") resource[:stage] = 'other' @compiler.add_resource(@scope, resource) @compiler.catalog.edge?(other_stage, resource).should be_true end it "should fail if a non-class resource attempts to set a stage" do other_stage = resource(:stage, "other") @compiler.add_resource(@scope, other_stage) resource = resource(:file, "foo") resource[:stage] = 'other' lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) end it "should fail if an unknown stage is specified" do resource = resource(:class, "foo") resource[:stage] = 'other' lambda { @compiler.add_resource(@scope, resource) }.should raise_error(ArgumentError) end it "should add edges from the class resources to the parent's stage if no stage is specified" do main = @compiler.catalog.resource(:stage, :main) foo_stage = resource(:stage, :foo_stage) @compiler.add_resource(@scope, foo_stage) resource = resource(:class, "foo") @scope.stubs(:resource).returns(:stage => :foo_stage) @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(foo_stage, resource) end it "should add edges from top-level class resources to the main stage if no stage is specified" do main = @compiler.catalog.resource(:stage, :main) resource = resource(:class, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(main, resource) end it "should not add non-class resources that don't specify a stage to the 'main' stage" do main = @compiler.catalog.resource(:stage, :main) resource = resource(:file, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should_not be_edge(main, resource) end it "should not add any parent-edges to stages" do stage = resource(:stage, "other") @compiler.add_resource(@scope, stage) @scope.resource = resource(:class, "foo") @compiler.catalog.edge?(@scope.resource, stage).should be_false end it "should not attempt to add stages to other stages" do other_stage = resource(:stage, "other") second_stage = resource(:stage, "second") @compiler.add_resource(@scope, other_stage) @compiler.add_resource(@scope, second_stage) second_stage[:stage] = "other" @compiler.catalog.edge?(other_stage, second_stage).should be_false end it "should have a method for looking up resources" do resource = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay[foo]").should equal(resource) end it "should be able to look resources up by type and title" do resource = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay", "foo").should equal(resource) end it "should not evaluate virtual defined resources" do resource = resource(:file, "testing") resource.virtual = true @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end end describe "when evaluating collections" do it "should evaluate each collection" do 2.times { |i| coll = mock 'coll%s' % i @compiler.add_collection(coll) # This is the hard part -- we have to emulate the fact that # collections delete themselves if they are done evaluating. coll.expects(:evaluate).with do @compiler.delete_collection(coll) end } @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } end it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(nil) @compiler.add_collection(coll) lambda { @compiler.compile }.should_not raise_error end it "should fail when there are unevaluated resource collections that refer to a specific resource" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(:something) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources something' end it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns([:one, :two]) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources one, two' end end describe "when evaluating relationships" do it "should evaluate each relationship with its catalog" do dep = stub 'dep' dep.expects(:evaluate).with(@compiler.catalog) @compiler.add_relationship dep @compiler.evaluate_relationships end end describe "when told to evaluate missing classes" do it "should fail if there's no source listed for the scope" do scope = stub 'scope', :source => nil proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) end it "should raise an error if a class is not found" do @scope.expects(:find_hostclass).with("notfound").returns(nil) lambda{ @compiler.evaluate_classes(%w{notfound}, @scope) }.should raise_error(Puppet::Error, /Could not find class/) end it "should raise an error when it can't find class" do klasses = {'foo'=>nil} @node.classes = klasses @compiler.topscope.stubs(:find_hostclass).with('foo').returns(nil) lambda{ @compiler.compile }.should raise_error(Puppet::Error, /Could not find class foo for testnode/) end end describe "when evaluating found classes" do before do @class = stub 'class', :name => "my::class" @scope.stubs(:find_hostclass).with("myclass").returns(@class) @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" end it "should evaluate each class" do @compiler.catalog.stubs(:tag) @class.expects(:ensure_in_catalog).with(@scope) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should ensure each node class hash is in catalog and have appropriate parameters" do klasses = {'foo'=>{'1'=>'one'}, 'bar::foo'=>{'2'=>'two'}, 'bar'=>{'1'=> [1,2,3], '2'=>{'foo'=>'bar'}}} @node.classes = klasses ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') klasses.each do |name, params| klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj}) @compiler.topscope.known_resource_types.add klass end catalog = @compiler.compile catalog.classes.should =~ ['foo', 'bar::foo', 'settings', 'bar'] r1 = catalog.resources.detect {|r| r.title == 'Foo' } r1.to_hash.should == {:'1' => 'one', :'2' => 'foo'} r1.tags. should =~ ['class', 'foo'] r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } r2.to_hash.should == {:'1' => 'foo', :'2' => 'two'} r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo'] r2 = catalog.resources.detect {|r| r.title == 'Bar' } r2.to_hash.should == {:'1' => [1,2,3], :'2' => {'foo'=>'bar'}} r2.tags.should =~ ['class', 'bar'] end it "should ensure each node class is in catalog and has appropriate tags" do klasses = ['bar::foo'] @node.classes = klasses ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') klasses.each do |name| klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj}) @compiler.topscope.known_resource_types.add klass end catalog = @compiler.compile r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo'] end it "should fail if required parameters are missing" do klass = {'foo'=>{'1'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error Puppet::ParseError, "Must pass 2 to Class[Foo]" end it "should fail if invalid parameters are passed" do klass = {'foo'=>{'3'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error Puppet::ParseError, "Invalid parameter 3" end it "should ensure class is in catalog without params" do @node.classes = klasses = {'foo'=>nil} foo = Puppet::Resource::Type.new(:hostclass, 'foo') @compiler.topscope.known_resource_types.add foo catalog = @compiler.compile catalog.classes.should include 'foo' end it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate).never @class.expects(:ensure_in_catalog).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should immediately evaluate the resources created for found classes when asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate) @class.expects(:ensure_in_catalog).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes that have already been evaluated" do @compiler.catalog.stubs(:tag) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes previously evaluated with different capitalization" do @compiler.catalog.stubs(:tag) @scope.stubs(:find_hostclass).with("MyClass").returns(@class) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{MyClass}, @scope, false) end end describe "when evaluating AST nodes with no AST nodes present" do it "should do nothing" do @compiler.expects(:ast_nodes?).returns(false) @compiler.known_resource_types.expects(:nodes).never Puppet::Parser::Resource.expects(:new).never @compiler.send(:evaluate_ast_node) end end describe "when evaluating AST nodes with AST nodes present" do before do @compiler.known_resource_types.stubs(:nodes?).returns true # Set some names for our test @node.stubs(:names).returns(%w{a b c}) @compiler.known_resource_types.stubs(:node).with("a").returns(nil) @compiler.known_resource_types.stubs(:node).with("b").returns(nil) @compiler.known_resource_types.stubs(:node).with("c").returns(nil) # It should check this last, of course. @compiler.known_resource_types.stubs(:node).with("default").returns(nil) end it "should fail if the named node cannot be found" do proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) end it "should evaluate the first node class matching the node name" do node_class = stub 'node', :name => "c", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) @compiler.compile end it "should match the default node if no matching node can be found" do node_class = stub 'node', :name => "default", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) @compiler.compile end it "should evaluate the node resource immediately rather than using lazy evaluation" do node_class = stub 'node', :name => "c" @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) node_resource.expects(:evaluate) @compiler.send(:evaluate_ast_node) end it "should set the node's scope as the top scope" do node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class = stub 'node', :name => "c", :ensure_in_catalog => node_resource @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) # The #evaluate method normally does this. scope = stub 'scope', :source => "mysource" @compiler.topscope.expects(:class_scope).with(node_class).returns(scope) node_resource.stubs(:evaluate) @compiler.stubs :create_settings_scope @compiler.compile @compiler.topscope.should equal(scope) end end describe "when managing resource overrides" do before do @override = stub 'override', :ref => "File[/foo]", :type => "my" @resource = resource(:file, "/foo") end it "should be able to store overrides" do lambda { @compiler.add_override(@override) }.should_not raise_error end it "should apply overrides to the appropriate resources" do @compiler.add_resource(@scope, @resource) @resource.expects(:merge).with(@override) @compiler.add_override(@override) @compiler.compile end it "should accept overrides before the related resource has been created" do @resource.expects(:merge).with(@override) # First store the override @compiler.add_override(@override) # Then the resource @compiler.add_resource(@scope, @resource) # And compile, so they get resolved @compiler.compile end it "should fail if the compile is finished and resource overrides have not been applied" do @compiler.add_override(@override) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Could not find resource(s) File[/foo] for overriding' end end end diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb old mode 100644 new mode 100755 index 525c83697..04777f0ec --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -1,201 +1,200 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/files' describe Puppet::Parser::Files do before do @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" end it "should have a method for finding a template" do Puppet::Parser::Files.should respond_to(:find_template) end it "should have a method for finding manifests" do Puppet::Parser::Files.should respond_to(:find_manifests) end describe "when searching for templates" do it "should return fully-qualified templates directly" do Puppet::Parser::Files.expects(:modulepath).never Puppet::Parser::Files.find_template(@basepath + "/my/template").should == @basepath + "/my/template" end it "should return the template from the first found module" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/one/mymod/templates/mytemplate") Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" end it "should return the file in the templatedir if it exists" do Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") Puppet[:modulepath] = "/one:/two" File.stubs(:directory?).returns(true) FileTest.stubs(:exist?).returns(true) Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" end it "should not raise an error if no valid templatedir exists and the template exists in a module" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/one/mymod/templates/mytemplate") Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(nil) Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" end it "should return unqualified templates if they exist in the template dir" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should only return templates if they actually exist" do FileTest.expects(:exist?).with("/my/templates/mytemplate").returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should return nil when asked for a template that doesn't exist" do FileTest.expects(:exist?).with("/my/templates/mytemplate").returns false Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should be_nil end it "should search in the template directories before modules" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).never Puppet::Parser::Files.find_template("mytemplate") end it "should accept relative templatedirs" do FileTest.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" File.expects(:directory?).with(File.join(Dir.getwd,"my/templates")).returns(true) Puppet::Parser::Files.find_template("mytemplate").should == File.join(Dir.getwd,"my/templates/mytemplate") end it "should use the environment templatedir if no module is found and an environment is specified" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use first dir from environment templatedir if no module is found and an environment is specified" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(false) FileTest.expects(:exist?).with("/two/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" end it "should use the node environment if specified" do mod = mock 'module' Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") Puppet::Parser::Files.find_template("mymod/envtemplate", "myenv").should == "/my/modules/mymod/templates/envtemplate" end it "should return nil if no template can be found" do Puppet::Parser::Files.find_template("foomod/envtemplate", "myenv").should be_nil end after { Puppet.settings.clear } end describe "when searching for manifests" do it "should ignore invalid modules" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") Puppet.expects(:value).with(:modulepath).never Dir.stubs(:glob).returns %w{foo} Puppet::Parser::Files.find_manifests("mymod/init.pp") end end describe "when searching for manifests when no module is found" do before do File.stubs(:find).returns(nil) end it "should not look for modules when paths are fully qualified" do Puppet.expects(:value).with(:modulepath).never file = @basepath + "/fully/qualified/file.pp" Dir.stubs(:glob).with(file).returns([file]) Puppet::Parser::Files.find_manifests(file) end it "should return nil and an array of fully qualified files" do file = @basepath + "/fully/qualified/file.pp" Dir.stubs(:glob).with(file).returns([file]) Puppet::Parser::Files.find_manifests(file).should == [nil, [file]] end it "should match against provided fully qualified patterns" do pattern = @basepath + "/fully/qualified/pattern/*" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns(%w{my file list}) Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{my file list} end it "should look for files relative to the current directory" do cwd = Dir.getwd Dir.expects(:glob).with("#{cwd}/foobar/init.pp").returns(["#{cwd}/foobar/init.pp"]) Puppet::Parser::Files.find_manifests("foobar/init.pp")[1].should == ["#{cwd}/foobar/init.pp"] end it "should only return files, not directories" do pattern = @basepath + "/fully/qualified/pattern/*" file = @basepath + "/my/file" dir = @basepath + "/my/directory" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns([file, dir]) FileTest.expects(:directory?).with(file).returns(false) FileTest.expects(:directory?).with(dir).returns(true) Puppet::Parser::Files.find_manifests(pattern)[1].should == [file] end it "should return files once only" do pattern = @basepath + "/fully/qualified/pattern/*" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns(%w{one two one}) Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{one two} end end describe "when searching for manifests in a found module" do it "should return the name of the module and the manifests from the first found module" do mod = Puppet::Module.new("mymod") Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) Puppet::Parser::Files.find_manifests("mymod/init.pp").should == ["mymod", ["/one/mymod/manifests/init.pp"]] end it "should use the node environment if specified" do mod = Puppet::Module.new("mymod") Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) Puppet::Parser::Files.find_manifests("mymod/init.pp", :environment => "myenv")[1].should == ["/one/mymod/manifests/init.pp"] end after { Puppet.settings.clear } end end diff --git a/spec/unit/parser/functions/create_resources_spec.rb b/spec/unit/parser/functions/create_resources_spec.rb index 366fb536c..88a67e369 100755 --- a/spec/unit/parser/functions/create_resources_spec.rb +++ b/spec/unit/parser/functions/create_resources_spec.rb @@ -1,137 +1,137 @@ require 'puppet' -require File.dirname(__FILE__) + '/../../../spec_helper' +require 'spec_helper' describe 'function for dynamically creating resources' do def get_scope @topscope = Puppet::Parser::Scope.new # This is necessary so we don't try to use the compiler to discover our parent. @topscope.parent = nil @scope = Puppet::Parser::Scope.new @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) @scope.parent = @topscope @compiler = @scope.compiler end before :each do get_scope Puppet::Parser::Functions.function(:create_resources) end it "should exist" do Puppet::Parser::Functions.function(:create_resources).should == "function_create_resources" end it 'should require two arguments' do lambda { @scope.function_create_resources(['foo']) }.should raise_error(ArgumentError, 'create_resources(): wrong number of arguments (1; must be 2)') end describe 'when creating native types' do before :each do Puppet[:code]='notify{test:}' get_scope @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) end it 'empty hash should not cause resources to be added' do @scope.function_create_resources(['file', {}]) @compiler.catalog.resources.size == 1 end it 'should be able to add' do @scope.function_create_resources(['file', {'/etc/foo'=>{'ensure'=>'present'}}]) @compiler.catalog.resource(:file, "/etc/foo")['ensure'].should == 'present' end it 'should accept multiple types' do type_hash = {} type_hash['foo'] = {'message' => 'one'} type_hash['bar'] = {'message' => 'two'} @scope.function_create_resources(['notify', type_hash]) @compiler.catalog.resource(:notify, "foo")['message'].should == 'one' @compiler.catalog.resource(:notify, "bar")['message'].should == 'two' end it 'should fail to add non-existing type' do lambda { @scope.function_create_resources(['foo', {}]) }.should raise_error(ArgumentError, 'could not create resource of unknown type foo') end it 'should be able to add edges' do @scope.function_create_resources(['notify', {'foo'=>{'require' => 'Notify[test]'}}]) @scope.compiler.compile rg = @scope.compiler.catalog.to_ral.relationship_graph test = rg.vertices.find { |v| v.title == 'test' } foo = rg.vertices.find { |v| v.title == 'foo' } test.should be foo.should be rg.path_between(test,foo).should be end end describe 'when dynamically creating resource types' do before :each do Puppet[:code]= 'define foo($one){notify{$name: message => $one}} notify{test:} ' get_scope @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) Puppet::Parser::Functions.function(:create_resources) end it 'should be able to create defined resoure types' do @scope.function_create_resources(['foo', {'blah'=>{'one'=>'two'}}]) # still have to compile for this to work... # I am not sure if this constraint ruins the tests @scope.compiler.compile @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' end it 'should fail if defines are missing params' do @scope.function_create_resources(['foo', {'blah'=>{}}]) lambda { @scope.compiler.compile }.should raise_error(Puppet::ParseError, 'Must pass one to Foo[blah] at line 1') end it 'should be able to add multiple defines' do hash = {} hash['blah'] = {'one' => 'two'} hash['blaz'] = {'one' => 'three'} @scope.function_create_resources(['foo', hash]) # still have to compile for this to work... # I am not sure if this constraint ruins the tests @scope.compiler.compile @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' @compiler.catalog.resource(:notify, "blaz")['message'].should == 'three' end it 'should be able to add edges' do @scope.function_create_resources(['foo', {'blah'=>{'one'=>'two', 'require' => 'Notify[test]'}}]) @scope.compiler.compile rg = @scope.compiler.catalog.to_ral.relationship_graph test = rg.vertices.find { |v| v.title == 'test' } blah = rg.vertices.find { |v| v.title == 'blah' } test.should be blah.should be # (Yoda speak like we do) rg.path_between(test,blah).should be @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' end end describe 'when creating classes' do before :each do Puppet[:code]= 'class bar($one){notify{test: message => $one}} notify{tester:} ' get_scope @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) Puppet::Parser::Functions.function(:create_resources) end it 'should be able to create classes' do @scope.function_create_resources(['class', {'bar'=>{'one'=>'two'}}]) @scope.compiler.compile @compiler.catalog.resource(:notify, "test")['message'].should == 'two' @compiler.catalog.resource(:class, "bar").should_not be_nil#['message'].should == 'two' end it 'should fail to create non-existing classes' do lambda { @scope.function_create_resources(['class', {'blah'=>{'one'=>'two'}}]) }.should raise_error(ArgumentError ,'could not find hostclass blah') end it 'should be able to add edges' do @scope.function_create_resources(['class', {'bar'=>{'one'=>'two', 'require' => 'Notify[tester]'}}]) @scope.compiler.compile rg = @scope.compiler.catalog.to_ral.relationship_graph test = rg.vertices.find { |v| v.title == 'test' } tester = rg.vertices.find { |v| v.title == 'tester' } test.should be tester.should be rg.path_between(tester,test).should be end end end diff --git a/spec/unit/parser/functions/defined_spec.rb b/spec/unit/parser/functions/defined_spec.rb index 0113c3233..0651864fb 100755 --- a/spec/unit/parser/functions/defined_spec.rb +++ b/spec/unit/parser/functions/defined_spec.rb @@ -1,53 +1,52 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the 'defined' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do Puppet::Node::Environment.stubs(:current).returns(nil) @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should exist" do Puppet::Parser::Functions.function("defined").should == "function_defined" end it "should be true when the name is defined as a class" do @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "yayness") @scope.function_defined("yayness").should be_true end it "should be true when the name is defined as a definition" do @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") @scope.function_defined("yayness").should be_true end it "should be true when the name is defined as a builtin type" do @scope.function_defined("file").should be_true end it "should be true when any of the provided names are defined" do @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") @scope.function_defined(["meh", "yayness", "booness"]).should be_true end it "should be false when a single given name is not defined" do @scope.function_defined("meh").should be_false end it "should be false when none of the names are defined" do @scope.function_defined(["meh", "yayness", "booness"]).should be_false end it "should be true when a resource reference is provided and the resource is in the catalog" do resource = Puppet::Resource.new("file", "/my/file") @compiler.add_resource(@scope, resource) @scope.function_defined(resource).should be_true end end diff --git a/spec/unit/parser/functions/extlookup_spec.rb b/spec/unit/parser/functions/extlookup_spec.rb index 46cd3cc27..f68daaf3f 100755 --- a/spec/unit/parser/functions/extlookup_spec.rb +++ b/spec/unit/parser/functions/extlookup_spec.rb @@ -1,96 +1,95 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'tempfile' describe "the extlookup function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new @scope.stubs(:environment).returns(Puppet::Node::Environment.new('production')) end it "should exist" do Puppet::Parser::Functions.function("extlookup").should == "function_extlookup" end it "should raise a ParseError if there is less than 1 arguments" do lambda { @scope.function_extlookup([]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 3 arguments" do lambda { @scope.function_extlookup(["foo", "bar", "baz", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should return the default" do result = @scope.function_extlookup([ "key", "default"]) result.should == "default" end it "should lookup the key in a supplied datafile" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value' t.puts 'nonkey,nonvalue' t.close result = @scope.function_extlookup([ "key", "default", t.path]) result.should == "value" end end it "should return an array if the datafile contains more than two columns" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value1,value2' t.puts 'nonkey,nonvalue,nonvalue' t.close result = @scope.function_extlookup([ "key", "default", t.path]) result.should == ["value1", "value2"] end end it "should raise an error if there's no matching key and no default" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value' t.puts 'nonkey,nonvalue' t.close result = @scope.function_extlookup([ "key", nil, t.path]) result.should == "value" end end describe "should look in $extlookup_datadir for data files listed by $extlookup_precedence" do before do - @scope.stubs(:lookupvar).with('extlookup_datadir').returns("/tmp") + @scope.stubs(:lookupvar).with('::extlookup_datadir').returns("/tmp") File.open("/tmp/one.csv","w"){|one| one.puts "key,value1" } File.open("/tmp/two.csv","w") do |two| two.puts "key,value2" two.puts "key2,value_two" end end it "when the key is in the first file" do - @scope.stubs(:lookupvar).with('extlookup_precedence').returns(["one","two"]) + @scope.stubs(:lookupvar).with('::extlookup_precedence').returns(["one","two"]) result = @scope.function_extlookup([ "key" ]) result.should == "value1" end it "when the key is in the second file" do - @scope.stubs(:lookupvar).with('extlookup_precedence').returns(["one","two"]) + @scope.stubs(:lookupvar).with('::extlookup_precedence').returns(["one","two"]) result = @scope.function_extlookup([ "key2" ]) result.should == "value_two" end it "should not modify extlookup_precedence data" do variable = '%{fqdn}' - @scope.stubs(:lookupvar).with('extlookup_precedence').returns([variable,"one"]) - @scope.stubs(:lookupvar).with('fqdn').returns('myfqdn') + @scope.stubs(:lookupvar).with('::extlookup_precedence').returns([variable,"one"]) + @scope.stubs(:lookupvar).with('::fqdn').returns('myfqdn') result = @scope.function_extlookup([ "key" ]) variable.should == '%{fqdn}' end end end diff --git a/spec/unit/parser/functions/fqdn_rand_spec.rb b/spec/unit/parser/functions/fqdn_rand_spec.rb index be2e6fa76..90fc0ef41 100755 --- a/spec/unit/parser/functions/fqdn_rand_spec.rb +++ b/spec/unit/parser/functions/fqdn_rand_spec.rb @@ -1,65 +1,64 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the fqdn_rand function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("fqdn_rand").should == "function_fqdn_rand" end it "should handle 0 arguments" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") lambda { @scope.function_fqdn_rand([]) }.should_not raise_error(Puppet::ParseError) end it "should handle 1 argument'}" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") lambda { @scope.function_fqdn_rand([3]) }.should_not raise_error(Puppet::ParseError) end (1..10).each { |n| it "should handle #{n} additional arguments" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") lambda { @scope.function_fqdn_rand([3,1,2,3,4,5,6,7,8,9,10][0..n]) }.should_not raise_error(Puppet::ParseError) end it "should handle #{n} additional string arguments" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") lambda { @scope.function_fqdn_rand([3,%w{ 1 2 3 4 5 6 7 8 9 10}].flatten[0..n]) }.should_not raise_error(Puppet::ParseError) end } it "should return a value less than max" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") @scope.function_fqdn_rand([3]).should satisfy {|n| n.to_i < 3 } end it "should return the same values on subsequent invocations for the same host" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1").twice + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1").twice @scope.function_fqdn_rand([3,4]).should eql(@scope.function_fqdn_rand([3, 4])) end it "should return different sequences of value for different hosts" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") val1 = @scope.function_fqdn_rand([10000000,4]) - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.2") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.2") val2 = @scope.function_fqdn_rand([10000000,4]) val1.should_not eql(val2) end it "should return different values for the same hosts with different seeds" do - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") val1 = @scope.function_fqdn_rand([10000000,4]) - @scope.expects(:lookupvar).with("fqdn").returns("127.0.0.1") + @scope.expects(:lookupvar).with("::fqdn").returns("127.0.0.1") val2 = @scope.function_fqdn_rand([10000000,42]) val1.should_not eql(val2) end end diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb index d25015b56..6c90ae531 100755 --- a/spec/unit/parser/functions/generate_spec.rb +++ b/spec/unit/parser/functions/generate_spec.rb @@ -1,44 +1,43 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the generate function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("generate").should == "function_generate" end it "should accept a fully-qualified path as a command" do command = File::SEPARATOR + "command" Puppet::Util.expects(:execute).with([command]).returns("yay") lambda { @scope.function_generate([command]) }.should_not raise_error(Puppet::ParseError) end it "should not accept a relative path as a command" do command = "command" lambda { @scope.function_generate([command]) }.should raise_error(Puppet::ParseError) end # Really not sure how to implement this test, just sure it needs # to be implemented. it "should not accept a command containing illegal characters" it "should not accept a command containing '..'" do command = File::SEPARATOR + "command#{File::SEPARATOR}..#{File::SEPARATOR}" lambda { @scope.function_generate([command]) }.should raise_error(Puppet::ParseError) end it "should execute the generate script with the correct working directory" do command = File::SEPARATOR + "command" Dir.expects(:chdir).with(File.dirname(command)).yields Puppet::Util.expects(:execute).with([command]).returns("yay") lambda { @scope.function_generate([command]) }.should_not raise_error(Puppet::ParseError) end end diff --git a/spec/unit/parser/functions/include_spec.rb b/spec/unit/parser/functions/include_spec.rb index cfaadfbb6..15206cd7c 100755 --- a/spec/unit/parser/functions/include_spec.rb +++ b/spec/unit/parser/functions/include_spec.rb @@ -1,36 +1,35 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the 'include' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do Puppet::Node::Environment.stubs(:current).returns(nil) @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should exist" do Puppet::Parser::Functions.function("include").should == "function_include" end it "should include a single class" do inc = "foo" @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| klasses == [inc]}.returns([inc]) @scope.function_include("foo") end it "should include multiple classes" do inc = ["foo","bar"] @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| klasses == inc}.returns(inc) @scope.function_include(["foo","bar"]) end it "should not lazily evaluate the included class" do @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| lazy == false}.returns("foo") @scope.function_include("foo") end end diff --git a/spec/unit/parser/functions/inline_template_spec.rb b/spec/unit/parser/functions/inline_template_spec.rb index 712c68c69..a9ac0c2d0 100755 --- a/spec/unit/parser/functions/inline_template_spec.rb +++ b/spec/unit/parser/functions/inline_template_spec.rb @@ -1,62 +1,61 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the inline_template function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("inline_template").should == "function_inline_template" end it "should create a TemplateWrapper when called" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) @scope.function_inline_template("test") end it "should pass the template string to TemplateWrapper.result" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.expects(:result).with("test") @scope.function_inline_template("test") end it "should return what TemplateWrapper.result returns" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.expects(:result).returns("template contents evaluated") @scope.function_inline_template("test").should == "template contents evaluated" end it "should concatenate template wrapper outputs for multiple templates" do tw1 = stub_everything "template_wrapper1" tw2 = stub_everything "template_wrapper2" Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) tw1.stubs(:result).returns("result1") tw2.stubs(:result).returns("result2") @scope.function_inline_template(["1","2"]).should == "result1result2" end it "should raise an error if the template raises an error" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.stubs(:result).raises lambda { @scope.function_inline_template("1") }.should raise_error(Puppet::ParseError) end end diff --git a/spec/unit/parser/functions/realize_spec.rb b/spec/unit/parser/functions/realize_spec.rb index 3106c42b6..159805cbd 100755 --- a/spec/unit/parser/functions/realize_spec.rb +++ b/spec/unit/parser/functions/realize_spec.rb @@ -1,54 +1,53 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the realize function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @collector = stub_everything 'collector' @scope = Puppet::Parser::Scope.new @compiler = stub 'compiler' @compiler.stubs(:add_collection).with(@collector) @scope.stubs(:compiler).returns(@compiler) end it "should exist" do Puppet::Parser::Functions.function("realize").should == "function_realize" end it "should create a Collector when called" do Puppet::Parser::Collector.expects(:new).returns(@collector) @scope.function_realize("test") end it "should assign the passed-in resources to the collector" do Puppet::Parser::Collector.stubs(:new).returns(@collector) @collector.expects(:resources=).with(["test"]) @scope.function_realize("test") end it "should flatten the resources assigned to the collector" do Puppet::Parser::Collector.stubs(:new).returns(@collector) @collector.expects(:resources=).with(["test"]) @scope.function_realize([["test"]]) end it "should let the compiler know this collector" do Puppet::Parser::Collector.stubs(:new).returns(@collector) @collector.stubs(:resources=).with(["test"]) @compiler.expects(:add_collection).with(@collector) @scope.function_realize("test") end end diff --git a/spec/unit/parser/functions/regsubst_spec.rb b/spec/unit/parser/functions/regsubst_spec.rb index 1fb8e410c..4ed3bcf68 100755 --- a/spec/unit/parser/functions/regsubst_spec.rb +++ b/spec/unit/parser/functions/regsubst_spec.rb @@ -1,195 +1,194 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the regsubst function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("regsubst").should == "function_regsubst" end it "should raise a ParseError if there is less than 3 arguments" do lambda { @scope.function_regsubst(["foo", "bar"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 5 arguments" do lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "del", "x", "y"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError when given a bad flag" do lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "X"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for non-string and non-array target" do lambda { @scope.function_regsubst([4711, "bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for array target with non-string element" do lambda { @scope.function_regsubst([["x", ["y"], "z"], "bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for a bad regular expression" do lambda { @scope.function_regsubst(["foo", "(bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for a non-string regular expression" do lambda { @scope.function_regsubst(["foo", ["bar"], "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should handle groups" do result = @scope.function_regsubst( [ '130.236.254.10', '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', '\4-\3-\2-\1' ]) result.should(eql("10-254-236-130")) end it "should handle simple regexps" do result = @scope.function_regsubst( [ "the monkey breaks banana trees", "b[an]*a", "coconut" ]) result.should(eql("the monkey breaks coconut trees")) end it "should handle case-sensitive regexps" do result = @scope.function_regsubst( [ "the monkey breaks baNAna trees", "b[an]+a", "coconut" ]) result.should(eql("the monkey breaks baNAna trees")) end it "should handle case-insensitive regexps" do result = @scope.function_regsubst( [ "the monkey breaks baNAna trees", "b[an]+a", "coconut", "I" ]) result.should(eql("the monkey breaks coconut trees")) end it "should handle global substitutions" do result = @scope.function_regsubst( [ "the monkey breaks\tbanana trees", "[ \t]", "--", "G" ]) result.should(eql("the--monkey--breaks--banana--trees")) end it "should handle global substitutions with groups" do result = @scope.function_regsubst( [ '130.236.254.10', '([0-9]+)', '<\1>', 'G' ]) result.should(eql('<130>.<236>.<254>.<10>')) end it "should apply on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst([ data, '[.]', '-']) result.should(eql( ['130-236.254.10', 'foo-example.com', 'coconut', '10-20.30.40'])) end it "should apply global substitutions on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst([ data, '[.]', '-', 'G']) result.should(eql( ['130-236-254-10', 'foo-example-com', 'coconut', '10-20-30-40'])) end it "should handle groups on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst( [ data, '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', '\4-\3-\2-\1' ]) result.should(eql( ['10-254-236-130', 'foo.example.com', 'coconut', '40-30-20-10'])) end it "should handle global substitutions with groups on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst( [ data, '([^.]+)', '<\1>', 'G' ]) result.should(eql( ['<130>.<236>.<254>.<10>', '..', '', '<10>.<20>.<30>.<40>'])) end it "should return an array (not a string) for a single element array parameter" do data = ['130.236.254.10'] result = @scope.function_regsubst( [ data, '([^.]+)', '<\1>', 'G' ]) result.should(eql(['<130>.<236>.<254>.<10>'])) end it "should return a string (not a one element array) for a simple string parameter" do data = '130.236.254.10' result = @scope.function_regsubst( [ data, '([^.]+)', '<\1>', 'G' ]) result.should(eql('<130>.<236>.<254>.<10>')) end end diff --git a/spec/unit/parser/functions/require_spec.rb b/spec/unit/parser/functions/require_spec.rb index edcbc4ae6..692b35305 100755 --- a/spec/unit/parser/functions/require_spec.rb +++ b/spec/unit/parser/functions/require_spec.rb @@ -1,77 +1,76 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the require function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @catalog = stub 'catalog' @compiler = stub 'compiler', :catalog => @catalog, :environment => nil @scope = Puppet::Parser::Scope.new @scope.stubs(:findresource) @scope.stubs(:compiler).returns(@compiler) @klass = stub 'class', :name => "myclass" @scope.stubs(:find_hostclass).returns(@klass) @resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope, :source => "source") @resource.stubs(:metaparam_compatibility_mode?).returns false @scope.stubs(:resource).returns @resource end it "should exist" do Puppet::Parser::Functions.function("require").should == "function_require" end it "should delegate to the 'include' puppet function" do @scope.expects(:function_include).with("myclass") @scope.function_require("myclass") end it "should set the 'require' prarameter on the resource to a resource reference" do @scope.stubs(:function_include) @scope.function_require("myclass") @resource["require"].should be_instance_of(Array) @resource["require"][0].should be_instance_of(Puppet::Resource) end it "should verify the 'include' function is loaded" do Puppet::Parser::Functions.expects(:function).with(:include).returns(:function_include) @scope.stubs(:function_include) @scope.function_require("myclass") end it "should include the class but not add a dependency if used on a client not at least version 0.25" do @resource.expects(:metaparam_compatibility_mode?).returns true @scope.expects(:warning) @resource.expects(:set_parameter).never @scope.expects(:function_include) @scope.function_require("myclass") end it "should lookup the absolute class path" do @scope.stubs(:function_include) @scope.expects(:find_hostclass).with("myclass").returns(@klass) @klass.expects(:name).returns("myclass") @scope.function_require("myclass") end it "should append the required class to the require parameter" do @scope.stubs(:function_include) one = Puppet::Resource.new(:file, "/one") @resource[:require] = one @scope.function_require("myclass") @resource[:require].should be_include(one) @resource[:require].detect { |r| r.to_s == "Class[Myclass]" }.should be_instance_of(Puppet::Resource) end end diff --git a/spec/unit/parser/functions/shellquote_spec.rb b/spec/unit/parser/functions/shellquote_spec.rb index 55302b97b..b100b4913 100755 --- a/spec/unit/parser/functions/shellquote_spec.rb +++ b/spec/unit/parser/functions/shellquote_spec.rb @@ -1,84 +1,83 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the shellquote function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("shellquote").should == "function_shellquote" end it "should handle no arguments" do result = @scope.function_shellquote([]) result.should(eql("")) end it "should handle several simple arguments" do result = @scope.function_shellquote( ['foo', 'bar@example.com', 'localhost:/dev/null', 'xyzzy+-4711,23']) result.should(eql( 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23')) end it "should handle array arguments" do result = @scope.function_shellquote( ['foo', ['bar@example.com', 'localhost:/dev/null'], 'xyzzy+-4711,23']) result.should(eql( 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23')) end it "should quote unsafe characters" do result = @scope.function_shellquote( ['/etc/passwd ', '(ls)', '*', '[?]', "'&'"]) result.should(eql( '"/etc/passwd " "(ls)" "*" "[?]" "\'&\'"')) end it "should deal with double quotes" do result = @scope.function_shellquote( ['"foo"bar"']) result.should(eql( '\'"foo"bar"\'')) end it "should cope with dollar signs" do result = @scope.function_shellquote( ['$PATH', 'foo$bar', '"x$"']) result.should(eql( "'$PATH' 'foo$bar' '\"x$\"'")) end it "should deal with apostrophes (single quotes)" do result = @scope.function_shellquote( ["'foo'bar'", "`$'EDITOR'`"]) result.should(eql( '"\'foo\'bar\'" "\\`\\$\'EDITOR\'\\`"')) end it "should cope with grave accents (backquotes)" do result = @scope.function_shellquote( ['`echo *`', '`ls "$MAILPATH"`']) result.should(eql( "'`echo *`' '`ls \"$MAILPATH\"`'")) end it "should deal with both single and double quotes" do result = @scope.function_shellquote( ['\'foo"bar"xyzzy\'', '"foo\'bar\'xyzzy"']) result.should(eql( '"\'foo\\"bar\\"xyzzy\'" "\\"foo\'bar\'xyzzy\\""')) end it "should handle multiple quotes *and* dollars and backquotes" do result = @scope.function_shellquote( ['\'foo"$x`bar`"xyzzy\'']) result.should(eql( '"\'foo\\"\\$x\\`bar\\`\\"xyzzy\'"')) end it "should handle linefeeds" do result = @scope.function_shellquote( ["foo \n bar"]) result.should(eql( "\"foo \n bar\"")) end end diff --git a/spec/unit/parser/functions/split_spec.rb b/spec/unit/parser/functions/split_spec.rb index b892a5c2a..18a21a0cf 100755 --- a/spec/unit/parser/functions/split_spec.rb +++ b/spec/unit/parser/functions/split_spec.rb @@ -1,52 +1,51 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the split function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("split").should == "function_split" end it "should raise a ParseError if there is less than 2 arguments" do lambda { @scope.function_split(["foo"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 2 arguments" do lambda { @scope.function_split(["foo", "bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a RegexpError if the regexp is malformed" do lambda { @scope.function_split(["foo", "("]) }.should( raise_error(RegexpError)) end it "should handle simple string without metacharacters" do result = @scope.function_split([ "130;236;254;10", ";"]) result.should(eql(["130", "236", "254", "10"])) end it "should handle simple regexps" do result = @scope.function_split([ "130.236;254.;10", "[.;]+"]) result.should(eql(["130", "236", "254", "10"])) end it "should handle groups" do result = @scope.function_split([ "130.236;254.;10", "([.;]+)"]) result.should(eql(["130", ".", "236", ";", "254", ".;", "10"])) end it "should handle simple string without metacharacters" do result = @scope.function_split([ "130.236.254.10", ";"]) result.should(eql(["130.236.254.10"])) end end diff --git a/spec/unit/parser/functions/sprintf_spec.rb b/spec/unit/parser/functions/sprintf_spec.rb index 69fbb5e97..bd4863f23 100755 --- a/spec/unit/parser/functions/sprintf_spec.rb +++ b/spec/unit/parser/functions/sprintf_spec.rb @@ -1,44 +1,43 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the sprintf function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("sprintf").should == "function_sprintf" end it "should raise a ParseError if there is less than 1 argument" do lambda { @scope.function_sprintf([]) }.should( raise_error(Puppet::ParseError)) end it "should format integers" do result = @scope.function_sprintf(["%+05d", "23"]) result.should(eql("+0023")) end it "should format floats" do result = @scope.function_sprintf(["%+.2f", "2.7182818284590451"]) result.should(eql("+2.72")) end it "should format large floats" do result = @scope.function_sprintf(["%+.2e", "27182818284590451"]) result.should(eql("+2.72e+16")) end it "should perform more complex formatting" do result = @scope.function_sprintf( [ "<%.8s:%#5o %#8X (%-8s)>", "overlongstring", "23", "48879", "foo" ]) result.should(eql("")) end end diff --git a/spec/unit/parser/functions/tag_spec.rb b/spec/unit/parser/functions/tag_spec.rb index b6bb45252..e8a07e1bb 100755 --- a/spec/unit/parser/functions/tag_spec.rb +++ b/spec/unit/parser/functions/tag_spec.rb @@ -1,28 +1,27 @@ -#! /usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the 'tag' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new @scope.stubs(:environment).returns(nil) end it "should exist" do Puppet::Parser::Functions.function(:tag).should == "function_tag" end it "should tag the resource with any provided tags" do resource = Puppet::Parser::Resource.new(:file, "/file", :scope => @scope) @scope.expects(:resource).returns resource @scope.function_tag ["one", "two"] resource.should be_tagged("one") resource.should be_tagged("two") end end diff --git a/spec/unit/parser/functions/template_spec.rb b/spec/unit/parser/functions/template_spec.rb index 7eaf3554d..e7ee974d3 100755 --- a/spec/unit/parser/functions/template_spec.rb +++ b/spec/unit/parser/functions/template_spec.rb @@ -1,65 +1,64 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the template function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("template").should == "function_template" end it "should create a TemplateWrapper when called" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) @scope.function_template("test") end it "should give the template filename to the TemplateWrapper" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.expects(:file=).with("test") @scope.function_template("test") end it "should return what TemplateWrapper.result returns" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.stubs(:file=).with("test") tw.expects(:result).returns("template contents evaluated") @scope.function_template("test").should == "template contents evaluated" end it "should concatenate template wrapper outputs for multiple templates" do tw1 = stub_everything "template_wrapper1" tw2 = stub_everything "template_wrapper2" Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) tw1.stubs(:file=).with("1") tw2.stubs(:file=).with("2") tw1.stubs(:result).returns("result1") tw2.stubs(:result).returns("result2") @scope.function_template(["1","2"]).should == "result1result2" end it "should raise an error if the template raises an error" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.stubs(:result).raises lambda { @scope.function_template("1") }.should raise_error(Puppet::ParseError) end end diff --git a/spec/unit/parser/functions/versioncmp_spec.rb b/spec/unit/parser/functions/versioncmp_spec.rb index ddc79cd85..6fc724c38 100755 --- a/spec/unit/parser/functions/versioncmp_spec.rb +++ b/spec/unit/parser/functions/versioncmp_spec.rb @@ -1,32 +1,31 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "the versioncmp function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("versioncmp").should == "function_versioncmp" end it "should raise a ParseError if there is less than 2 arguments" do lambda { @scope.function_versioncmp(["1.2"]) }.should raise_error(Puppet::ParseError) end it "should raise a ParseError if there is more than 2 arguments" do lambda { @scope.function_versioncmp(["1.2", "2.4.5", "3.5.6"]) }.should raise_error(Puppet::ParseError) end it "should call Puppet::Util::Package.versioncmp (included in scope)" do Puppet::Util::Package.expects(:versioncmp).with("1.2", "1.3").returns(-1) @scope.function_versioncmp(["1.2", "1.3"]) end end diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb old mode 100644 new mode 100755 index 51e52faee..8240a184c --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -1,102 +1,101 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::Functions do after(:each) do # Rationale: # our various tests will almost all register to Pupet::Parser::Functions # a new function called "name". All tests are required to stub Puppet::Parser::Scope # so that +no+ new real ruby method are defined. # After each test, we want to leave the whole Puppet::Parser::Functions environment # as it was before we were called, hence we call rmfunction (which might not succeed # if the function hasn't been registered in the test). It is also important in this # section to stub +remove_method+ here so that we don't pollute the scope. Puppet::Parser::Scope.stubs(:remove_method) begin Puppet::Parser::Functions.rmfunction("name") rescue end end it "should have a method for returning an environment-specific module" do Puppet::Parser::Functions.environment_module("myenv").should be_instance_of(Module) end it "should use the current default environment if no environment is provided" do Puppet::Parser::Functions.environment_module.should be_instance_of(Module) end describe "when calling newfunction" do before do @module = Module.new Puppet::Parser::Functions.stubs(:environment_module).returns @module end it "should create the function in the environment module" do @module.expects(:define_method).with { |name,block| name == "function_name" } Puppet::Parser::Functions.newfunction("name", :type => :rvalue) end it "should raise an error if the function already exists" do @module.expects(:define_method).with { |name,block| name == "function_name" }.once Puppet::Parser::Functions.newfunction("name", :type => :rvalue) lambda { Puppet::Parser::Functions.newfunction("name", :type => :rvalue) }.should raise_error end it "should raise an error if the function type is not correct" do @module.expects(:define_method).with { |name,block| name == "function_name" }.never lambda { Puppet::Parser::Functions.newfunction("name", :type => :unknown) }.should raise_error end end describe "when calling rmfunction" do before do @module = Module.new Puppet::Parser::Functions.stubs(:environment_module).returns @module end it "should remove the function in the scope class" do @module.expects(:define_method).with { |name,block| name == "function_name" } Puppet::Parser::Functions.newfunction("name", :type => :rvalue) @module.expects(:remove_method).with("function_name").once Puppet::Parser::Functions.rmfunction("name") end it "should raise an error if the function doesn't exists" do lambda { Puppet::Parser::Functions.rmfunction("name") }.should raise_error end end describe "when calling function to test function existance" do before do @module = Module.new Puppet::Parser::Functions.stubs(:environment_module).returns @module end it "should return false if the function doesn't exist" do Puppet::Parser::Functions.autoloader.stubs(:load) Puppet::Parser::Functions.function("name").should be_false end it "should return its name if the function exists" do @module.expects(:define_method).with { |name,block| name == "function_name" } Puppet::Parser::Functions.newfunction("name", :type => :rvalue) Puppet::Parser::Functions.function("name").should == "function_name" end it "should try to autoload the function if it doesn't exist yet" do Puppet::Parser::Functions.autoloader.expects(:load) Puppet::Parser::Functions.function("name") end end end diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb index bc9e22e48..6cdb0553a 100755 --- a/spec/unit/parser/lexer_spec.rb +++ b/spec/unit/parser/lexer_spec.rb @@ -1,690 +1,689 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/lexer' # This is a special matcher to match easily lexer output RSpec::Matchers.define :be_like do |*expected| match do |actual| expected.zip(actual).all? { |e,a| !e or a[0] == e or (e.is_a? Array and a[0] == e[0] and (a[1] == e[1] or (a[1].is_a?(Hash) and a[1][:value] == e[1]))) } end end __ = nil describe Puppet::Parser::Lexer do describe "when reading strings" do before { @lexer = Puppet::Parser::Lexer.new } it "should increment the line count for every carriage return in the string" do @lexer.line = 10 @lexer.string = "this\nis\natest'" @lexer.slurpstring("'") @lexer.line.should == 12 end it "should not increment the line count for escapes in the string" do @lexer.line = 10 @lexer.string = "this\\nis\\natest'" @lexer.slurpstring("'") @lexer.line.should == 10 end it "should not think the terminator is escaped, when preceeded by an even number of backslashes" do @lexer.line = 10 @lexer.string = "here\nis\nthe\nstring\\\\'with\nextra\njunk" @lexer.slurpstring("'") @lexer.line.should == 13 end end end describe Puppet::Parser::Lexer::Token do before do @token = Puppet::Parser::Lexer::Token.new(%r{something}, :NAME) end [:regex, :name, :string, :skip, :incr_line, :skip_text, :accumulate].each do |param| it "should have a #{param.to_s} reader" do @token.should be_respond_to(param) end it "should have a #{param.to_s} writer" do @token.should be_respond_to(param.to_s + "=") end end end describe Puppet::Parser::Lexer::Token, "when initializing" do it "should create a regex if the first argument is a string" do Puppet::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something} end it "should set the string if the first argument is one" do Puppet::Parser::Lexer::Token.new("something", :NAME).string.should == "something" end it "should set the regex if the first argument is one" do Puppet::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something} end end describe Puppet::Parser::Lexer::TokenList do before do @list = Puppet::Parser::Lexer::TokenList.new end it "should have a method for retrieving tokens by the name" do token = @list.add_token :name, "whatever" @list[:name].should equal(token) end it "should have a method for retrieving string tokens by the string" do token = @list.add_token :name, "whatever" @list.lookup("whatever").should equal(token) end it "should add tokens to the list when directed" do token = @list.add_token :name, "whatever" @list[:name].should equal(token) end it "should have a method for adding multiple tokens at once" do @list.add_tokens "whatever" => :name, "foo" => :bar @list[:name].should_not be_nil @list[:bar].should_not be_nil end it "should fail to add tokens sharing a name with an existing token" do @list.add_token :name, "whatever" lambda { @list.add_token :name, "whatever" }.should raise_error(ArgumentError) end it "should set provided options on tokens being added" do token = @list.add_token :name, "whatever", :skip_text => true token.skip_text.should == true end it "should define any provided blocks as a :convert method" do token = @list.add_token(:name, "whatever") do "foo" end token.convert.should == "foo" end it "should store all string tokens in the :string_tokens list" do one = @list.add_token(:name, "1") @list.string_tokens.should be_include(one) end it "should store all regex tokens in the :regex_tokens list" do one = @list.add_token(:name, %r{one}) @list.regex_tokens.should be_include(one) end it "should not store string tokens in the :regex_tokens list" do one = @list.add_token(:name, "1") @list.regex_tokens.should_not be_include(one) end it "should not store regex tokens in the :string_tokens list" do one = @list.add_token(:name, %r{one}) @list.string_tokens.should_not be_include(one) end it "should sort the string tokens inversely by length when asked" do one = @list.add_token(:name, "1") two = @list.add_token(:other, "12") @list.sort_tokens @list.string_tokens.should == [two, one] end end describe Puppet::Parser::Lexer::TOKENS do before do @lexer = Puppet::Parser::Lexer.new end { :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 => '+>', :APPENDS => '+=', :PLUS => '+', :MINUS => '-', :DIV => '/', :TIMES => '*', :LSHIFT => '<<', :RSHIFT => '>>', :MATCH => '=~', :NOMATCH => '!~', :IN_EDGE => '->', :OUT_EDGE => '<-', :IN_EDGE_SUB => '~>', :OUT_EDGE_SUB => '<~', }.each do |name, string| it "should have a token named #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].should_not be_nil end it "should match '#{string}' for the token #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].string.should == string end end { "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, "in" => :IN, }.each do |string, name| it "should have a keyword named #{name.to_s}" do Puppet::Parser::Lexer::KEYWORDS[name].should_not be_nil end it "should have the keyword for #{name.to_s} set to #{string}" do Puppet::Parser::Lexer::KEYWORDS[name].string.should == string end end # These tokens' strings don't matter, just that the tokens exist. [:STRING, :DQPRE, :DQMID, :DQPOST, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :MLCOMMENT, :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name| it "should have a token named #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].should_not be_nil end end end describe Puppet::Parser::Lexer::TOKENS[:CLASSNAME] do before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSNAME] } it "should match against lower-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "one::two" end it "should match against many lower-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "one::two::three::four::five" end it "should match against lower-case alpha-numeric terms prefixed by double colons" do @token.regex.should =~ "::one" end end describe Puppet::Parser::Lexer::TOKENS[:CLASSREF] do before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSREF] } it "should match against single upper-case alpha-numeric terms" do @token.regex.should =~ "One" end it "should match against upper-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "One::Two" end it "should match against many upper-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "One::Two::Three::Four::Five" end it "should match against upper-case alpha-numeric terms prefixed by double colons" do @token.regex.should =~ "::One" end end describe Puppet::Parser::Lexer::TOKENS[:NAME] do before { @token = Puppet::Parser::Lexer::TOKENS[:NAME] } it "should match against lower-case alpha-numeric terms" do @token.regex.should =~ "one-two" end it "should return itself and the value if the matched term is not a keyword" do Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil) @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] end it "should return the keyword token and the value if the matched term is a keyword" do keyword = stub 'keyword', :name => :testing Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) @token.convert(stub("lexer"), "myval").should == [keyword, "myval"] end it "should return the BOOLEAN token and 'true' if the matched term is the string 'true'" do keyword = stub 'keyword', :name => :TRUE Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) @token.convert(stub('lexer'), "true").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], true] end it "should return the BOOLEAN token and 'false' if the matched term is the string 'false'" do keyword = stub 'keyword', :name => :FALSE Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) @token.convert(stub('lexer'), "false").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], false] end end describe Puppet::Parser::Lexer::TOKENS[:NUMBER] do before do @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] @regex = @token.regex end it "should match against numeric terms" do @regex.should =~ "2982383139" end it "should match against float terms" do @regex.should =~ "29823.235" end it "should match against hexadecimal terms" do @regex.should =~ "0xBEEF0023" end it "should match against float with exponent terms" do @regex.should =~ "10e23" end it "should match against float terms with negative exponents" do @regex.should =~ "10e-23" end it "should match against float terms with fractional parts and exponent" do @regex.should =~ "1.234e23" end it "should return the NAME token and the value" do @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] end end describe Puppet::Parser::Lexer::TOKENS[:COMMENT] do before { @token = Puppet::Parser::Lexer::TOKENS[:COMMENT] } it "should match against lines starting with '#'" do @token.regex.should =~ "# this is a comment" end it "should be marked to get skipped" do @token.skip?.should be_true end it "should be marked to accumulate" do @token.accumulate?.should be_true end it "'s block should return the comment without the #" do @token.convert(@lexer,"# this is a comment")[1].should == "this is a comment" end end describe Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] do before do @token = Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] @lexer = stub 'lexer', :line => 0 end it "should match against lines enclosed with '/*' and '*/'" do @token.regex.should =~ "/* this is a comment */" end it "should match multiple lines enclosed with '/*' and '*/'" do @token.regex.should =~ """/* this is a comment */""" end it "should increase the lexer current line number by the amount of lines spanned by the comment" do @lexer.expects(:line=).with(2) @token.convert(@lexer, "1\n2\n3") end it "should not greedily match comments" do match = @token.regex.match("/* first */ word /* second */") match[1].should == " first " end it "should be marked to accumulate" do @token.accumulate?.should be_true end it "'s block should return the comment without the comment marks" do @lexer.stubs(:line=).with(0) @token.convert(@lexer,"/* this is a comment */")[1].should == "this is a comment" end end describe Puppet::Parser::Lexer::TOKENS[:RETURN] do before { @token = Puppet::Parser::Lexer::TOKENS[:RETURN] } it "should match against carriage returns" do @token.regex.should =~ "\n" end it "should be marked to initiate text skipping" do @token.skip_text.should be_true end it "should be marked to increment the line" do @token.incr_line.should be_true end end def tokens_scanned_from(s) lexer = Puppet::Parser::Lexer.new lexer.string = s lexer.fullscan[0..-2] end describe Puppet::Parser::Lexer,"when lexing strings" do { %q{'single quoted string')} => [[:STRING,'single quoted string']], %q{"double quoted string"} => [[:STRING,'double quoted string']], %q{'single quoted string with an escaped "\\'"'} => [[:STRING,'single quoted string with an escaped "\'"']], %q{'single quoted string with an escaped "\$"'} => [[:STRING,'single quoted string with an escaped "\$"']], %q{'single quoted string with an escaped "\."'} => [[:STRING,'single quoted string with an escaped "\."']], %q{'single quoted string with an escaped "\n"'} => [[:STRING,'single quoted string with an escaped "\n"']], %q{'single quoted string with an escaped "\\\\"'} => [[:STRING,'single quoted string with an escaped "\\\\"']], %q{"string with an escaped '\\"'"} => [[:STRING,"string with an escaped '\"'"]], %q{"string with an escaped '\\$'"} => [[:STRING,"string with an escaped '$'"]], %Q{"string with a line ending with a backslash: \\\nfoo"} => [[:STRING,"string with a line ending with a backslash: foo"]], %q{"string with $v (but no braces)"} => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' (but no braces)']], %q["string with ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' in braces']], %q["string with ${qualified::var} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'qualified::var'],[:DQPOST,' in braces']], %q{"string with $v and $v (but no braces)"} => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," (but no braces)"]], %q["string with ${v} and ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," in braces"]], %q["string with ${'a nested single quoted string'} inside it."] => [[:DQPRE,"string with "],[:STRING,'a nested single quoted string'],[:DQPOST,' inside it.']], %q["string with ${['an array ',$v2]} in it."] => [[:DQPRE,"string with "],:LBRACK,[:STRING,"an array "],:COMMA,[:VARIABLE,"v2"],:RBRACK,[:DQPOST," in it."]], %q{a simple "scanner" test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"scanner"],[:NAME,"test"]], %q{a simple 'single quote scanner' test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"single quote scanner"],[:NAME,"test"]], %q{a harder 'a $b \c"'} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,'a $b \c"']], %q{a harder "scanner test"} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,"scanner test"]], %q{a hardest "scanner \"test\""} => [[:NAME,"a"],[:NAME,"hardest"],[:STRING,'scanner "test"']], %Q{a hardestest "scanner \\"test\\"\n"} => [[:NAME,"a"],[:NAME,"hardestest"],[:STRING,%Q{scanner "test"\n}]], %q{function("call")} => [[:NAME,"function"],[:LPAREN,"("],[:STRING,'call'],[:RPAREN,")"]], %q["string with ${(3+5)/4} nested math."] => [[:DQPRE,"string with "],:LPAREN,[:NAME,"3"],:PLUS,[:NAME,"5"],:RPAREN,:DIV,[:NAME,"4"],[:DQPOST," nested math."]], %q["$$$$"] => [[:STRING,"$$$$"]], %q["$variable"] => [[:DQPRE,""],[:VARIABLE,"variable"],[:DQPOST,""]], %q["$var$other"] => [[:DQPRE,""],[:VARIABLE,"var"],[:DQMID,""],[:VARIABLE,"other"],[:DQPOST,""]], %q["foo$bar$"] => [[:DQPRE,"foo"],[:VARIABLE,"bar"],[:DQPOST,"$"]], %q["foo$$bar"] => [[:DQPRE,"foo$"],[:VARIABLE,"bar"],[:DQPOST,""]], %q[""] => [[:STRING,""]], }.each { |src,expected_result| it "should handle #{src} correctly" do tokens_scanned_from(src).should be_like(*expected_result) end } end describe Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] do before { @token = Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] } it "should match against alpha words prefixed with '$'" do @token.regex.should =~ '$this_var' end it "should return the VARIABLE token and the variable name stripped of the '$'" do @token.convert(stub("lexer"), "$myval").should == [Puppet::Parser::Lexer::TOKENS[:VARIABLE], "myval"] end end describe Puppet::Parser::Lexer::TOKENS[:REGEX] do before { @token = Puppet::Parser::Lexer::TOKENS[:REGEX] } it "should match against any expression enclosed in //" do @token.regex.should =~ '/this is a regex/' end it 'should not match if there is \n in the regex' do @token.regex.should_not =~ "/this is \n a regex/" end describe "when scanning" do it "should not consider escaped slashes to be the end of a regex" do tokens_scanned_from("$x =~ /this \\/ foo/").should be_like(__,__,[:REGEX,%r{this / foo}]) end it "should not lex chained division as a regex" do tokens_scanned_from("$x = $a/$b/$c").collect { |name, data| name }.should_not be_include( :REGEX ) end it "should accept a regular expression after NODE" do tokens_scanned_from("node /www.*\.mysite\.org/").should be_like(__,[:REGEX,Regexp.new("www.*\.mysite\.org")]) end it "should accept regular expressions in a CASE" do s = %q{case $variable { "something": {$othervar = 4096 / 2} /regex/: {notice("this notably sucks")} } } tokens_scanned_from(s).should be_like( :CASE,:VARIABLE,:LBRACE,:STRING,:COLON,:LBRACE,:VARIABLE,:EQUALS,:NAME,:DIV,:NAME,:RBRACE,[:REGEX,/regex/],:COLON,:LBRACE,:NAME,:LPAREN,:STRING,:RPAREN,:RBRACE,:RBRACE ) end end it "should return the REGEX token and a Regexp" do @token.convert(stub("lexer"), "/myregex/").should == [Puppet::Parser::Lexer::TOKENS[:REGEX], Regexp.new(/myregex/)] end end describe Puppet::Parser::Lexer, "when lexing comments" do before { @lexer = Puppet::Parser::Lexer.new } it "should accumulate token in munge_token" do token = stub 'token', :skip => true, :accumulate? => true, :incr_line => nil, :skip_text => false token.stubs(:convert).with(@lexer, "# this is a comment").returns([token, " this is a comment"]) @lexer.munge_token(token, "# this is a comment") @lexer.munge_token(token, "# this is a comment") @lexer.getcomment.should == " this is a comment\n this is a comment\n" end it "should add a new comment stack level on LBRACE" do @lexer.string = "{" @lexer.expects(:commentpush) @lexer.fullscan end it "should add a new comment stack level on LPAREN" do @lexer.string = "(" @lexer.expects(:commentpush) @lexer.fullscan end it "should pop the current comment on RPAREN" do @lexer.string = ")" @lexer.expects(:commentpop) @lexer.fullscan end it "should return the current comments on getcomment" do @lexer.string = "# comment" @lexer.fullscan @lexer.getcomment.should == "comment\n" end it "should discard the previous comments on blank line" do @lexer.string = "# 1\n\n# 2" @lexer.fullscan @lexer.getcomment.should == "2\n" end it "should skip whitespace before lexing the next token after a non-token" do tokens_scanned_from("/* 1\n\n */ \ntest").should be_like([:NAME, "test"]) end it "should not return comments seen after the current line" do @lexer.string = "# 1\n\n# 2" @lexer.fullscan @lexer.getcomment(1).should == "" end it "should return a comment seen before the current line" do @lexer.string = "# 1\n# 2" @lexer.fullscan @lexer.getcomment(2).should == "1\n2\n" end end # FIXME: We need to rewrite all of these tests, but I just don't want to take the time right now. describe "Puppet::Parser::Lexer in the old tests" do before { @lexer = Puppet::Parser::Lexer.new } it "should do simple lexing" do { %q{\\} => [[:BACKSLASH,"\\"]], %q{simplest scanner test} => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"]], %Q{returned scanner test\n} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"]] }.each { |source,expected| tokens_scanned_from(source).should be_like(*expected) } end it "should fail usefully" do lambda { tokens_scanned_from('^') }.should raise_error(RuntimeError) end it "should fail if the string is not set" do lambda { @lexer.fullscan }.should raise_error(Puppet::LexError) end it "should correctly identify keywords" do tokens_scanned_from("case").should be_like([:CASE, "case"]) end it "should correctly parse class references" do %w{Many Different Words A Word}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF,t])} end # #774 it "should correctly parse namespaced class refernces token" do %w{Foo ::Foo Foo::Bar ::Foo::Bar}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF, t]) } end it "should correctly parse names" do %w{this is a bunch of names}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } end it "should correctly parse names with numerals" do %w{1name name1 11names names11}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } end it "should correctly parse empty strings" do lambda { tokens_scanned_from('$var = ""') }.should_not raise_error end it "should correctly parse virtual resources" do tokens_scanned_from("@type {").should be_like([:AT, "@"], [:NAME, "type"], [:LBRACE, "{"]) end it "should correctly deal with namespaces" do @lexer.string = %{class myclass} @lexer.fullscan @lexer.namespace.should == "myclass" @lexer.namepop @lexer.namespace.should == "" @lexer.string = "class base { class sub { class more" @lexer.fullscan @lexer.namespace.should == "base::sub::more" @lexer.namepop @lexer.namespace.should == "base::sub" end it "should not put class instantiation on the namespace" do @lexer.string = "class base { class sub { class { mode" @lexer.fullscan @lexer.namespace.should == "base::sub" end it "should correctly handle fully qualified names" do @lexer.string = "class base { class sub::more {" @lexer.fullscan @lexer.namespace.should == "base::sub::more" @lexer.namepop @lexer.namespace.should == "base" end it "should correctly lex variables" do ["$variable", "$::variable", "$qualified::variable", "$further::qualified::variable"].each do |string| tokens_scanned_from(string).should be_like([:VARIABLE,string.sub(/^\$/,'')]) end end end describe "Puppet::Parser::Lexer in the old tests when lexing example files" do my_fixtures('*.pp') do |file| it "should correctly lex #{file}" do lexer = Puppet::Parser::Lexer.new lexer.file = file lambda { lexer.fullscan }.should_not raise_error end end end describe "when trying to lex an non-existent file" do include PuppetSpec::Files it "should return an empty list of tokens" do lexer = Puppet::Parser::Lexer.new lexer.file = nofile = tmpfile('lexer') File.exists?(nofile).should == false lexer.fullscan.should == [[false,false]] end end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index 01cdcb976..78adc30ee 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -1,425 +1,424 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser do Puppet::Parser::AST before :each do @known_resource_types = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @known_resource_types @true_ast = Puppet::Parser::AST::Boolean.new :value => true end it "should require an environment at initialization" do lambda { Puppet::Parser::Parser.new }.should raise_error(ArgumentError) end it "should set the environment" do env = Puppet::Node::Environment.new Puppet::Parser::Parser.new(env).environment.should == env end it "should convert the environment into an environment instance if a string is provided" do env = Puppet::Node::Environment.new("testing") Puppet::Parser::Parser.new("testing").environment.should == env end it "should be able to look up the environment-specific resource type collection" do rtc = Puppet::Node::Environment.new("development").known_resource_types parser = Puppet::Parser::Parser.new "development" parser.known_resource_types.should equal(rtc) end it "should delegate importing to the known resource type loader" do parser = Puppet::Parser::Parser.new "development" parser.known_resource_types.loader.expects(:import).with("newfile", "current_file") parser.lexer.expects(:file).returns "current_file" parser.import("newfile") end describe "when parsing files" do before do FileTest.stubs(:exist?).returns true File.stubs(:read).returns "" @parser.stubs(:watch_file) end it "should treat files ending in 'rb' as ruby files" do @parser.expects(:parse_ruby_file) @parser.file = "/my/file.rb" @parser.parse end end describe "when parsing append operator" do it "should not raise syntax errors" do lambda { @parser.parse("$var += something") }.should_not raise_error end it "shouldraise syntax error on incomplete syntax " do lambda { @parser.parse("$var += ") }.should raise_error end it "should create ast::VarDef with append=true" do vardef = @parser.parse("$var += 2").code[0] vardef.should be_a(Puppet::Parser::AST::VarDef) vardef.append.should == true end it "should work with arrays too" do vardef = @parser.parse("$var += ['test']").code[0] vardef.should be_a(Puppet::Parser::AST::VarDef) vardef.append.should == true end end describe "when parsing selector" do it "should support hash access on the left hand side" do lambda { @parser.parse("$h = { 'a' => 'b' } $a = $h['a'] ? { 'b' => 'd', default => undef }") }.should_not raise_error end end describe "when parsing 'if'" do it "not, it should create the correct ast objects" do Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } @parser.parse("if ! true { $var = 1 }") end it "boolean operation, it should create the correct ast objects" do Puppet::Parser::AST::BooleanOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Boolean) and h[:lval].is_a?(Puppet::Parser::AST::Boolean) and h[:operator]=="or" } @parser.parse("if true or true { $var = 1 }") end it "comparison operation, it should create the correct ast objects" do Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="<" } @parser.parse("if 1 < 2 { $var = 1 }") end end describe "when parsing if complex expressions" do it "should create a correct ast tree" do aststub = stub_everything 'ast' Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]==">" }.returns(aststub) Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="==" }.returns(aststub) Puppet::Parser::AST::BooleanOperator.expects(:new).with { |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and" } @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") end it "should raise an error on incorrect expression" do lambda { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.should raise_error end end describe "when parsing resource references" do it "should not raise syntax errors" do lambda { @parser.parse('exec { test: param => File["a"] }') }.should_not raise_error end it "should not raise syntax errors with multiple references" do lambda { @parser.parse('exec { test: param => File["a","b"] }') }.should_not raise_error end it "should create an ast::ResourceReference" do Puppet::Parser::AST::ResourceReference.expects(:new).with { |arg| arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(Puppet::Parser::AST::ASTArray) } @parser.parse('exec { test: command => File["a","b"] }') end end describe "when parsing resource overrides" do it "should not raise syntax errors" do lambda { @parser.parse('Resource["title"] { param => value }') }.should_not raise_error end it "should not raise syntax errors with multiple overrides" do lambda { @parser.parse('Resource["title1","title2"] { param => value }') }.should_not raise_error end it "should create an ast::ResourceOverride" do #Puppet::Parser::AST::ResourceOverride.expects(:new).with { |arg| # arg[:line]==1 and arg[:object].is_a?(Puppet::Parser::AST::ResourceReference) and arg[:parameters].is_a?(Puppet::Parser::AST::ResourceParam) #} ro = @parser.parse('Resource["title1","title2"] { param => value }').code[0] ro.should be_a(Puppet::Parser::AST::ResourceOverride) ro.line.should == 1 ro.object.should be_a(Puppet::Parser::AST::ResourceReference) ro.parameters[0].should be_a(Puppet::Parser::AST::ResourceParam) end end describe "when parsing if statements" do it "should not raise errors with empty if" do lambda { @parser.parse("if true { }") }.should_not raise_error end it "should not raise errors with empty else" do lambda { @parser.parse("if false { notice('if') } else { }") }.should_not raise_error end it "should not raise errors with empty if and else" do lambda { @parser.parse("if false { } else { }") }.should_not raise_error end it "should create a nop node for empty branch" do Puppet::Parser::AST::Nop.expects(:new) @parser.parse("if true { }") end it "should create a nop node for empty else branch" do Puppet::Parser::AST::Nop.expects(:new) @parser.parse("if true { notice('test') } else { }") end it "should build a chain of 'ifs' if there's an 'elsif'" do lambda { @parser.parse(<<-PP) }.should_not raise_error if true { notice('test') } elsif true {} else { } PP end end describe "when parsing function calls" do it "should not raise errors with no arguments" do lambda { @parser.parse("tag()") }.should_not raise_error end it "should not raise errors with rvalue function with no args" do lambda { @parser.parse("$a = template()") }.should_not raise_error end it "should not raise errors with arguments" do lambda { @parser.parse("notice(1)") }.should_not raise_error end it "should not raise errors with multiple arguments" do lambda { @parser.parse("notice(1,2)") }.should_not raise_error end it "should not raise errors with multiple arguments and a trailing comma" do lambda { @parser.parse("notice(1,2,)") }.should_not raise_error end end describe "when parsing arrays with trailing comma" do it "should not raise errors with a trailing comma" do lambda { @parser.parse("$a = [1,2,]") }.should_not raise_error end end describe "when providing AST context" do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer end it "should include the lexer's line" do @parser.ast_context[:line].should == 50 end it "should include the lexer's file" do @parser.ast_context[:file].should == "/foo/bar" end it "should include the docs if directed to do so" do @parser.ast_context(true)[:doc].should == "whev" end it "should not include the docs when told not to" do @parser.ast_context(false)[:doc].should be_nil end it "should not include the docs by default" do @parser.ast_context[:doc].should be_nil end end describe "when building ast nodes" do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer @class = Puppet::Resource::Type.new(:hostclass, "myclass", :use_docs => false) end it "should return a new instance of the provided class created with the provided options" do @class.expects(:new).with { |opts| opts[:foo] == "bar" } @parser.ast(@class, :foo => "bar") end it "should merge the ast context into the provided options" do @class.expects(:new).with { |opts| opts[:file] == "/foo" } @parser.expects(:ast_context).returns :file => "/foo" @parser.ast(@class, :foo => "bar") end it "should prefer provided options over AST context" do @class.expects(:new).with { |opts| opts[:file] == "/bar" } @lexer.expects(:file).returns "/foo" @parser.ast(@class, :file => "/bar") end it "should include docs when the AST class uses them" do @class.expects(:use_docs).returns true @class.stubs(:new) @parser.expects(:ast_context).with{ |docs, line| docs == true }.returns({}) @parser.ast(@class, :file => "/bar") end it "should get docs from lexer using the correct AST line number" do @class.expects(:use_docs).returns true @class.stubs(:new).with{ |a| a[:doc] == "doc" } @lexer.expects(:getcomment).with(12).returns "doc" @parser.ast(@class, :file => "/bar", :line => 12) end end describe "when retrieving a specific node" do it "should delegate to the known_resource_types node" do @known_resource_types.expects(:node).with("node") @parser.node("node") end end describe "when retrieving a specific class" do it "should delegate to the loaded code" do @known_resource_types.expects(:hostclass).with("class") @parser.hostclass("class") end end describe "when retrieving a specific definitions" do it "should delegate to the loaded code" do @known_resource_types.expects(:definition).with("define") @parser.definition("define") end end describe "when determining the configuration version" do it "should determine it from the resource type collection" do @parser.known_resource_types.expects(:version).returns "foo" @parser.version.should == "foo" end end describe "when looking up definitions" do it "should use the known resource types to check for them by name" do @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:definition).returns(:this_value) @parser.find_definition("namespace","name").should == :this_value end end describe "when looking up hostclasses" do it "should use the known resource types to check for them by name" do @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:hostclass).returns(:this_value) @parser.find_hostclass("namespace","name").should == :this_value end end describe "when parsing classes" do before :each do @krt = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @krt end it "should not create new classes" do @parser.parse("class foobar {}").code[0].should be_a(Puppet::Parser::AST::Hostclass) @krt.hostclass("foobar").should be_nil end it "should correctly set the parent class when one is provided" do @parser.parse("class foobar inherits yayness {}").code[0].instantiate('')[0].parent.should == "yayness" end it "should correctly set the parent class for multiple classes at a time" do statements = @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}").code statements[0].instantiate('')[0].parent.should == "yayness" statements[1].instantiate('')[0].parent.should == "bar" end it "should define the code when some is provided" do @parser.parse("class foobar { $var = val }").code[0].code.should_not be_nil end it "should define parameters when provided" do foobar = @parser.parse("class foobar($biz,$baz) {}").code[0].instantiate('')[0] foobar.arguments.should == {"biz" => nil, "baz" => nil} end end describe "when parsing resources" do before :each do @krt = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @krt end it "should be able to parse class resources" do @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil})) lambda { @parser.parse("class { foobar: biz => stuff }") }.should_not raise_error end it "should correctly mark exported resources as exported" do @parser.parse("@@file { '/file': }").code[0].exported.should be_true end it "should correctly mark virtual resources as virtual" do @parser.parse("@file { '/file': }").code[0].virtual.should be_true end end describe "when parsing nodes" do it "should be able to parse a node with a single name" do node = @parser.parse("node foo { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 1 node.names[0].value.should == "foo" end it "should be able to parse a node with two names" do node = @parser.parse("node foo, bar { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 2 node.names[0].value.should == "foo" node.names[1].value.should == "bar" end it "should be able to parse a node with three names" do node = @parser.parse("node foo, bar, baz { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 3 node.names[0].value.should == "foo" node.names[1].value.should == "bar" node.names[2].value.should == "baz" end end end diff --git a/spec/unit/parser/relationship_spec.rb b/spec/unit/parser/relationship_spec.rb old mode 100644 new mode 100755 index 883a38d11..5a49831e1 --- a/spec/unit/parser/relationship_spec.rb +++ b/spec/unit/parser/relationship_spec.rb @@ -1,70 +1,69 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/relationship' describe Puppet::Parser::Relationship do before do @source = Puppet::Resource.new(:mytype, "source") @target = Puppet::Resource.new(:mytype, "target") @dep = Puppet::Parser::Relationship.new(@source, @target, :relationship) end describe "when evaluating" do before do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@source) @catalog.add_resource(@target) end it "should fail if the source resource cannot be found" do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @target lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @source lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) end it "should add the target as a 'before' value if the type is 'relationship'" do @dep.type = :relationship @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") end it "should add the target as a 'notify' value if the type is 'subscription'" do @dep.type = :subscription @dep.evaluate(@catalog) @source[:notify].should be_include("Mytype[target]") end it "should supplement rather than clobber existing relationship values" do @source[:before] = "File[/bar]" @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") @source[:before].should be_include("File[/bar]") end it "should use the collected retargets if the target is a Collector" do orig_target = @target @target = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) @target.collected[:foo] = @target @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") end it "should use the collected resources if the source is a Collector" do orig_source = @source @source = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) @source.collected[:foo] = @source @dep.evaluate(@catalog) orig_source[:before].should be_include("Mytype[target]") end end end diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb index 73c4412dd..b03c18e5f 100755 --- a/spec/unit/parser/resource_spec.rb +++ b/spec/unit/parser/resource_spec.rb @@ -1,558 +1,557 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' # LAK: FIXME This is just new tests for resources; I have # not moved all tests over yet. describe Puppet::Parser::Resource do before do @node = Puppet::Node.new("yaynode") @known_resource_types = Puppet::Resource::TypeCollection.new("env") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @source = newclass "" @scope = @compiler.topscope end def mkresource(args = {}) args[:source] ||= @source args[:scope] ||= @scope params = args[:parameters] || {:one => "yay", :three => "rah"} if args[:parameters] == :none args.delete(:parameters) elsif not args[:parameters].is_a? Array args[:parameters] = paramify(args[:source], params) end Puppet::Parser::Resource.new("resource", "testing", args) end def param(name, value, source) Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source) end def paramify(source, hash) hash.collect do |name, value| Puppet::Parser::Resource::Param.new( :name => name, :value => value, :source => source ) end end def newclass(name) @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def newdefine(name) @known_resource_types.add Puppet::Resource::Type.new(:definition, name) end def newnode(name) @known_resource_types.add Puppet::Resource::Type.new(:node, name) end it "should use the file lookup module" do Puppet::Parser::Resource.ancestors.should be_include(Puppet::FileCollection::Lookup) end it "should get its environment from its scope" do scope = stub 'scope', :source => stub("source"), :namespaces => nil scope.expects(:environment).returns("foo").at_least_once Puppet::Parser::Resource.new("file", "whatever", :scope => scope).environment.should == "foo" end it "should use the resource type collection helper module" do Puppet::Parser::Resource.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should use the scope's environment as its environment" do @scope.expects(:environment).returns("myenv").at_least_once Puppet::Parser::Resource.new("file", "whatever", :scope => @scope).environment.should == "myenv" end it "should be isomorphic if it is builtin and models an isomorphic type" do Puppet::Type.type(:file).expects(:isomorphic?).returns(true) @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should not be isomorphic if it is builtin and models a non-isomorphic type" do Puppet::Type.type(:file).expects(:isomorphic?).returns(false) @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_false end it "should be isomorphic if it is not builtin" do newdefine "whatever" @resource = Puppet::Parser::Resource.new("whatever", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should have a array-indexing method for retrieving parameter values" do @resource = mkresource @resource[:one].should == "yay" end it "should use a Puppet::Resource for converting to a ral resource" do trans = mock 'resource', :to_ral => "yay" @resource = mkresource @resource.expects(:to_resource).returns trans @resource.to_ral.should == "yay" end it "should be able to use the indexing operator to access parameters" do resource = Puppet::Parser::Resource.new("resource", "testing", :source => "source", :scope => @scope) resource["foo"] = "bar" resource["foo"].should == "bar" end it "should return the title when asked for a parameter named 'title'" do Puppet::Parser::Resource.new("resource", "testing", :source => @source, :scope => @scope)[:title].should == "testing" end describe "when initializing" do before do @arguments = {:scope => @scope} end it "should fail unless #{name.to_s} is specified" do lambda { Puppet::Parser::Resource.new('file', '/my/file') }.should raise_error(ArgumentError) end it "should set the reference correctly" do res = Puppet::Parser::Resource.new("resource", "testing", @arguments) res.ref.should == "Resource[testing]" end it "should be tagged with user tags" do tags = [ "tag1", "tag2" ] @arguments[:parameters] = [ param(:tag, tags , :source) ] res = Puppet::Parser::Resource.new("resource", "testing", @arguments) (res.tags & tags).should == tags end end describe "when evaluating" do it "should evaluate the associated AST definition" do definition = newdefine "mydefine" res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source) definition.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST class" do @class = newclass "myclass" res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source) @class.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST node" do nodedef = newnode("mynode") res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source) nodedef.expects(:evaluate_code).with(res) res.evaluate end end describe "when finishing" do before do @class = newclass "myclass" @nodedef = newnode("mynode") @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source) end it "should do nothing if it has already been finished" do @resource.finish @resource.expects(:add_metaparams).never @resource.finish end it "should add all defaults available from the scope" do @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => param(:owner, "default", @resource.source)) @resource.finish @resource[:owner].should == "default" end it "should not replace existing parameters with defaults" do @resource.set_parameter :owner, "oldvalue" @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => :replaced) @resource.finish @resource[:owner].should == "oldvalue" end it "should add a copy of each default, rather than the actual default parameter instance" do newparam = param(:owner, "default", @resource.source) other = newparam.dup other.value = "other" newparam.expects(:dup).returns(other) @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => newparam) @resource.finish @resource[:owner].should == "other" end it "should be running in metaparam compatibility mode if running a version below 0.25" do catalog = stub 'catalog', :client_version => "0.24.8" @resource.stubs(:catalog).returns catalog @resource.should be_metaparam_compatibility_mode end it "should be running in metaparam compatibility mode if running no client version is available" do catalog = stub 'catalog', :client_version => nil @resource.stubs(:catalog).returns catalog @resource.should be_metaparam_compatibility_mode end it "should not be running in metaparam compatibility mode if running a version at or above 0.25" do catalog = stub 'catalog', :client_version => "0.25.0" @resource.stubs(:catalog).returns catalog @resource.should_not be_metaparam_compatibility_mode end it "should not copy relationship metaparams when not in metaparam compatibility mode" do @scope.setvar("require", "bar") @resource.stubs(:metaparam_compatibility_mode?).returns false @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should be_nil end it "should copy relationship metaparams when in metaparam compatibility mode" do @scope.setvar("require", "bar") @resource.stubs(:metaparam_compatibility_mode?).returns true @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should == "bar" end it "should stack relationship metaparams when in metaparam compatibility mode" do @resource.set_parameter("require", "foo") @scope.setvar("require", "bar") @resource.stubs(:metaparam_compatibility_mode?).returns true @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should == ["foo", "bar"] end end describe "when being tagged" do before do @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} @scope.stubs(:resource).returns @scope_resource @resource = Puppet::Parser::Resource.new("file", "yay", :scope => @scope, :source => mock('source')) end it "should get tagged with the resource type" do @resource.tags.should be_include("file") end it "should get tagged with the title" do @resource.tags.should be_include("yay") end it "should get tagged with each name in the title if the title is a qualified class name" do resource = Puppet::Parser::Resource.new("file", "one::two", :scope => @scope, :source => mock('source')) resource.tags.should be_include("one") resource.tags.should be_include("two") end it "should get tagged with each name in the type if the type is a qualified class name" do resource = Puppet::Parser::Resource.new("one::two", "whatever", :scope => @scope, :source => mock('source')) resource.tags.should be_include("one") resource.tags.should be_include("two") end it "should not get tagged with non-alphanumeric titles" do resource = Puppet::Parser::Resource.new("file", "this is a test", :scope => @scope, :source => mock('source')) resource.tags.should_not be_include("this is a test") end it "should fail on tags containing '*' characters" do lambda { @resource.tag("bad*tag") }.should raise_error(Puppet::ParseError) end it "should fail on tags starting with '-' characters" do lambda { @resource.tag("-badtag") }.should raise_error(Puppet::ParseError) end it "should fail on tags containing ' ' characters" do lambda { @resource.tag("bad tag") }.should raise_error(Puppet::ParseError) end it "should allow alpha tags" do lambda { @resource.tag("good_tag") }.should_not raise_error(Puppet::ParseError) end end describe "when merging overrides" do before do @source = "source1" @resource = mkresource :source => @source @override = mkresource :source => @source end it "should fail when the override was not created by a parent class" do @override.source = "source2" @override.source.expects(:child_of?).with("source1").returns(false) lambda { @resource.merge(@override) }.should raise_error(Puppet::ParseError) end it "should succeed when the override was created in the current scope" do @resource.source = "source3" @override.source = @resource.source @override.source.expects(:child_of?).with("source3").never params = {:a => :b, :c => :d} @override.expects(:parameters).returns(params) @resource.expects(:override_parameter).with(:b) @resource.expects(:override_parameter).with(:d) @resource.merge(@override) end it "should succeed when a parent class created the override" do @resource.source = "source3" @override.source = "source4" @override.source.expects(:child_of?).with("source3").returns(true) params = {:a => :b, :c => :d} @override.expects(:parameters).returns(params) @resource.expects(:override_parameter).with(:b) @resource.expects(:override_parameter).with(:d) @resource.merge(@override) end it "should add new parameters when the parameter is not set" do @source.stubs(:child_of?).returns true @override.set_parameter(:testing, "value") @resource.merge(@override) @resource[:testing].should == "value" end it "should replace existing parameter values" do @source.stubs(:child_of?).returns true @resource.set_parameter(:testing, "old") @override.set_parameter(:testing, "value") @resource.merge(@override) @resource[:testing].should == "value" end it "should add values to the parameter when the override was created with the '+>' syntax" do @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "testing", :source => @resource.source) param.add = true @override.set_parameter(param) @resource.set_parameter(:testing, "other") @resource.merge(@override) @resource[:testing].should == %w{other testing} end it "should not merge parameter values when multiple resources are overriden with '+>' at once " do @resource_2 = mkresource :source => @source @resource. set_parameter(:testing, "old_val_1") @resource_2.set_parameter(:testing, "old_val_2") @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "new_val", :source => @resource.source) param.add = true @override.set_parameter(param) @resource. merge(@override) @resource_2.merge(@override) @resource [:testing].should == %w{old_val_1 new_val} @resource_2[:testing].should == %w{old_val_2 new_val} end it "should promote tag overrides to real tags" do @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :tag, :value => "testing", :source => @resource.source) @override.set_parameter(param) @resource.merge(@override) @resource.tagged?("testing").should be_true end end it "should be able to be converted to a normal resource" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source @resource.should respond_to(:to_resource) end it "should use its resource converter to convert to a transportable resource" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source newresource = Puppet::Resource.new(:file, "/my") Puppet::Resource.expects(:new).returns(newresource) newresource.expects(:to_trans).returns "mytrans" @resource.to_trans.should == "mytrans" end it "should return nil if converted to a transportable resource and it is virtual" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source @resource.expects(:virtual?).returns true @resource.to_trans.should be_nil end describe "when being converted to a resource" do before do @parser_resource = mkresource :scope => @scope, :parameters => {:foo => "bar", :fee => "fum"} end it "should create an instance of Puppet::Resource" do @parser_resource.to_resource.should be_instance_of(Puppet::Resource) end it "should set the type correctly on the Puppet::Resource" do @parser_resource.to_resource.type.should == @parser_resource.type end it "should set the title correctly on the Puppet::Resource" do @parser_resource.to_resource.title.should == @parser_resource.title end it "should copy over all of the parameters" do result = @parser_resource.to_resource.to_hash # The name will be in here, also. result[:foo].should == "bar" result[:fee].should == "fum" end it "should copy over the tags" do @parser_resource.tag "foo" @parser_resource.tag "bar" @parser_resource.to_resource.tags.should == @parser_resource.tags end it "should copy over the line" do @parser_resource.line = 40 @parser_resource.to_resource.line.should == 40 end it "should copy over the file" do @parser_resource.file = "/my/file" @parser_resource.to_resource.file.should == "/my/file" end it "should copy over the 'exported' value" do @parser_resource.exported = true @parser_resource.to_resource.exported.should be_true end it "should copy over the 'virtual' value" do @parser_resource.virtual = true @parser_resource.to_resource.virtual.should be_true end it "should convert any parser resource references to Puppet::Resource instances" do ref = Puppet::Resource.new("file", "/my/file") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ref} result = @parser_resource.to_resource result[:fee].should == Puppet::Resource.new(:file, "/my/file") end it "should convert any parser resource references to Puppet::Resource instances even if they are in an array" do ref = Puppet::Resource.new("file", "/my/file") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", ref]} result = @parser_resource.to_resource result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file")] end it "should convert any parser resource references to Puppet::Resource instances even if they are in an array of array, and even deeper" do ref1 = Puppet::Resource.new("file", "/my/file1") ref2 = Puppet::Resource.new("file", "/my/file2") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", [ref1,ref2]]} result = @parser_resource.to_resource result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file1"), Puppet::Resource.new(:file, "/my/file2")] end it "should fail if the same param is declared twice" do lambda do @parser_resource = mkresource :source => @source, :parameters => [ Puppet::Parser::Resource::Param.new( :name => :foo, :value => "bar", :source => @source ), Puppet::Parser::Resource::Param.new( :name => :foo, :value => "baz", :source => @source ) ] end.should raise_error(Puppet::ParseError) end end describe "when validating" do it "should check each parameter" do resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") resource[:one] = :two resource[:three] = :four resource.expects(:validate_parameter).with(:one) resource.expects(:validate_parameter).with(:three) resource.send(:validate) end it "should raise a parse error when there's a failure" do resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") resource[:one] = :two resource.expects(:validate_parameter).with(:one).raises ArgumentError lambda { resource.send(:validate) }.should raise_error(Puppet::ParseError) end end describe "when setting parameters" do before do @source = newclass "foobar" @resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => @source end it "should accept Param instances and add them to the parameter list" do param = Puppet::Parser::Resource::Param.new :name => "foo", :value => "bar", :source => @source @resource.set_parameter(param) @resource["foo"].should == "bar" end it "should fail when provided a parameter name but no value" do lambda { @resource.set_parameter("myparam") }.should raise_error(ArgumentError) end it "should allow parameters to be set to 'false'" do @resource.set_parameter("myparam", false) @resource["myparam"].should be_false end it "should use its source when provided a parameter name and value" do @resource.set_parameter("myparam", "myvalue") @resource["myparam"].should == "myvalue" end end # part of #629 -- the undef keyword. Make sure 'undef' params get skipped. it "should not include 'undef' parameters when converting itself to a hash" do resource = Puppet::Parser::Resource.new "file", "/tmp/testing", :source => mock("source"), :scope => mock("scope") resource[:owner] = :undef resource[:mode] = "755" resource.to_hash[:owner].should be_nil end end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 32a87f1f4..bf4d1e29e 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -1,628 +1,484 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Parser::Scope do before :each do @topscope = Puppet::Parser::Scope.new # This is necessary so we don't try to use the compiler to discover our parent. @topscope.parent = nil @scope = Puppet::Parser::Scope.new @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope.parent = @topscope end it "should be able to store references to class scopes" do lambda { @scope.class_set "myname", "myscope" }.should_not raise_error end it "should be able to retrieve class scopes by name" do @scope.class_set "myname", "myscope" @scope.class_scope("myname").should == "myscope" end it "should be able to retrieve class scopes by object" do klass = mock 'ast_class' klass.expects(:name).returns("myname") @scope.class_set "myname", "myscope" @scope.class_scope(klass).should == "myscope" end it "should be able to retrieve its parent module name from the source of its parent type" do @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo, :module_name => "foo") @scope.parent_module_name.should == "foo" end it "should return a nil parent module name if it has no parent" do @topscope.parent_module_name.should be_nil end it "should return a nil parent module name if its parent has no source" do @scope.parent_module_name.should be_nil end it "should get its environment from its compiler" do env = stub 'environment' compiler = stub 'compiler', :environment => env scope = Puppet::Parser::Scope.new :compiler => compiler scope.environment.should equal(env) end it "should use the resource type collection helper to find its known resource types" do Puppet::Parser::Scope.ancestors.should include(Puppet::Resource::TypeCollectionHelper) end describe "when initializing" do it "should extend itself with its environment's Functions module as well as the default" do env = Puppet::Node::Environment.new("myenv") compiler = stub 'compiler', :environment => env mod = Module.new root_mod = Module.new Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns root_mod Puppet::Parser::Functions.expects(:environment_module).with(env).returns mod Puppet::Parser::Scope.new(:compiler => compiler).singleton_class.ancestors.should be_include(mod) end it "should extend itself with the default Functions module if it has no environment" do mod = Module.new Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns(mod) Puppet::Parser::Functions.expects(:environment_module).with(nil).returns mod Puppet::Parser::Scope.new.singleton_class.ancestors.should be_include(mod) end - end - describe "when looking up a variable" do - it "should default to an empty string" do - @scope.lookupvar("var").should == "" + it "should remember if it is dynamic" do + (!!Puppet::Parser::Scope.new(:dynamic => true).dynamic).should == true end - it "should return an string when asked for a string" do - @scope.lookupvar("var", true).should == "" + it "should assume it is not dynamic" do + (!Puppet::Parser::Scope.new.dynamic).should == true end + end - it "should return ':undefined' for unset variables when asked not to return a string" do - @scope.lookupvar("var", false).should == :undefined + describe "when looking up a variable" do + it "should return ':undefined' for unset variables" do + @scope.lookupvar("var").should == :undefined end it "should be able to look up values" do @scope.setvar("var", "yep") @scope.lookupvar("var").should == "yep" end it "should be able to look up hashes" do @scope.setvar("var", {"a" => "b"}) @scope.lookupvar("var").should == {"a" => "b"} end it "should be able to look up variables in parent scopes" do @topscope.setvar("var", "parentval") @scope.lookupvar("var").should == "parentval" end it "should prefer its own values to parent values" do @topscope.setvar("var", "parentval") @scope.setvar("var", "childval") @scope.lookupvar("var").should == "childval" end describe "and the variable is qualified" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode")) @scope.compiler = @compiler @known_resource_types = @scope.known_resource_types end def newclass(name) @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def create_class_scope(name) klass = newclass(name) Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate @scope.class_scope(klass) end it "should be able to look up explicitly fully qualified variables from main" do other_scope = create_class_scope("") other_scope.setvar("othervar", "otherval") @scope.lookupvar("::othervar").should == "otherval" end it "should be able to look up explicitly fully qualified variables from other scopes" do other_scope = create_class_scope("other") other_scope.setvar("var", "otherval") @scope.lookupvar("::other::var").should == "otherval" end it "should be able to look up deeply qualified variables" do other_scope = create_class_scope("other::deep::klass") other_scope.setvar("var", "otherval") @scope.lookupvar("other::deep::klass::var").should == "otherval" end - it "should return an empty string for qualified variables that cannot be found in other classes" do + it "should return ':undefined' for qualified variables that cannot be found in other classes" do other_scope = create_class_scope("other::deep::klass") - @scope.lookupvar("other::deep::klass::var").should == "" + @scope.lookupvar("other::deep::klass::var").should == :undefined end - it "should warn and return an empty string for qualified variables whose classes have not been evaluated" do + it "should warn and return ':undefined' for qualified variables whose classes have not been evaluated" do klass = newclass("other::deep::klass") @scope.expects(:warning) - @scope.lookupvar("other::deep::klass::var").should == "" + @scope.lookupvar("other::deep::klass::var").should == :undefined end - it "should warn and return an empty string for qualified variables whose classes do not exist" do + it "should warn and return ':undefined' for qualified variables whose classes do not exist" do @scope.expects(:warning) - @scope.lookupvar("other::deep::klass::var").should == "" + @scope.lookupvar("other::deep::klass::var").should == :undefined end it "should return ':undefined' when asked for a non-string qualified variable from a class that does not exist" do @scope.stubs(:warning) - @scope.lookupvar("other::deep::klass::var", false).should == :undefined + @scope.lookupvar("other::deep::klass::var").should == :undefined end it "should return ':undefined' when asked for a non-string qualified variable from a class that has not been evaluated" do @scope.stubs(:warning) klass = newclass("other::deep::klass") - @scope.lookupvar("other::deep::klass::var", false).should == :undefined + @scope.lookupvar("other::deep::klass::var").should == :undefined end end end describe "when setvar is called with append=true" do it "should raise error if the variable is already defined in this scope" do @scope.setvar("var","1", :append => false) lambda { @scope.setvar("var","1", :append => true) }.should raise_error(Puppet::ParseError) end it "should lookup current variable value" do @scope.expects(:lookupvar).with("var").returns("2") @scope.setvar("var","1", :append => true) end it "should store the concatenated string '42'" do @topscope.setvar("var","4", :append => false) @scope.setvar("var","2", :append => true) @scope.lookupvar("var").should == "42" end it "should store the concatenated array [4,2]" do @topscope.setvar("var",[4], :append => false) @scope.setvar("var",[2], :append => true) @scope.lookupvar("var").should == [4,2] end it "should store the merged hash {a => b, c => d}" do @topscope.setvar("var",{"a" => "b"}, :append => false) @scope.setvar("var",{"c" => "d"}, :append => true) @scope.lookupvar("var").should == {"a" => "b", "c" => "d"} end it "should raise an error when appending a hash with something other than another hash" do @topscope.setvar("var",{"a" => "b"}, :append => false) lambda { @scope.setvar("var","not a hash", :append => true) }.should raise_error end end describe "when calling number?" do it "should return nil if called with anything not a number" do Puppet::Parser::Scope.number?([2]).should be_nil end it "should return a Fixnum for a Fixnum" do Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) end it "should return a Float for a Float" do Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) end it "should return 234 for '234'" do Puppet::Parser::Scope.number?("234").should == 234 end it "should return nil for 'not a number'" do Puppet::Parser::Scope.number?("not a number").should be_nil end it "should return 23.4 for '23.4'" do Puppet::Parser::Scope.number?("23.4").should == 23.4 end it "should return 23.4e13 for '23.4e13'" do Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 end it "should understand negative numbers" do Puppet::Parser::Scope.number?("-234").should == -234 end it "should know how to convert exponential float numbers ala '23e13'" do Puppet::Parser::Scope.number?("23e13").should == 23e13 end it "should understand hexadecimal numbers" do Puppet::Parser::Scope.number?("0x234").should == 0x234 end it "should understand octal numbers" do Puppet::Parser::Scope.number?("0755").should == 0755 end it "should return nil on malformed integers" do Puppet::Parser::Scope.number?("0.24.5").should be_nil end it "should convert strings with leading 0 to integer if they are not octal" do Puppet::Parser::Scope.number?("0788").should == 788 end it "should convert strings of negative integers" do Puppet::Parser::Scope.number?("-0788").should == -788 end it "should return nil on malformed hexadecimal numbers" do Puppet::Parser::Scope.number?("0x89g").should be_nil end end describe "when using ephemeral variables" do it "should store the variable value" do @scope.setvar("1", :value, :ephemeral => true) @scope.lookupvar("1").should == :value end it "should remove the variable value when unset_ephemeral_var is called" do @scope.setvar("1", :value, :ephemeral => true) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var - @scope.lookupvar("1", false).should == :undefined + @scope.lookupvar("1").should == :undefined end it "should not remove classic variables when unset_ephemeral_var is called" do @scope.setvar("myvar", :value1) @scope.setvar("1", :value2, :ephemeral => true) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var - @scope.lookupvar("myvar", false).should == :value1 + @scope.lookupvar("myvar").should == :value1 end it "should raise an error when setting it again" do @scope.setvar("1", :value2, :ephemeral => true) lambda { @scope.setvar("1", :value3, :ephemeral => true) }.should raise_error end it "should declare ephemeral number only variable names" do @scope.ephemeral?("0").should be_true end it "should not declare ephemeral other variable names" do @scope.ephemeral?("abc0").should be_nil end describe "with more than one level" do it "should prefer latest ephemeral scopes" do @scope.setvar("0", :earliest, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :latest, :ephemeral => true) - @scope.lookupvar("0", false).should == :latest + @scope.lookupvar("0").should == :latest end it "should be able to report the current level" do @scope.ephemeral_level.should == 1 @scope.new_ephemeral @scope.ephemeral_level.should == 2 end it "should check presence of an ephemeral variable accross multiple levels" do @scope.new_ephemeral @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :value2, :ephemeral => true) @scope.new_ephemeral @scope.ephemeral_include?("1").should be_true end it "should return false when an ephemeral variable doesn't exist in any ephemeral scope" do @scope.new_ephemeral @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :value2, :ephemeral => true) @scope.new_ephemeral @scope.ephemeral_include?("2").should be_false end it "should get ephemeral values from earlier scope when not in later" do @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :value2, :ephemeral => true) - @scope.lookupvar("1", false).should == :value1 + @scope.lookupvar("1").should == :value1 end describe "when calling unset_ephemeral_var without a level" do it "should remove all the variables values" do @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("1", :value2, :ephemeral => true) @scope.unset_ephemeral_var - @scope.lookupvar("1", false).should == :undefined + @scope.lookupvar("1").should == :undefined end end describe "when calling unset_ephemeral_var with a level" do it "should remove ephemeral scopes up to this level" do @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("1", :value2, :ephemeral => true) @scope.new_ephemeral @scope.setvar("1", :value3, :ephemeral => true) @scope.unset_ephemeral_var(2) - @scope.lookupvar("1", false).should == :value2 - end - end - end - end - - describe "when interpolating string" do - (0..9).each do |n| - it "should allow $#{n} to match" do - @scope.setvar(n.to_s, "value", :ephemeral => true) - - @scope.strinterp("$#{n}").should == "value" - end - end - - (0..9).each do |n| - it "should not allow $#{n} to match if not ephemeral" do - @scope.setvar(n.to_s, "value", :ephemeral => false) - - @scope.strinterp("$#{n}").should_not == "value" - end - end - - it "should not allow $10 to match" do - @scope.setvar("10", "value", :ephemeral => true) - - @scope.strinterp('==$10==').should_not == "==value==" - end - - it "should not allow ${10} to match" do - @scope.setvar("10", "value", :ephemeral => true) - - @scope.strinterp('==${10}==').should == "==value==" - end - - describe "with qualified variables" do - before do - @scopes = {} - klass = @scope.known_resource_types.add(Puppet::Resource::Type.new(:hostclass, "")) - Puppet::Parser::Resource.new("class", :main, :scope => @scope, :source => mock('source')).evaluate - @scopes[""] = @scope.class_scope(klass) - @scopes[""].setvar("test", "value") - - %w{one one::two one::two::three}.each do |name| - klass = @scope.known_resource_types.add(Puppet::Resource::Type.new(:hostclass, name)) - Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source')).evaluate - @scopes[name] = @scope.class_scope(klass) - @scopes[name].setvar("test", "value-#{name.sub(/.+::/,'')}") + @scope.lookupvar("1").should == :value2 end end - { - "===${one::two::three::test}===" => "===value-three===", - "===$one::two::three::test===" => "===value-three===", - "===${one::two::test}===" => "===value-two===", - "===$one::two::test===" => "===value-two===", - "===${one::test}===" => "===value-one===", - "===$one::test===" => "===value-one===", - "===${::test}===" => "===value===", - "===$::test===" => "===value===" - }.each do |input, output| - it "should parse '#{input}' correctly" do - @scope.strinterp(input).should == output - end - end - end - - tests = { - "===${test}===" => "===value===", - "===${test} ${test} ${test}===" => "===value value value===", - "===$test ${test} $test===" => "===value value value===", - "===\\$test===" => "===$test===", - '===\\$test string===' => "===$test string===", - '===$test string===' => "===value string===", - '===a testing $===' => "===a testing $===", - '===a testing \$===' => "===a testing $===", - "===an escaped \\\n carriage return===" => "===an escaped carriage return===", - '\$' => "$", - '\s' => "\s", - '\t' => "\t", - '\n' => "\n" - } - - tests.each do |input, output| - it "should parse '#{input}' correctly" do - @scope.setvar("test", "value") - @scope.strinterp(input).should == output - end - end - - # #523 - %w{d f h l w z}.each do |l| - it "should parse '#{l}' when escaped" do - string = "\\#{l}" - @scope.strinterp(string).should == string - end - end - end - - def test_strinterp - # Make and evaluate our classes so the qualified lookups work - parser = mkparser - klass = parser.newclass("") - scope = mkscope(:parser => parser) - Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => scope, :source => mock('source')).evaluate - - assert_nothing_raised { - scope.setvar("test","value") - } - - scopes = {"" => scope} - - %w{one one::two one::two::three}.each do |name| - klass = parser.newclass(name) - Puppet::Parser::Resource.new(:type => "class", :title => name, :scope => scope, :source => mock('source')).evaluate - scopes[name] = scope.class_scope(klass) - scopes[name].setvar("test", "value-#{name.sub(/.+::/,'')}") - end - - assert_equal("value", scope.lookupvar("::test"), "did not look up qualified value correctly") - tests.each do |input, output| - assert_nothing_raised("Failed to scan #{input.inspect}") do - assert_equal(output, scope.strinterp(input), 'did not parserret %s correctly' % input.inspect) - end - end - - logs = [] - Puppet::Util::Log.close - Puppet::Util::Log.newdestination(logs) - - # #523 - %w{d f h l w z}.each do |l| - string = "\\#{l}" - assert_nothing_raised do - - assert_equal( - string, scope.strinterp(string), - - 'did not parserret %s correctly' % string) - end - - - assert( - logs.detect { |m| m.message =~ /Unrecognised escape/ }, - - "Did not get warning about escape sequence with #{string}") - logs.clear end end describe "when setting ephemeral vars from matches" do before :each do @match = stub 'match', :is_a? => true @match.stubs(:[]).with(0).returns("this is a string") @match.stubs(:captures).returns([]) @scope.stubs(:setvar) end it "should accept only MatchData" do lambda { @scope.ephemeral_from("match") }.should raise_error end it "should set $0 with the full match" do @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] } @scope.ephemeral_from(@match) end it "should set every capture as ephemeral var" do @match.stubs(:captures).returns([:capture1,:capture2]) @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] } @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] } @scope.ephemeral_from(@match) end it "should create a new ephemeral level" do @scope.expects(:new_ephemeral) @scope.ephemeral_from(@match) end end describe "when unsetting variables" do it "should be able to unset normal variables" do @scope.setvar("foo", "bar") @scope.unsetvar("foo") - @scope.lookupvar("foo").should == "" + @scope.lookupvar("foo").should == :undefined end it "should be able to unset ephemeral variables" do @scope.setvar("0", "bar", :ephemeral => true) @scope.unsetvar("0") - @scope.lookupvar("0").should == "" + @scope.lookupvar("0").should == :undefined end it "should not unset ephemeral variables in previous ephemeral scope" do @scope.setvar("0", "bar", :ephemeral => true) @scope.new_ephemeral @scope.unsetvar("0") @scope.lookupvar("0").should == "bar" end end it "should use its namespaces to find hostclasses" do klass = @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "a::b::c") @scope.add_namespace "a::b" @scope.find_hostclass("c").should equal(klass) end it "should use its namespaces to find definitions" do define = @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "a::b::c") @scope.add_namespace "a::b" @scope.find_definition("c").should equal(define) end describe "when managing defaults" do it "should be able to set and lookup defaults" do param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.setdefaults(:mytype, param) @scope.lookupdefaults(:mytype).should == {:myparam => param} end it "should fail if a default is already defined and a new default is being defined" do param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.setdefaults(:mytype, param) lambda { @scope.setdefaults(:mytype, param) }.should raise_error(Puppet::ParseError) end it "should return multiple defaults at once" do param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.setdefaults(:mytype, param1) param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) @scope.setdefaults(:mytype, param2) @scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} end it "should look up defaults defined in parent scopes" do param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.setdefaults(:mytype, param1) child_scope = @scope.newscope param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) child_scope.setdefaults(:mytype, param2) child_scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} end end end diff --git a/spec/unit/parser/templatewrapper_spec.rb b/spec/unit/parser/templatewrapper_spec.rb index aab169348..600293bbf 100755 --- a/spec/unit/parser/templatewrapper_spec.rb +++ b/spec/unit/parser/templatewrapper_spec.rb @@ -1,145 +1,142 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/templatewrapper' describe Puppet::Parser::TemplateWrapper do before(:each) do @known_resource_types = Puppet::Resource::TypeCollection.new("env") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @scope = Puppet::Parser::Scope.new :compiler => @compiler @file = "fake_template" Puppet::Parser::Files.stubs(:find_template).returns("/tmp/fake_template") FileTest.stubs(:exists?).returns("true") File.stubs(:read).with("/tmp/fake_template").returns("template content") @tw = Puppet::Parser::TemplateWrapper.new(@scope) end + def mock_template(source=nil) + template_mock = mock("template", :result => "woot!") + ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + template_mock.expects(:filename=).with(source) + end + it "should create a new object TemplateWrapper from a scope" do tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.should be_a_kind_of(Puppet::Parser::TemplateWrapper) end it "should check template file existance and read its content" do Puppet::Parser::Files.expects(:find_template).with("fake_template", @scope.environment.to_s).returns("/tmp/fake_template") File.expects(:read).with("/tmp/fake_template").returns("template content") @tw.file = @file end it "should mark the file for watching" do Puppet::Parser::Files.expects(:find_template).returns("/tmp/fake_template") File.stubs(:read) @known_resource_types.expects(:watch_file).with("/tmp/fake_template") @tw.file = @file end it "should fail if a template cannot be found" do Puppet::Parser::Files.expects(:find_template).returns nil lambda { @tw.file = @file }.should raise_error(Puppet::ParseError) end it "should turn into a string like template[name] for file based template" do @tw.file = @file @tw.to_s.should eql("template[/tmp/fake_template]") end it "should turn into a string like template[inline] for string-based template" do @tw.to_s.should eql("template[inline]") end it "should return the processed template contents with a call to result" do - template_mock = mock("template", :result => "woot!") + mock_template("/tmp/fake_template") File.expects(:read).with("/tmp/fake_template").returns("template contents") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) @tw.file = @file @tw.result.should eql("woot!") end it "should return the processed template contents with a call to result and a string" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - + mock_template @tw.result("template contents").should eql("woot!") end it "should return the contents of a variable if called via method_missing" do - @scope.expects(:lookupvar).with("chicken", false).returns("is good") + @scope.expects(:lookupvar).with { |name,options| name == "chicken"}.returns("is good") tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.chicken.should eql("is good") end it "should throw an exception if a variable is called via method_missing and it does not exist" do - @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) + @scope.expects(:lookupvar).with { |name,options| name == "chicken"}.returns(:undefined) tw = Puppet::Parser::TemplateWrapper.new(@scope) lambda { tw.chicken }.should raise_error(Puppet::ParseError) end it "should allow you to check whether a variable is defined with has_variable?" do - @scope.expects(:lookupvar).with("chicken", false).returns("is good") + @scope.expects(:lookupvar).with { |name,options| name == "chicken"}.returns("is good") tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.has_variable?("chicken").should eql(true) end it "should allow you to check whether a variable is not defined with has_variable?" do - @scope.expects(:lookupvar).with("chicken", false).returns(:undefined) + @scope.expects(:lookupvar).with { |name,options| name == "chicken"}.returns(:undefined) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.has_variable?("chicken").should eql(false) end it "should allow you to retrieve the defined classes with classes" do catalog = mock 'catalog', :classes => ["class1", "class2"] @scope.expects(:catalog).returns( catalog ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.classes.should == ["class1", "class2"] end it "should allow you to retrieve all the tags with all_tags" do catalog = mock 'catalog', :tags => ["tag1", "tag2"] @scope.expects(:catalog).returns( catalog ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.all_tags.should == ["tag1","tag2"] end it "should allow you to retrieve the tags defined in the current scope" do @scope.expects(:tags).returns( ["tag1", "tag2"] ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.tags.should == ["tag1","tag2"] end it "should set all of the scope's variables as instance variables" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) - + mock_template @scope.expects(:to_hash).returns("one" => "foo") @tw.result("template contents") @tw.instance_variable_get("@one").should == "foo" end it "should not error out if one of the variables is a symbol" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + mock_template @scope.expects(:to_hash).returns(:_timestamp => "1234") @tw.result("template contents") end %w{! . ; :}.each do |badchar| it "should translate #{badchar} to _ when setting the instance variables" do - template_mock = mock("template", :result => "woot!") - ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) + mock_template + @scope.expects(:to_hash).returns("one#{badchar}" => "foo") + @tw.result("template contents") - @scope.expects(:to_hash).returns("one#{badchar}" => "foo") - @tw.result("template contents") - - @tw.instance_variable_get("@one_").should == "foo" - end + @tw.instance_variable_get("@one_").should == "foo" + end end end diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb old mode 100644 new mode 100755 index 12bc1ccd6..9367b61c8 --- a/spec/unit/parser/type_loader_spec.rb +++ b/spec/unit/parser/type_loader_spec.rb @@ -1,231 +1,230 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/parser/type_loader' require 'puppet_spec/files' describe Puppet::Parser::TypeLoader do include PuppetSpec::Files before do @loader = Puppet::Parser::TypeLoader.new(:myenv) end it "should support an environment" do loader = Puppet::Parser::TypeLoader.new(:myenv) loader.environment.name.should == :myenv end it "should include the Environment Helper" do @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper) end it "should delegate its known resource types to its environment" do @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) end describe "when loading names from namespaces" do it "should do nothing if the name to import is an empty string" do @loader.expects(:name2files).never @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil end it "should attempt to import each generated name" do @loader.expects(:import).with("foo/bar",nil).returns([]) @loader.expects(:import).with("foo",nil).returns([]) @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false } end end describe "when importing" do before do Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}] Puppet::Parser::Parser.any_instance.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) Puppet::Parser::Parser.any_instance.stubs(:file=) end it "should return immediately when imports are being ignored" do Puppet::Parser::Files.expects(:find_manifests).never Puppet[:ignoreimport] = true @loader.import("foo").should be_nil end it "should find all manifests matching the file or pattern" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}] @loader.import("myfile") end it "should use the directory of the current file if one is set" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == "/current" }.returns ["modname", %w{one}] @loader.import("myfile", "/current/file") end it "should pass the environment when looking for files" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}] @loader.import("myfile") end it "should fail if no files are found" do Puppet::Parser::Files.expects(:find_manifests).returns [nil, []] lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError) end it "should parse each found file" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] @loader.expects(:parse_file).with("/one").returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") end it "should make each file qualified before attempting to parse it" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] @loader.expects(:parse_file).with("/current/one").returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile", "/current/file") end it "should not attempt to import files that have already been imported" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] Puppet::Parser::Parser.any_instance.expects(:parse).once.returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") # This will fail if it tries to reimport the file. @loader.import("myfile") end end describe "when importing all" do before do @base = tmpdir("base") # Create two module path directories @modulebase1 = File.join(@base, "first") FileUtils.mkdir_p(@modulebase1) @modulebase2 = File.join(@base, "second") FileUtils.mkdir_p(@modulebase2) Puppet[:modulepath] = "#{@modulebase1}:#{@modulebase2}" end def mk_module(basedir, name) module_dir = File.join(basedir, name) # Go ahead and make our manifest directory FileUtils.mkdir_p(File.join(module_dir, "manifests")) return Puppet::Module.new(name) end # We have to pass the base path so that we can # write to modules that are in the second search path def mk_manifests(base, mod, type, files) exts = {"ruby" => ".rb", "puppet" => ".pp"} files.collect do |file| name = mod.name + "::" + file.gsub("/", "::") path = File.join(base, mod.name, "manifests", file + exts[type]) FileUtils.mkdir_p(File.split(path)[0]) # write out the class if type == "ruby" File.open(path, "w") { |f| f.print "hostclass '#{name}' do\nend" } else File.open(path, "w") { |f| f.print "class #{name} {}" } end name end end it "should load all puppet manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "puppet", %w{a b}) mk_manifests(@modulebase2, @module2, "puppet", %w{c d}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should load all ruby manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "ruby", %w{a b}) mk_manifests(@modulebase2, @module2, "ruby", %w{c d}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should not load manifests from duplicate modules later in the module path" do @module1 = mk_module(@modulebase1, "one") # duplicate @module2 = mk_module(@modulebase2, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a}) mk_manifests(@modulebase2, @module2, "puppet", %w{c}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::c").should be_nil end it "should load manifests from subdirectories" do @module1 = mk_module(@modulebase1, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a a/b a/b/c}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::a::b::c").should be_instance_of(Puppet::Resource::Type) end end describe "when parsing a file" do before do @parser = Puppet::Parser::Parser.new(@loader.environment) @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @parser.stubs(:file=) Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser end it "should create a new parser instance for each file using the current environment" do Puppet::Parser::Parser.expects(:new).with(@loader.environment).returns @parser @loader.parse_file("/my/file") end it "should assign the parser its file and parse" do @parser.expects(:file=).with("/my/file") @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.parse_file("/my/file") end end it "should be able to add classes to the current resource type collection" do file = tmpfile("simple_file.pp") File.open(file, "w") { |f| f.puts "class foo {}" } @loader.import(file) @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type) end describe "when deciding where to look for files" do { 'foo' => ['foo'], 'foo::bar' => ['foo/bar', 'foo'], 'foo::bar::baz' => ['foo/bar/baz', 'foo/bar', 'foo'] }.each do |fqname, expected_paths| it "should look for #{fqname.inspect} in #{expected_paths.inspect}" do @loader.instance_eval { name2files(fqname) }.should == expected_paths end end end end diff --git a/spec/unit/property/ensure_spec.rb b/spec/unit/property/ensure_spec.rb old mode 100644 new mode 100755 index d3029c658..35151553b --- a/spec/unit/property/ensure_spec.rb +++ b/spec/unit/property/ensure_spec.rb @@ -1,13 +1,12 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/property/ensure' klass = Puppet::Property::Ensure describe klass do it "should be a subclass of Property" do klass.superclass.must == Puppet::Property end end diff --git a/spec/unit/property/keyvalue_spec.rb b/spec/unit/property/keyvalue_spec.rb old mode 100644 new mode 100755 index a0175cfa3..821c61799 --- a/spec/unit/property/keyvalue_spec.rb +++ b/spec/unit/property/keyvalue_spec.rb @@ -1,168 +1,167 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/property/keyvalue' klass = Puppet::Property::KeyValue describe klass do it "should be a subclass of Property" do klass.superclass.must == Puppet::Property end describe "as an instance" do before do # Wow that's a messy interface to the resource. klass.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = klass.new(:resource => @resource) end it "should have a , as default delimiter" do @property.delimiter.should == ";" end it "should have a = as default separator" do @property.separator.should == "=" end it "should have a :membership as default membership" do @property.membership.should == :key_value_membership end it "should return the same value passed into should_to_s" do @property.should_to_s({:foo => "baz", :bar => "boo"}) == "foo=baz;bar=boo" end it "should return the passed in array values joined with the delimiter from is_to_s" do @property.is_to_s({"foo" => "baz" , "bar" => "boo"}).should == "foo=baz;bar=boo" end describe "when calling inclusive?" do it "should use the membership method to look up on the @resource" do @property.expects(:membership).returns(:key_value_membership) @resource.expects(:[]).with(:key_value_membership) @property.inclusive? end it "should return true when @resource[membership] == inclusive" do @property.stubs(:membership).returns(:key_value_membership) @resource.stubs(:[]).with(:key_value_membership).returns(:inclusive) @property.inclusive?.must == true end it "should return false when @resource[membership] != inclusive" do @property.stubs(:membership).returns(:key_value_membership) @resource.stubs(:[]).with(:key_value_membership).returns(:minimum) @property.inclusive?.must == false end end describe "when calling process_current_hash" do it "should return {} if hash is :absent" do @property.process_current_hash(:absent).must == {} end it "should set every key to nil if inclusive?" do @property.stubs(:inclusive?).returns(true) @property.process_current_hash({:foo => "bar", :do => "re"}).must == { :foo => nil, :do => nil } end it "should return the hash if !inclusive?" do @property.stubs(:inclusive?).returns(false) @property.process_current_hash({:foo => "bar", :do => "re"}).must == {:foo => "bar", :do => "re"} end end describe "when calling should" do it "should return nil if @should is nil" do @property.should.must == nil end it "should call process_current_hash" do @property.should = ["foo=baz", "bar=boo"] @property.stubs(:retrieve).returns({:do => "re", :mi => "fa" }) @property.expects(:process_current_hash).returns({}) @property.should end it "should return the hashed values of @should and the nilled values of retrieve if inclusive" do @property.should = ["foo=baz", "bar=boo"] @property.expects(:retrieve).returns({:do => "re", :mi => "fa" }) @property.expects(:inclusive?).returns(true) @property.should.must == { :foo => "baz", :bar => "boo", :do => nil, :mi => nil } end it "should return the hashed @should + the unique values of retrieve if !inclusive" do @property.should = ["foo=baz", "bar=boo"] @property.expects(:retrieve).returns({:foo => "diff", :do => "re", :mi => "fa"}) @property.expects(:inclusive?).returns(false) @property.should.must == { :foo => "baz", :bar => "boo", :do => "re", :mi => "fa" } end end describe "when calling retrieve" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) end it "should send 'name' to the provider" do @provider.expects(:send).with(:keys) @property.expects(:name).returns(:keys) @property.retrieve end it "should return a hash with the provider returned info" do @provider.stubs(:send).with(:keys).returns({"do" => "re", "mi" => "fa" }) @property.stubs(:name).returns(:keys) @property.retrieve == {"do" => "re", "mi" => "fa" } end it "should return :absent when the provider returns :absent" do @provider.stubs(:send).with(:keys).returns(:absent) @property.stubs(:name).returns(:keys) @property.retrieve == :absent end end describe "when calling hashify" do it "should return the array hashified" do @property.hashify(["foo=baz", "bar=boo"]).must == { :foo => "baz", :bar => "boo" } end end describe "when calling safe_insync?" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) @property.stubs(:name).returns(:prop_name) end it "should return true unless @should is defined and not nil" do @property.safe_insync?("foo") == true end it "should return true if the passed in values is nil" do @property.should = "foo" @property.safe_insync?(nil) == true end it "should return true if hashified should value == (retrieved) value passed in" do @provider.stubs(:prop_name).returns({ :foo => "baz", :bar => "boo" }) @property.should = ["foo=baz", "bar=boo"] @property.expects(:inclusive?).returns(true) @property.safe_insync?({ :foo => "baz", :bar => "boo" }).must == true end it "should return false if prepared value != should value" do @provider.stubs(:prop_name).returns({ "foo" => "bee", "bar" => "boo" }) @property.should = ["foo=baz", "bar=boo"] @property.expects(:inclusive?).returns(true) @property.safe_insync?({ "foo" => "bee", "bar" => "boo" }).must == false end end end end diff --git a/spec/unit/property/list_spec.rb b/spec/unit/property/list_spec.rb old mode 100644 new mode 100755 index 704fbe3d9..a29d65751 --- a/spec/unit/property/list_spec.rb +++ b/spec/unit/property/list_spec.rb @@ -1,166 +1,165 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/property/list' list_class = Puppet::Property::List describe list_class do it "should be a subclass of Property" do list_class.superclass.must == Puppet::Property end describe "as an instance" do before do # Wow that's a messy interface to the resource. list_class.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = list_class.new(:resource => @resource) end it "should have a , as default delimiter" do @property.delimiter.should == "," end it "should have a :membership as default membership" do @property.membership.should == :membership end it "should return the same value passed into should_to_s" do @property.should_to_s("foo") == "foo" end it "should return the passed in array values joined with the delimiter from is_to_s" do @property.is_to_s(["foo","bar"]).should == "foo,bar" end it "should be able to correctly convert ':absent' to a string" do @property.is_to_s(:absent).should == "absent" end describe "when adding should to current" do it "should add the arrays when current is an array" do @property.add_should_with_current(["foo"], ["bar"]).should == ["foo", "bar"] end it "should return should if current is not a array" do @property.add_should_with_current(["foo"], :absent).should == ["foo"] end it "should return only the uniq elements" do @property.add_should_with_current(["foo", "bar"], ["foo", "baz"]).should == ["foo", "bar", "baz"] end end describe "when calling inclusive?" do it "should use the membership method to look up on the @resource" do @property.expects(:membership).returns(:membership) @resource.expects(:[]).with(:membership) @property.inclusive? end it "should return true when @resource[membership] == inclusive" do @property.stubs(:membership).returns(:membership) @resource.stubs(:[]).with(:membership).returns(:inclusive) @property.inclusive?.must == true end it "should return false when @resource[membership] != inclusive" do @property.stubs(:membership).returns(:membership) @resource.stubs(:[]).with(:membership).returns(:minimum) @property.inclusive?.must == false end end describe "when calling should" do it "should return nil if @should is nil" do @property.should.must == nil end it "should return the sorted values of @should as a string if inclusive" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(true) @property.should.must == "bar,foo" end it "should return the uniq sorted values of @should + retrieve as a string if !inclusive" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(false) @property.expects(:retrieve).returns(["foo","baz"]) @property.should.must == "bar,baz,foo" end end describe "when calling retrieve" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) end it "should send 'name' to the provider" do @provider.expects(:send).with(:group) @property.expects(:name).returns(:group) @property.retrieve end it "should return an array with the provider returned info" do @provider.stubs(:send).with(:group).returns("foo,bar,baz") @property.stubs(:name).returns(:group) @property.retrieve == ["foo", "bar", "baz"] end it "should return :absent when the provider returns :absent" do @provider.stubs(:send).with(:group).returns(:absent) @property.stubs(:name).returns(:group) @property.retrieve == :absent end end describe "when calling safe_insync?" do it "should return true unless @should is defined and not nil" do @property.must be_safe_insync("foo") end it "should return true unless the passed in values is not nil" do @property.should = "foo" @property.must be_safe_insync(nil) end it "should call prepare_is_for_comparison with value passed in and should" do @property.should = "foo" @property.expects(:prepare_is_for_comparison).with("bar") @property.expects(:should) @property.safe_insync?("bar") end it "should return true if 'is' value is array of comma delimited should values" do @property.should = "bar,foo" @property.expects(:inclusive?).returns(true) @property.must be_safe_insync(["bar","foo"]) end it "should return true if 'is' value is :absent and should value is empty string" do @property.should = "" @property.expects(:inclusive?).returns(true) @property.must be_safe_insync([]) end it "should return false if prepared value != should value" do @property.should = "bar,baz,foo" @property.expects(:inclusive?).returns(true) @property.must_not be_safe_insync(["bar","foo"]) end end describe "when calling dearrayify" do it "should sort and join the array with 'delimiter'" do array = mock "array" array.expects(:sort).returns(array) array.expects(:join).with(@property.delimiter) @property.dearrayify(array) end end end end diff --git a/spec/unit/property/ordered_list_spec.rb b/spec/unit/property/ordered_list_spec.rb old mode 100644 new mode 100755 index 460bec79d..2ad05367c --- a/spec/unit/property/ordered_list_spec.rb +++ b/spec/unit/property/ordered_list_spec.rb @@ -1,64 +1,63 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/property/ordered_list' ordered_list_class = Puppet::Property::OrderedList describe ordered_list_class do it "should be a subclass of List" do ordered_list_class.superclass.must == Puppet::Property::List end describe "as an instance" do before do # Wow that's a messy interface to the resource. ordered_list_class.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = ordered_list_class.new(:resource => @resource) end describe "when adding should to current" do it "should add the arrays when current is an array" do @property.add_should_with_current(["should"], ["current"]).should == ["should", "current"] end it "should return 'should' if current is not a array" do @property.add_should_with_current(["should"], :absent).should == ["should"] end it "should return only the uniq elements leading with the order of 'should'" do @property.add_should_with_current(["this", "is", "should"], ["is", "this", "current"]).should == ["this", "is", "should", "current"] end end describe "when calling should" do it "should return nil if @should is nil" do @property.should.must == nil end it "should return the values of @should (without sorting) as a string if inclusive" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(true) @property.should.must == "foo,bar" end it "should return the uniq values of @should + retrieve as a string if !inclusive with the @ values leading" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(false) @property.expects(:retrieve).returns(["foo","baz"]) @property.should.must == "foo,bar,baz" end end describe "when calling dearrayify" do it "should join the array with the delimiter" do array = mock "array" array.expects(:join).with(@property.delimiter) @property.dearrayify(array) end end end end diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index 602d3c66b..7728b5d40 100755 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -1,410 +1,409 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/property' describe Puppet::Property do before do @class = Class.new(Puppet::Property) do @name = :foo end @class.initvars @provider = mock 'provider' @resource = stub 'resource', :provider => @provider @resource.stub_everything @property = @class.new :resource => @resource end it "should return its name as a string when converted to a string" do @property.to_s.should == @property.name.to_s end it "should be able to look up the modified name for a given value" do @class.newvalue(:foo) @class.value_name("foo").should == :foo end it "should be able to look up the modified name for a given value matching a regex" do @class.newvalue(%r{.}) @class.value_name("foo").should == %r{.} end it "should be able to look up a given value option" do @class.newvalue(:foo, :event => :whatever) @class.value_option(:foo, :event).should == :whatever end it "should be able to specify required features" do @class.should respond_to(:required_features=) end {"one" => [:one],:one => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value| it "should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})" do @class.required_features = in_value @class.required_features.should == out_value end } it "should be able to shadow metaparameters" do @property.must respond_to(:shadow) end describe "when returning the default event name" do before do @resource = stub 'resource' @instance = @class.new(:resource => @resource) @instance.stubs(:should).returns "myval" end it "should use the current 'should' value to pick the event name" do @instance.expects(:should).returns "myvalue" @class.expects(:value_option).with('myvalue', :event).returns :event_name @instance.event_name end it "should return any event defined with the specified value" do @instance.expects(:should).returns :myval @class.expects(:value_option).with(:myval, :event).returns :event_name @instance.event_name.should == :event_name end describe "and the property is 'ensure'" do before do @instance.stubs(:name).returns :ensure @resource.expects(:type).returns :mytype end it "should use _created if the 'should' value is 'present'" do @instance.expects(:should).returns :present @instance.event_name.should == :mytype_created end it "should use _removed if the 'should' value is 'absent'" do @instance.expects(:should).returns :absent @instance.event_name.should == :mytype_removed end it "should use _changed if the 'should' value is not 'absent' or 'present'" do @instance.expects(:should).returns :foo @instance.event_name.should == :mytype_changed end it "should use _changed if the 'should value is nil" do @instance.expects(:should).returns nil @instance.event_name.should == :mytype_changed end end it "should use _changed if the property is not 'ensure'" do @instance.stubs(:name).returns :myparam @instance.expects(:should).returns :foo @instance.event_name.should == :myparam_changed end it "should use _changed if no 'should' value is set" do @instance.stubs(:name).returns :myparam @instance.expects(:should).returns nil @instance.event_name.should == :myparam_changed end end describe "when creating an event" do before do @event = Puppet::Transaction::Event.new # Use a real resource so we can test the event creation integration @resource = Puppet::Type.type(:mount).new :name => "foo" @instance = @class.new(:resource => @resource) @instance.stubs(:should).returns "myval" end it "should use an event from the resource as the base event" do event = Puppet::Transaction::Event.new @resource.expects(:event).returns event @instance.event.should equal(event) end it "should have the default event name" do @instance.expects(:event_name).returns :my_event @instance.event.name.should == :my_event end it "should have the property's name" do @instance.event.property.should == @instance.name.to_s end it "should have the 'should' value set" do @instance.stubs(:should).returns "foo" @instance.event.desired_value.should == "foo" end it "should provide its path as the source description" do @instance.stubs(:path).returns "/my/param" @instance.event.source_description.should == "/my/param" end end describe "when shadowing metaparameters" do before do @shadow_class = Class.new(Puppet::Property) do @name = :alias end @shadow_class.initvars end it "should create an instance of the metaparameter at initialization" do Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => @resource) @shadow_class.new :resource => @resource end it "should munge values using the shadow's munge method" do shadow = mock 'shadow' Puppet::Type.metaparamclass(:alias).expects(:new).returns shadow shadow.expects(:munge).with "foo" property = @shadow_class.new :resource => @resource property.munge("foo") end end describe "when defining new values" do it "should define a method for each value created with a block that's not a regex" do @class.newvalue(:foo) { } @property.must respond_to(:set_foo) end end describe "when assigning the value" do it "should just set the 'should' value" do @property.value = "foo" @property.should.must == "foo" end it "should validate each value separately" do @property.expects(:validate).with("one") @property.expects(:validate).with("two") @property.value = %w{one two} end it "should munge each value separately and use any result as the actual value" do @property.expects(:munge).with("one").returns :one @property.expects(:munge).with("two").returns :two # Do this so we get the whole array back. @class.array_matching = :all @property.value = %w{one two} @property.should.must == [:one, :two] end it "should return any set value" do (@property.value = :one).should == :one end end describe "when returning the value" do it "should return nil if no value is set" do @property.should.must be_nil end it "should return the first set 'should' value if :array_matching is set to :first" do @class.array_matching = :first @property.should = %w{one two} @property.should.must == "one" end it "should return all set 'should' values as an array if :array_matching is set to :all" do @class.array_matching = :all @property.should = %w{one two} @property.should.must == %w{one two} end it "should default to :first array_matching" do @class.array_matching.should == :first end it "should unmunge the returned value if :array_matching is set to :first" do @property.class.unmunge do |v| v.to_sym end @class.array_matching = :first @property.should = %w{one two} @property.should.must == :one end it "should unmunge all the returned values if :array_matching is set to :all" do @property.class.unmunge do |v| v.to_sym end @class.array_matching = :all @property.should = %w{one two} @property.should.must == [:one, :two] end end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do lambda { @property.should = "foo" }.should_not raise_error end it "should fail if the value is not a defined value or alias and does not match a regex" do @class.newvalue(:foo) lambda { @property.should = "bar" }.should raise_error end it "should succeeed if the value is one of the defined values" do @class.newvalue(:foo) lambda { @property.should = :foo }.should_not raise_error end it "should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do @class.newvalue(:foo) lambda { @property.should = "foo" }.should_not raise_error end it "should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do @class.newvalue("foo") lambda { @property.should = :foo }.should_not raise_error end it "should succeed if the value is one of the defined aliases" do @class.newvalue("foo") @class.aliasvalue("bar", "foo") lambda { @property.should = :bar }.should_not raise_error end it "should succeed if the value matches one of the regexes" do @class.newvalue(/./) lambda { @property.should = "bar" }.should_not raise_error end it "should validate that all required features are present" do @class.newvalue(:foo, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns true @property.should = :foo end it "should fail if required features are missing" do @class.newvalue(:foo, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns false lambda { @property.should = :foo }.should raise_error(Puppet::Error) end it "should internally raise an ArgumentError if required features are missing" do @class.newvalue(:foo, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns false lambda { @property.validate_features_per_value :foo }.should raise_error(ArgumentError) end it "should validate that all required features are present for regexes" do value = @class.newvalue(/./, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns true @property.should = "foo" end it "should support specifying an individual required feature" do value = @class.newvalue(/./, :required_features => :a) @provider.expects(:satisfies?).returns true @property.should = "foo" end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do @property.munge("foo").should == "foo" end it "should return return any matching defined values" do @class.newvalue(:foo) @property.munge("foo").should == :foo end it "should return any matching aliases" do @class.newvalue(:foo) @class.aliasvalue(:bar, :foo) @property.munge("bar").should == :foo end it "should return the value if it matches a regex" do @class.newvalue(/./) @property.munge("bar").should == "bar" end it "should return the value if no other option is matched" do @class.newvalue(:foo) @property.munge("bar").should == "bar" end end describe "when syncing the 'should' value" do it "should set the value" do @class.newvalue(:foo) @property.should = :foo @property.expects(:set).with(:foo) @property.sync end end describe "when setting a value" do it "should catch exceptions and raise Puppet::Error" do @class.newvalue(:foo) { raise "eh" } lambda { @property.set(:foo) }.should raise_error(Puppet::Error) end describe "that was defined without a block" do it "should call the settor on the provider" do @class.newvalue(:bar) @provider.expects(:foo=).with :bar @property.set(:bar) end end describe "that was defined with a block" do it "should call the method created for the value if the value is not a regex" do @class.newvalue(:bar) {} @property.expects(:set_bar) @property.set(:bar) end it "should call the provided block if the value is a regex" do @class.newvalue(/./) { self.test } @property.expects(:test) @property.set("foo") end end end describe "when producing a change log" do it "should say 'defined' when the current value is 'absent'" do @property.change_to_s(:absent, "foo").should =~ /^defined/ end it "should say 'undefined' when the new value is 'absent'" do @property.change_to_s("foo", :absent).should =~ /^undefined/ end it "should say 'changed' when neither value is 'absent'" do @property.change_to_s("foo", "bar").should =~ /changed/ end end end diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb old mode 100644 new mode 100755 index c65f39097..5bb98eadf --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -1,491 +1,490 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:augeas).provider(:augeas) describe provider_class do describe "command parsing" do before do @resource = stub("resource") @provider = provider_class.new(@resource) end it "should break apart a single line into three tokens and clean up the context" do @resource.stubs(:[]).returns("/context") tokens = @provider.parse_commands("set Jar/Jar Binks") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/context/Jar/Jar" tokens[0][2].should == "Binks" end it "should break apart a multiple line into six tokens" do @resource.stubs(:[]).returns("") tokens = @provider.parse_commands("set /Jar/Jar Binks\nrm anakin") tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 2 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "anakin" end it "should strip whitespace and ignore blank lines" do @resource.stubs(:[]).returns("") tokens = @provider.parse_commands(" set /Jar/Jar Binks \t\n \n\n rm anakin ") tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 2 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "anakin" end it "should handle arrays" do @resource.stubs(:[]).returns("/foo/") commands = ["set /Jar/Jar Binks", "rm anakin"] tokens = @provider.parse_commands(commands) tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 2 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "/foo/anakin" end # This is not supported in the new parsing class #it "should concat the last values" do # provider = provider_class.new # tokens = provider.parse_commands("set /Jar/Jar Binks is my copilot") # tokens.size.should == 1 # tokens[0].size.should == 3 # tokens[0][0].should == "set" # tokens[0][1].should == "/Jar/Jar" # tokens[0][2].should == "Binks is my copilot" #end it "should accept spaces in the value and single ticks" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands("set JarJar 'Binks is my copilot'") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/foo/JarJar" tokens[0][2].should == "Binks is my copilot" end it "should accept spaces in the value and double ticks" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands('set /JarJar "Binks is my copilot"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == '/JarJar' tokens[0][2].should == 'Binks is my copilot' end it "should accept mixed ticks" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands('set JarJar "Some \'Test\'"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == '/foo/JarJar' tokens[0][2].should == "Some \'Test\'" end it "should handle predicates with literals" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands("rm */*[module='pam_console.so']") tokens.should == [["rm", "/foo/*/*[module='pam_console.so']"]] end it "should handle whitespace in predicates" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands("ins 42 before /files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]") tokens.should == [["ins", "42", "before","/files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]"]] end it "should handle multiple predicates" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands("clear pam.d/*/*[module = 'system-auth'][type = 'account']") tokens.should == [["clear", "/foo/pam.d/*/*[module = 'system-auth'][type = 'account']"]] end it "should handle nested predicates" do @resource.stubs(:[]).returns("/foo/") args = ["clear", "/foo/pam.d/*/*[module[ ../type = 'type] = 'system-auth'][type[last()] = 'account']"] tokens = @provider.parse_commands(args.join(" ")) tokens.should == [ args ] end it "should handle escaped doublequotes in doublequoted string" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands("set /foo \"''\\\"''\"") tokens.should == [[ "set", "/foo", "''\\\"''" ]] end it "should allow escaped spaces and brackets in paths" do @resource.stubs(:[]).returns("/foo/") args = [ "set", "/white\\ space/\\[section", "value" ] tokens = @provider.parse_commands(args.join(" \t ")) tokens.should == [ args ] end it "should allow single quoted escaped spaces in paths" do @resource.stubs(:[]).returns("/foo/") args = [ "set", "'/white\\ space/key'", "value" ] tokens = @provider.parse_commands(args.join(" \t ")) tokens.should == [[ "set", "/white\\ space/key", "value" ]] end it "should allow double quoted escaped spaces in paths" do @resource.stubs(:[]).returns("/foo/") args = [ "set", '"/white\\ space/key"', "value" ] tokens = @provider.parse_commands(args.join(" \t ")) tokens.should == [[ "set", "/white\\ space/key", "value" ]] end it "should remove trailing slashes" do @resource.stubs(:[]).returns("/foo/") tokens = @provider.parse_commands("set foo/ bar") tokens.should == [[ "set", "/foo/foo", "bar" ]] end end describe "get filters" do before do augeas_stub = stub("augeas", :get => "value") @provider = provider_class.new @provider.aug= augeas_stub end it "should return false for a = nonmatch" do command = ["get", "fake value", "==", "value"] @provider.process_get(command).should == true end it "should return true for a != match" do command = ["get", "fake value", "!=", "value"] @provider.process_get(command).should == false end it "should return true for a =~ match" do command = ["get", "fake value", "=~", "val*"] @provider.process_get(command).should == true end it "should return false for a == nonmatch" do command = ["get", "fake value", "=~", "num*"] @provider.process_get(command).should == false end end describe "match filters" do before do resource = stub("resource", :[] => "") augeas_stub = stub("augeas", :match => ["set", "of", "values"]) @provider = provider_class.new(resource) @provider.aug= augeas_stub end it "should return true for size match" do command = ["match", "fake value", "size == 3"] @provider.process_match(command).should == true end it "should return false for a size non match" do command = ["match", "fake value", "size < 3"] @provider.process_match(command).should == false end it "should return true for includes match" do command = ["match", "fake value", "include values"] @provider.process_match(command).should == true end it "should return false for includes non match" do command = ["match", "fake value", "include JarJar"] @provider.process_match(command).should == false end it "should return true for includes match" do command = ["match", "fake value", "not_include JarJar"] @provider.process_match(command).should == true end it "should return false for includes non match" do command = ["match", "fake value", "not_include values"] @provider.process_match(command).should == false end it "should return true for an array match" do command = ["match", "fake value", "== ['set', 'of', 'values']"] @provider.process_match(command).should == true end it "should return false for an array non match" do command = ["match", "fake value", "== ['this', 'should', 'not', 'match']"] @provider.process_match(command).should == false end it "should return false for an array match with noteq" do command = ["match", "fake value", "!= ['set', 'of', 'values']"] @provider.process_match(command).should == false end it "should return true for an array non match with noteq" do command = ["match", "fake value", "!= ['this', 'should', 'not', 'match']"] @provider.process_match(command).should == true end end describe "need to run" do it "should handle no filters" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("").then.returns("") augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider = provider_class.new(resource) provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end it "should return true when a get filter matches" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("get path == value").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :get => "value") augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end it "should return false when a get filter does not match" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("get path == another value").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :get => "value") augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end it "should return true when a match filter matches" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("match path size == 3").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end it "should return false when a match filter does not match" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("match path size == 2").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end #This is a copy of the last one, with setting the force to true it "setting force should not change the above logic" do resource = stub("resource") resource.stubs(:[]).returns(true).then.returns("match path size == 2").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end #Ticket 5211 testing it "should return true when a size != the provided value" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("match path size != 17").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == true end #Ticket 5211 testing it "should return false when a size doeas equal the provided value" do resource = stub("resource") resource.stubs(:[]).returns(false).then.returns("match path size != 3").then.returns("") provider = provider_class.new(resource) augeas_stub = stub("augeas", :match => ["set", "of", "values"]) augeas_stub.stubs("close") provider.aug= augeas_stub provider.stubs(:get_augeas_version).returns("0.3.5") provider.need_to_run?.should == false end end describe "augeas execution integration" do before do @resource = stub("resource") @provider = provider_class.new(@resource) @augeas = stub("augeas") @provider.aug= @augeas @provider.stubs(:get_augeas_version).returns("0.3.5") end it "should handle set commands" do command = "set JarJar Binks" context = "/some/path/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:set).with("/some/path/JarJar", "Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle rm commands" do command = "rm /Jar/Jar" context = "" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:rm).with("/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle remove commands" do command = "remove /Jar/Jar" context = "" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:rm).with("/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle clear commands" do command = "clear Jar/Jar" context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins commands with before" do command = "ins Binks before Jar/Jar" context = "/foo" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins commands with after" do command = "ins Binks after /Jar/Jar" context = "/foo" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins with no context" do command = "ins Binks after /Jar/Jar" context = "" # this is the default @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle multiple commands" do command = ["ins Binks after /Jar/Jar", "clear Jar/Jar"] context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle defvar commands" do command = "defvar myjar Jar/Jar" context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:defvar).with("myjar", "/foo/Jar/Jar").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should pass through augeas variables without context" do command = ["defvar myjar Jar/Jar","set $myjar/Binks 1"] context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:defvar).with("myjar", "/foo/Jar/Jar").returns(true) # this is the important bit, shouldn't be /foo/$myjar/Binks @augeas.expects(:set).with("$myjar/Binks", "1").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle defnode commands" do command = "defnode newjar Jar/Jar[last()+1] Binks" context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:defnode).with("newjar", "/foo/Jar/Jar[last()+1]", "Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle mv commands" do command = "mv Jar/Jar Binks" context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:mv).with("/foo/Jar/Jar", "/foo/Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle setm commands" do command = ["set test[1]/Jar/Jar Foo","set test[2]/Jar/Jar Bar","setm test Jar/Jar Binks"] context = "/foo/" @resource.expects(:[]).times(2).returns(command).then.returns(context) @augeas.expects(:set).with("/foo/test[1]/Jar/Jar", "Foo").returns(true) @augeas.expects(:set).with("/foo/test[2]/Jar/Jar", "Bar").returns(true) @augeas.expects(:setm).with("/foo/test", "Jar/Jar", "Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end end end diff --git a/spec/unit/provider/confine/exists_spec.rb b/spec/unit/provider/confine/exists_spec.rb index 1dabafb19..1e2d7f86f 100755 --- a/spec/unit/provider/confine/exists_spec.rb +++ b/spec/unit/provider/confine/exists_spec.rb @@ -1,74 +1,73 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine/exists' describe Puppet::Provider::Confine::Exists do before do @confine = Puppet::Provider::Confine::Exists.new("/my/file") @confine.label = "eh" end it "should be named :exists" do Puppet::Provider::Confine::Exists.name.should == :exists end it "should use the 'pass?' method to test validity" do @confine.expects(:pass?).with("/my/file") @confine.valid? end it "should return false if the value is false" do @confine.pass?(false).should be_false end it "should return false if the value does not point to a file" do FileTest.expects(:exist?).with("/my/file").returns false @confine.pass?("/my/file").should be_false end it "should return true if the value points to a file" do FileTest.expects(:exist?).with("/my/file").returns true @confine.pass?("/my/file").should be_true end it "should produce a message saying that a file is missing" do @confine.message("/my/file").should be_include("does not exist") end describe "and the confine is for binaries" do before { @confine.stubs(:for_binary).returns true } it "should use its 'which' method to look up the full path of the file" do @confine.expects(:which).returns nil @confine.pass?("/my/file") end it "should return false if no executable can be found" do @confine.expects(:which).with("/my/file").returns nil @confine.pass?("/my/file").should be_false end it "should return true if the executable can be found" do @confine.expects(:which).with("/my/file").returns "/my/file" @confine.pass?("/my/file").should be_true end end it "should produce a summary containing all missing files" do FileTest.stubs(:exist?).returns true FileTest.expects(:exist?).with("/two").returns false FileTest.expects(:exist?).with("/four").returns false confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four} confine.summary.should == %w{/two /four} end it "should summarize multiple instances by returning a flattened array of their summaries" do c1 = mock '1', :summary => %w{one} c2 = mock '2', :summary => %w{two} c3 = mock '3', :summary => %w{three} Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three} end end diff --git a/spec/unit/provider/confine/false_spec.rb b/spec/unit/provider/confine/false_spec.rb index 8ea4bf740..1afa57cbc 100755 --- a/spec/unit/provider/confine/false_spec.rb +++ b/spec/unit/provider/confine/false_spec.rb @@ -1,53 +1,52 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine/false' describe Puppet::Provider::Confine::False do it "should be named :false" do Puppet::Provider::Confine::False.name.should == :false end it "should require a value" do lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) end describe "when testing values" do before { @confine = Puppet::Provider::Confine::False.new("foo") } it "should use the 'pass?' method to test validity" do @confine = Puppet::Provider::Confine::False.new("foo") @confine.label = "eh" @confine.expects(:pass?).with("foo") @confine.valid? end it "should return true if the value is false" do @confine.pass?(false).should be_true end it "should return false if the value is not false" do @confine.pass?("else").should be_false end it "should produce a message that a value is true" do @confine = Puppet::Provider::Confine::False.new("foo") @confine.message("eh").should be_include("true") end end it "should be able to produce a summary with the number of incorrectly true values" do confine = Puppet::Provider::Confine::False.new %w{one two three four} confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) confine.summary.should == 2 end it "should summarize multiple instances by summing their summaries" do c1 = mock '1', :summary => 1 c2 = mock '2', :summary => 2 c3 = mock '3', :summary => 3 Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6 end end diff --git a/spec/unit/provider/confine/feature_spec.rb b/spec/unit/provider/confine/feature_spec.rb index 534c8c14a..959c7a3bb 100755 --- a/spec/unit/provider/confine/feature_spec.rb +++ b/spec/unit/provider/confine/feature_spec.rb @@ -1,60 +1,59 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine/feature' describe Puppet::Provider::Confine::Feature do it "should be named :feature" do Puppet::Provider::Confine::Feature.name.should == :feature end it "should require a value" do lambda { Puppet::Provider::Confine::Feature.new }.should raise_error(ArgumentError) end it "should always convert values to an array" do Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array) end describe "when testing values" do before do @features = mock 'features' Puppet.stubs(:features).returns @features @confine = Puppet::Provider::Confine::Feature.new("myfeature") @confine.label = "eh" end it "should use the Puppet features instance to test validity" do @features.expects(:myfeature?) @confine.valid? end it "should return true if the feature is present" do @features.expects(:myfeature?).returns true @confine.pass?("myfeature").should be_true end it "should return false if the value is false" do @features.expects(:myfeature?).returns false @confine.pass?("myfeature").should be_false end it "should log that a feature is missing" do @confine.message("myfeat").should be_include("missing") end end it "should summarize multiple instances by returning a flattened array of all missing features" do confines = [] confines << Puppet::Provider::Confine::Feature.new(%w{one two}) confines << Puppet::Provider::Confine::Feature.new(%w{two}) confines << Puppet::Provider::Confine::Feature.new(%w{three four}) features = mock 'feature' features.stub_everything Puppet.stubs(:features).returns features Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort end end diff --git a/spec/unit/provider/confine/true_spec.rb b/spec/unit/provider/confine/true_spec.rb index b23373b87..795819bd3 100755 --- a/spec/unit/provider/confine/true_spec.rb +++ b/spec/unit/provider/confine/true_spec.rb @@ -1,53 +1,52 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine/true' describe Puppet::Provider::Confine::True do it "should be named :true" do Puppet::Provider::Confine::True.name.should == :true end it "should require a value" do lambda { Puppet::Provider::Confine::True.new }.should raise_error(ArgumentError) end describe "when testing values" do before do @confine = Puppet::Provider::Confine::True.new("foo") @confine.label = "eh" end it "should use the 'pass?' method to test validity" do @confine.expects(:pass?).with("foo") @confine.valid? end it "should return true if the value is not false" do @confine.pass?("else").should be_true end it "should return false if the value is false" do @confine.pass?(nil).should be_false end it "should produce the message that a value is false" do @confine.message("eh").should be_include("false") end end it "should produce the number of false values when asked for a summary" do @confine = Puppet::Provider::Confine::True.new %w{one two three four} @confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) @confine.summary.should == 2 end it "should summarize multiple instances by summing their summaries" do c1 = mock '1', :summary => 1 c2 = mock '2', :summary => 2 c3 = mock '3', :summary => 3 Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6 end end diff --git a/spec/unit/provider/confine/variable_spec.rb b/spec/unit/provider/confine/variable_spec.rb index 083573c3a..7b9f53c3d 100755 --- a/spec/unit/provider/confine/variable_spec.rb +++ b/spec/unit/provider/confine/variable_spec.rb @@ -1,107 +1,106 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine/variable' describe Puppet::Provider::Confine::Variable do it "should be named :variable" do Puppet::Provider::Confine::Variable.name.should == :variable end it "should require a value" do lambda { Puppet::Provider::Confine::Variable.new }.should raise_error(ArgumentError) end it "should always convert values to an array" do Puppet::Provider::Confine::Variable.new("/some/file").values.should be_instance_of(Array) end it "should have an accessor for its name" do Puppet::Provider::Confine::Variable.new(:bar).should respond_to(:name) end describe "when testing values" do before do @confine = Puppet::Provider::Confine::Variable.new("foo") @confine.name = :myvar end it "should use settings if the variable name is a valid setting" do Puppet.settings.expects(:valid?).with(:myvar).returns true Puppet.settings.expects(:value).with(:myvar).returns "foo" @confine.valid? end it "should use Facter if the variable name is not a valid setting" do Puppet.settings.expects(:valid?).with(:myvar).returns false Facter.expects(:value).with(:myvar).returns "foo" @confine.valid? end it "should be valid if the value matches the facter value" do @confine.expects(:test_value).returns "foo" @confine.should be_valid end it "should return false if the value does not match the facter value" do @confine.expects(:test_value).returns "fee" @confine.should_not be_valid end it "should be case insensitive" do @confine.expects(:test_value).returns "FOO" @confine.should be_valid end it "should not care whether the value is a string or symbol" do @confine.expects(:test_value).returns "FOO" @confine.should be_valid end it "should produce a message that the fact value is not correct" do @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) @confine.name = "eh" message = @confine.message("value") message.should be_include("facter") message.should be_include("bar,bee") end it "should be valid if the test value matches any of the provided values" do @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) @confine.expects(:test_value).returns "bee" @confine.should be_valid end end describe "when summarizing multiple instances" do it "should return a hash of failing variables and their values" do c1 = Puppet::Provider::Confine::Variable.new("one") c1.name = "uno" c1.expects(:valid?).returns false c2 = Puppet::Provider::Confine::Variable.new("two") c2.name = "dos" c2.expects(:valid?).returns true c3 = Puppet::Provider::Confine::Variable.new("three") c3.name = "tres" c3.expects(:valid?).returns false Puppet::Provider::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}} end it "should combine the values of multiple confines with the same fact" do c1 = Puppet::Provider::Confine::Variable.new("one") c1.name = "uno" c1.expects(:valid?).returns false c2 = Puppet::Provider::Confine::Variable.new("two") c2.name = "uno" c2.expects(:valid?).returns false Puppet::Provider::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}} end end end diff --git a/spec/unit/provider/confine_collection_spec.rb b/spec/unit/provider/confine_collection_spec.rb index fc68ef7f6..f1dbaf35d 100755 --- a/spec/unit/provider/confine_collection_spec.rb +++ b/spec/unit/provider/confine_collection_spec.rb @@ -1,134 +1,133 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine_collection' describe Puppet::Provider::ConfineCollection do it "should be able to add confines" do Puppet::Provider::ConfineCollection.new("label").should respond_to(:confine) end it "should require a label at initialization" do lambda { Puppet::Provider::ConfineCollection.new }.should raise_error(ArgumentError) end it "should make its label available" do Puppet::Provider::ConfineCollection.new("mylabel").label.should == "mylabel" end describe "when creating confine instances" do it "should create an instance of the named test with the provided values" do test_class = mock 'test_class' test_class.expects(:new).with(%w{my values}).returns(stub('confine', :label= => nil)) Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} end it "should copy its label to the confine instance" do confine = mock 'confine' test_class = mock 'test_class' test_class.expects(:new).returns confine Puppet::Provider::Confine.expects(:test).returns test_class confine.expects(:label=).with("label") Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} end describe "and the test cannot be found" do it "should create a Facter test with the provided values and set the name to the test name" do confine = Puppet::Provider::Confine.test(:variable).new(%w{my values}) confine.expects(:name=).with(:foo) confine.class.expects(:new).with(%w{my values}).returns confine Puppet::Provider::ConfineCollection.new("label").confine(:foo => %w{my values}) end end describe "and the 'for_binary' option was provided" do it "should mark the test as a binary confine" do confine = Puppet::Provider::Confine.test(:exists).new(:bar) confine.expects(:for_binary=).with true Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine Puppet::Provider::ConfineCollection.new("label").confine :exists => :bar, :for_binary => true end end end it "should be valid if no confines are present" do Puppet::Provider::ConfineCollection.new("label").should be_valid end it "should be valid if all confines pass" do c1 = stub 'c1', :valid? => true, :label= => nil c2 = stub 'c2', :valid? => true, :label= => nil Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) confiner = Puppet::Provider::ConfineCollection.new("label") confiner.confine :true => :bar, :false => :bee confiner.should be_valid end it "should not be valid if any confines fail" do c1 = stub 'c1', :valid? => true, :label= => nil c2 = stub 'c2', :valid? => false, :label= => nil Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) confiner = Puppet::Provider::ConfineCollection.new("label") confiner.confine :true => :bar, :false => :bee confiner.should_not be_valid end describe "when providing a summary" do before do @confiner = Puppet::Provider::ConfineCollection.new("label") end it "should return a hash" do @confiner.summary.should be_instance_of(Hash) end it "should return an empty hash if the confiner is valid" do @confiner.summary.should == {} end it "should add each test type's summary to the hash" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:true => :tsumm, :false => :fsumm} end it "should not include tests that return 0" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0 Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:false => :fsumm} end it "should not include tests that return empty arrays" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns [] Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:false => :fsumm} end it "should not include tests that return empty hashes" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns({}) Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:false => :fsumm} end end end diff --git a/spec/unit/provider/confine_spec.rb b/spec/unit/provider/confine_spec.rb index ce969eded..ade444276 100755 --- a/spec/unit/provider/confine_spec.rb +++ b/spec/unit/provider/confine_spec.rb @@ -1,78 +1,77 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confine' describe Puppet::Provider::Confine do it "should require a value" do lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) end it "should always convert values to an array" do Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array) end it "should have a 'true' test" do Puppet::Provider::Confine.test(:true).should be_instance_of(Class) end it "should have a 'false' test" do Puppet::Provider::Confine.test(:false).should be_instance_of(Class) end it "should have a 'feature' test" do Puppet::Provider::Confine.test(:feature).should be_instance_of(Class) end it "should have an 'exists' test" do Puppet::Provider::Confine.test(:exists).should be_instance_of(Class) end it "should have a 'variable' test" do Puppet::Provider::Confine.test(:variable).should be_instance_of(Class) end describe "when testing all values" do before do @confine = Puppet::Provider::Confine.new(%w{a b c}) @confine.label = "foo" end it "should be invalid if any values fail" do @confine.stubs(:pass?).returns true @confine.expects(:pass?).with("b").returns false @confine.should_not be_valid end it "should be valid if all values pass" do @confine.stubs(:pass?).returns true @confine.should be_valid end it "should short-cut at the first failing value" do @confine.expects(:pass?).once.returns false @confine.valid? end it "should log failing confines with the label and message" do @confine.stubs(:pass?).returns false @confine.expects(:message).returns "My message" @confine.expects(:label).returns "Mylabel" Puppet.expects(:debug).with("Mylabel: My message") @confine.valid? end end describe "when testing the result of the values" do before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) } it "should return an array with the result of the test for each value" do @confine.stubs(:pass?).returns true @confine.expects(:pass?).with("b").returns false @confine.expects(:pass?).with("d").returns false @confine.result.should == [true, false, true, false] end end end diff --git a/spec/unit/provider/confiner_spec.rb b/spec/unit/provider/confiner_spec.rb index 15e2e6129..23ec162a5 100755 --- a/spec/unit/provider/confiner_spec.rb +++ b/spec/unit/provider/confiner_spec.rb @@ -1,63 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/confiner' describe Puppet::Provider::Confiner do before do @object = Object.new @object.extend(Puppet::Provider::Confiner) end it "should have a method for defining confines" do @object.should respond_to(:confine) end it "should have a method for returning its confine collection" do @object.should respond_to(:confine_collection) end it "should have a method for testing suitability" do @object.should respond_to(:suitable?) end it "should delegate its confine method to its confine collection" do coll = mock 'collection' @object.stubs(:confine_collection).returns coll coll.expects(:confine).with(:foo => :bar, :bee => :baz) @object.confine(:foo => :bar, :bee => :baz) end it "should create a new confine collection if one does not exist" do Puppet::Provider::ConfineCollection.expects(:new).with("mylabel").returns "mycoll" @object.expects(:to_s).returns "mylabel" @object.confine_collection.should == "mycoll" end it "should reuse the confine collection" do @object.confine_collection.should equal(@object.confine_collection) end describe "when testing suitability" do before do @coll = mock 'collection' @object.stubs(:confine_collection).returns @coll end it "should return true if the confine collection is valid" do @coll.expects(:valid?).returns true @object.should be_suitable end it "should return false if the confine collection is invalid" do @coll.expects(:valid?).returns false @object.should_not be_suitable end it "should return the summary of the confine collection if a long result is asked for" do @coll.expects(:summary).returns "myresult" @object.suitable?(false).should == "myresult" end end end diff --git a/spec/unit/provider/exec/posix_spec.rb b/spec/unit/provider/exec/posix_spec.rb index d02099250..50697d826 100755 --- a/spec/unit/provider/exec/posix_spec.rb +++ b/spec/unit/provider/exec/posix_spec.rb @@ -1,120 +1,120 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:exec).provider(:posix) describe provider_class do before :each do @resource = Puppet::Resource.new(:exec, 'foo') @provider = provider_class.new(@resource) end ["posix", "microsoft_windows"].each do |feature| describe "when in #{feature} environment" do before :each do if feature == "microsoft_windows" Puppet.features.stubs(:microsoft_windows?).returns(true) Puppet.features.stubs(:posix?).returns(false) else Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end end describe "#validatecmd" do it "should fail if no path is specified and the command is not fully qualified" do lambda { @provider.validatecmd("foo") }.should raise_error( Puppet::Error, "'foo' is not qualified and no path was specified. Please qualify the command or specify a path." ) end it "should pass if a path is given" do @provider.resource[:path] = ['/bogus/bin'] @provider.validatecmd("../foo") end it "should pass if command is fully qualifed" do @provider.resource[:path] = ['/bogus/bin'] @provider.validatecmd("/bin/blah/foo") end end describe "#run" do it "should fail if no path is specified and command does not exist" do lambda { @provider.run("foo") }.should raise_error(ArgumentError, "Could not find command 'foo'") end it "should fail if the command isn't in the path" do @provider.resource[:path] = ['/bogus/bin'] lambda { @provider.run("foo") }.should raise_error(ArgumentError, "Could not find command 'foo'") end it "should fail if the command isn't executable" do @provider.resource[:path] = ['/bogus/bin'] File.stubs(:exists?).with("foo").returns(true) lambda { @provider.run("foo") }.should raise_error(ArgumentError, "'foo' is not executable") end it "should not be able to execute shell builtins" do @provider.resource[:path] = ['/bin'] lambda { @provider.run("cd ..") }.should raise_error(ArgumentError, "Could not find command 'cd'") end it "should execute the command if the command given includes arguments or subcommands" do @provider.resource[:path] = ['/bogus/bin'] File.stubs(:exists?).returns(false) File.stubs(:exists?).with("foo").returns(true) File.stubs(:executable?).with("foo").returns(true) Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo bar --sillyarg=true --blah']) && (arguments.is_a? Hash) } @provider.run("foo bar --sillyarg=true --blah") end it "should fail if quoted command doesn't exist" do @provider.resource[:path] = ['/bogus/bin'] File.stubs(:exists?).returns(false) File.stubs(:exists?).with("foo").returns(true) File.stubs(:executable?).with("foo").returns(true) lambda { @provider.run('"foo bar --sillyarg=true --blah"') }.should raise_error(ArgumentError, "Could not find command 'foo bar --sillyarg=true --blah'") end it "should execute the command if it finds it in the path and is executable" do @provider.resource[:path] = ['/bogus/bin'] File.stubs(:exists?).with("foo").returns(true) File.stubs(:executable?).with("foo").returns(true) Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo']) && (arguments.is_a? Hash) } @provider.run("foo") end if feature == "microsoft_windows" [".exe", ".ps1", ".bat", ".com", ""].each do |extension| it "should check file extension #{extension} when it can't find the executable" do @provider.resource[:path] = ['/bogus/bin'] File.stubs(:exists?).returns(false) File.stubs(:exists?).with("/bogus/bin/foo#{extension}").returns(true) File.stubs(:executable?).with("foo").returns(true) Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo']) && (arguments.is_a? Hash) } @provider.run("foo") end end end it "should warn if you're overriding something in environment" do @provider.resource[:environment] = ['WHATEVER=/something/else', 'WHATEVER=/foo'] File.stubs(:exists?).returns(false) File.stubs(:exists?).with("foo").returns(true) File.stubs(:executable?).with("foo").returns(true) Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo']) && (arguments.is_a? Hash) } @provider.run("foo") @logs.map {|l| "#{l.level}: #{l.message}" }.should == ["warning: Overriding environment setting 'WHATEVER' with '/foo'"] end end end end end diff --git a/spec/unit/provider/exec/shell_spec.rb b/spec/unit/provider/exec/shell_spec.rb old mode 100644 new mode 100755 index 4bae354c9..90047b9d6 --- a/spec/unit/provider/exec/shell_spec.rb +++ b/spec/unit/provider/exec/shell_spec.rb @@ -1,50 +1,50 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:exec).provider(:shell) describe provider_class do before :each do @resource = Puppet::Resource.new(:exec, 'foo') @provider = provider_class.new(@resource) end describe "#run" do it "should be able to run builtin shell commands" do output, status = @provider.run("if [ 1 = 1 ]; then echo 'blah'; fi") status.exitstatus.should == 0 output.should == "blah\n" end it "should be able to run commands with single quotes in them" do output, status = @provider.run("echo 'foo bar'") status.exitstatus.should == 0 output.should == "foo bar\n" end it "should be able to run commands with double quotes in them" do output, status = @provider.run('echo "foo bar"') status.exitstatus.should == 0 output.should == "foo bar\n" end it "should be able to run multiple commands separated by a semicolon" do output, status = @provider.run("echo 'foo' ; echo 'bar'") status.exitstatus.should == 0 output.should == "foo\nbar\n" end it "should be able to read values from the environment parameter" do @resource[:environment] = "FOO=bar" output, status = @provider.run("echo $FOO") status.exitstatus.should == 0 output.should == "bar\n" end end describe "#validatecmd" do it "should always return true because builtins don't need path or to be fully qualified" do @provider.validatecmd('whateverdoesntmatter').should == true end end end diff --git a/spec/unit/provider/group/groupadd_spec.rb b/spec/unit/provider/group/groupadd_spec.rb index 41431fb59..585eb02af 100755 --- a/spec/unit/provider/group/groupadd_spec.rb +++ b/spec/unit/provider/group/groupadd_spec.rb @@ -1,31 +1,40 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:group).provider(:groupadd) describe provider_class do before do @resource = stub("resource", :name => "mygroup") @provider = provider_class.new(@resource) end - # #1360 it "should add -o when allowdupe is enabled and the group is being created" do @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" + @resource.stubs(:system?).returns true @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.create end it "should add -o when allowdupe is enabled and the gid is being modified" do @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.gid = 150 end + + it "should add -r when system is enabled and the group is being created" do + @resource.stubs(:should).returns "fakeval" + @resource.stubs(:[]).returns "fakeval" + @resource.expects(:system?).returns true + @resource.stubs(:allowdupe?).returns true + @provider.expects(:execute).with { |args| args.include?("-r") } + + @provider.create + end end diff --git a/spec/unit/provider/group/ldap_spec.rb b/spec/unit/provider/group/ldap_spec.rb index 8454b9604..947007f10 100755 --- a/spec/unit/provider/group/ldap_spec.rb +++ b/spec/unit/provider/group/ldap_spec.rb @@ -1,105 +1,105 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-10. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:group).provider(:ldap) describe provider_class do it "should have the Ldap provider class as its baseclass" do provider_class.superclass.should equal(Puppet::Provider::Ldap) end it "should manage :posixGroup objectclass" do provider_class.manager.objectclasses.should == [:posixGroup] end it "should use 'ou=Groups' as its relative base" do provider_class.manager.location.should == "ou=Groups" end it "should use :cn as its rdn" do provider_class.manager.rdn.should == :cn end it "should map :name to 'cn'" do provider_class.manager.ldap_name(:name).should == 'cn' end it "should map :gid to 'gidNumber'" do provider_class.manager.ldap_name(:gid).should == 'gidNumber' end it "should map :members to 'memberUid', to be used by the user ldap provider" do provider_class.manager.ldap_name(:members).should == 'memberUid' end describe "when being created" do before do # So we don't try to actually talk to ldap @connection = mock 'connection' provider_class.manager.stubs(:connect).yields @connection end describe "with no gid specified" do it "should pick the first available GID after the largest existing GID" do low = {:name=>["luke"], :gid=>["600"]} high = {:name=>["testing"], :gid=>["640"]} provider_class.manager.expects(:search).returns([low, high]) resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:gid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["641"] } instance.create instance.flush end it "should pick '501' as its GID if no groups are found" do provider_class.manager.expects(:search).returns nil resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:gid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["501"] } instance.create instance.flush end end end it "should have a method for converting group names to GIDs" do provider_class.should respond_to(:name2id) end describe "when converting from a group name to GID" do it "should use the ldap manager to look up the GID" do provider_class.manager.expects(:search).with("cn=foo") provider_class.name2id("foo") end it "should return nil if no group is found" do provider_class.manager.expects(:search).with("cn=foo").returns nil provider_class.name2id("foo").should be_nil provider_class.manager.expects(:search).with("cn=bar").returns [] provider_class.name2id("bar").should be_nil end # We shouldn't ever actually have more than one gid, but it doesn't hurt # to test for the possibility. it "should return the first gid from the first returned group" do provider_class.manager.expects(:search).with("cn=foo").returns [{:name => "foo", :gid => [10, 11]}, {:name => :bar, :gid => [20, 21]}] provider_class.name2id("foo").should == 10 end end end diff --git a/spec/unit/provider/host/parsed_spec.rb b/spec/unit/provider/host/parsed_spec.rb index 048d77ba2..9cb5890cc 100755 --- a/spec/unit/provider/host/parsed_spec.rb +++ b/spec/unit/provider/host/parsed_spec.rb @@ -1,198 +1,197 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' require 'puppet_spec/files' provider_class = Puppet::Type.type(:host).provider(:parsed) describe provider_class do include PuppetSpec::Files before do @host_class = Puppet::Type.type(:host) @provider = @host_class.provider(:parsed) @hostfile = tmpfile('hosts') @provider.any_instance.stubs(:target).returns @hostfile end after :each do @provider.initvars end def mkhost(args) hostresource = Puppet::Type::Host.new(:name => args[:name]) hostresource.stubs(:should).with(:target).returns @hostfile # Using setters of provider to build our testobject # Note: We already proved, that in case of host_aliases # the provider setter "host_aliases=(value)" will be # called with the joined array, so we just simulate that host = @provider.new(hostresource) args.each do |property,value| value = value.join(" ") if property == :host_aliases and value.is_a?(Array) host.send("#{property}=", value) end host end def genhost(host) @provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields host.flush @provider.target_object(@hostfile).read end describe "when parsing a line with ip and hostname" do it "should parse an ipv4 from the first field" do @provider.parse_line("127.0.0.1 localhost")[:ip].should == "127.0.0.1" end it "should parse an ipv6 from the first field" do @provider.parse_line("::1 localhost")[:ip].should == "::1" end it "should parse the name from the second field" do @provider.parse_line("::1 localhost")[:name].should == "localhost" end it "should set an empty comment" do @provider.parse_line("::1 localhost")[:comment].should == "" end it "should set host_aliases to :absent" do @provider.parse_line("::1 localhost")[:host_aliases].should == :absent end end describe "when parsing a line with ip, hostname and comment" do before do @testline = "127.0.0.1 localhost # A comment with a #-char" end it "should parse the ip from the first field" do @provider.parse_line(@testline)[:ip].should == "127.0.0.1" end it "should parse the hostname from the second field" do @provider.parse_line(@testline)[:name].should == "localhost" end it "should parse the comment after the first '#' character" do @provider.parse_line(@testline)[:comment].should == 'A comment with a #-char' end end describe "when parsing a line with ip, hostname and aliases" do it "should parse alias from the third field" do @provider.parse_line("127.0.0.1 localhost localhost.localdomain")[:host_aliases].should == "localhost.localdomain" end it "should parse multiple aliases" do @provider.parse_line("127.0.0.1 host alias1 alias2")[:host_aliases].should == 'alias1 alias2' @provider.parse_line("127.0.0.1 host alias1\talias2")[:host_aliases].should == 'alias1 alias2' @provider.parse_line("127.0.0.1 host alias1\talias2 alias3")[:host_aliases].should == 'alias1 alias2 alias3' end end describe "when parsing a line with ip, hostname, aliases and comment" do before do # Just playing with a few different delimiters @testline = "127.0.0.1\t host alias1\talias2 alias3 # A comment with a #-char" end it "should parse the ip from the first field" do @provider.parse_line(@testline)[:ip].should == "127.0.0.1" end it "should parse the hostname from the second field" do @provider.parse_line(@testline)[:name].should == "host" end it "should parse all host_aliases from the third field" do @provider.parse_line(@testline)[:host_aliases].should == 'alias1 alias2 alias3' end it "should parse the comment after the first '#' character" do @provider.parse_line(@testline)[:comment].should == 'A comment with a #-char' end end describe "when operating on /etc/hosts like files" do it_should_behave_like "all parsedfile providers", provider_class, my_fixtures('valid*') it "should be able to generate a simple hostfile entry" do host = mkhost( :name => 'localhost', :ip => '127.0.0.1', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost\n" end it "should be able to generate an entry with one alias" do host = mkhost( :name => 'localhost.localdomain', :ip => '127.0.0.1', :host_aliases => 'localhost', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost.localdomain\tlocalhost\n" end it "should be able to generate an entry with more than one alias" do host = mkhost( :name => 'host', :ip => '192.0.0.1', :host_aliases => [ 'a1','a2','a3','a4' ], :ensure => :present ) genhost(host).should == "192.0.0.1\thost\ta1 a2 a3 a4\n" end it "should be able to generate a simple hostfile entry with comments" do host = mkhost( :name => 'localhost', :ip => '127.0.0.1', :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost\t# Bazinga!\n" end it "should be able to generate an entry with one alias and a comment" do host = mkhost( :name => 'localhost.localdomain', :ip => '127.0.0.1', :host_aliases => 'localhost', :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost.localdomain\tlocalhost\t# Bazinga!\n" end it "should be able to generate an entry with more than one alias and a comment" do host = mkhost( :name => 'host', :ip => '192.0.0.1', :host_aliases => [ 'a1','a2','a3','a4' ], :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "192.0.0.1\thost\ta1 a2 a3 a4\t# Bazinga!\n" end end end diff --git a/spec/unit/provider/interface/cisco_spec.rb b/spec/unit/provider/interface/cisco_spec.rb new file mode 100755 index 000000000..d1f70609f --- /dev/null +++ b/spec/unit/provider/interface/cisco_spec.rb @@ -0,0 +1,64 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/interface/cisco' + +provider_class = Puppet::Type.type(:interface).provider(:cisco) + +describe provider_class do + before do + @resource = stub("resource", :name => "Fa0/1") + @provider = provider_class.new(@resource) + end + + it "should have a parent of Puppet::Provider::NetworkDevice" do + provider_class.should < Puppet::Provider::NetworkDevice + end + + it "should have an instances method" do + provider_class.should respond_to(:instances) + end + + describe "when looking up instances at prefetch" do + before do + @device = stub_everything 'device' + Puppet::Util::NetworkDevice::Cisco::Device.stubs(:new).returns(@device) + @device.stubs(:command).yields(@device) + end + + it "should initialize the network device with the given url" do + Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).with(:url).returns(@device) + provider_class.lookup(:url, "Fa0/1") + end + + it "should delegate to the device interface fetcher" do + @device.expects(:interface) + provider_class.lookup("", "Fa0/1") + end + + it "should return the given interface data" do + @device.expects(:interface).returns({ :description => "thisone", :mode => :access}) + provider_class.lookup("", "Fa0").should == {:description => "thisone", :mode => :access } + end + + end + + describe "when an instance is being flushed" do + it "should call the device interface update method with current and past properties" do + @instance = provider_class.new(:ensure => :present, :name => "Fa0/1", :description => "myinterface") + @instance.description = "newdesc" + @instance.resource = @resource + @resource.stubs(:[]).with(:name).returns("Fa0/1") + device = stub_everything 'device' + @instance.stubs(:device).returns(device) + device.expects(:command).yields(device) + interface = stub 'interface' + device.expects(:new_interface).with("Fa0/1").returns(interface) + interface.expects(:update).with( {:ensure => :present, :name => "Fa0/1", :description => "myinterface"}, + {:ensure => :present, :name => "Fa0/1", :description => "newdesc"}) + + @instance.flush + end + end +end diff --git a/spec/unit/provider/ldap_spec.rb b/spec/unit/provider/ldap_spec.rb index 7ed289c62..012a22b99 100755 --- a/spec/unit/provider/ldap_spec.rb +++ b/spec/unit/provider/ldap_spec.rb @@ -1,248 +1,248 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-21. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/provider/ldap' describe Puppet::Provider::Ldap do before do @class = Class.new(Puppet::Provider::Ldap) end it "should be able to define its manager" do manager = mock 'manager' Puppet::Util::Ldap::Manager.expects(:new).returns manager @class.stubs :mk_resource_methods manager.expects(:manages).with(:one) @class.manages(:one).should equal(manager) @class.manager.should equal(manager) end it "should be able to prefetch instances from ldap" do @class.should respond_to(:prefetch) end it "should create its resource getter/setter methods when the manager is defined" do manager = mock 'manager' Puppet::Util::Ldap::Manager.expects(:new).returns manager @class.expects :mk_resource_methods manager.stubs(:manages) @class.manages(:one).should equal(manager) end it "should have an instances method" do @class.should respond_to(:instances) end describe "when providing a list of instances" do it "should convert all results returned from the manager's :search method into provider instances" do manager = mock 'manager' @class.stubs(:manager).returns manager manager.expects(:search).returns %w{one two three} @class.expects(:new).with("one").returns(1) @class.expects(:new).with("two").returns(2) @class.expects(:new).with("three").returns(3) @class.instances.should == [1,2,3] end end it "should have a prefetch method" do @class.should respond_to(:prefetch) end describe "when prefetching" do before do @manager = mock 'manager' @class.stubs(:manager).returns @manager @resource = mock 'resource' @resources = {"one" => @resource} end it "should find an entry for each passed resource" do @manager.expects(:find).with("one").returns nil @class.stubs(:new) @resource.stubs(:provider=) @class.prefetch(@resources) end describe "resources that do not exist" do it "should create a provider with :ensure => :absent" do result = mock 'result' @manager.expects(:find).with("one").returns nil @class.expects(:new).with(:ensure => :absent).returns "myprovider" @resource.expects(:provider=).with("myprovider") @class.prefetch(@resources) end end describe "resources that exist" do it "should create a provider with the results of the find" do @manager.expects(:find).with("one").returns("one" => "two") @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" @resource.expects(:provider=).with("myprovider") @class.prefetch(@resources) end it "should set :ensure to :present in the returned values" do @manager.expects(:find).with("one").returns("one" => "two") @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" @resource.expects(:provider=).with("myprovider") @class.prefetch(@resources) end end end describe "when being initialized" do it "should fail if no manager has been defined" do lambda { @class.new }.should raise_error(Puppet::DevError) end it "should fail if the manager is invalid" do manager = stub "manager", :valid? => false @class.stubs(:manager).returns manager lambda { @class.new }.should raise_error(Puppet::DevError) end describe "with a hash" do before do @manager = stub "manager", :valid? => true @class.stubs(:manager).returns @manager @resource_class = mock 'resource_class' @class.stubs(:resource_type).returns @resource_class @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class.stubs(:attrclass).with(:one).returns(@property_class) @resource_class.stubs(:valid_parameter?).returns true end it "should store a copy of the hash as its ldap_properties" do instance = @class.new(:one => :two) instance.ldap_properties.should == {:one => :two} end it "should only store the first value of each value array for those attributes that do not match all values" do @property_class.expects(:array_matching).returns :first instance = @class.new(:one => %w{two three}) instance.properties.should == {:one => "two"} end it "should store the whole value array for those attributes that match all values" do @property_class.expects(:array_matching).returns :all instance = @class.new(:one => %w{two three}) instance.properties.should == {:one => %w{two three}} end it "should only use the first value for attributes that are not properties" do # Yay. hackish, but easier than mocking everything. @resource_class.expects(:attrclass).with(:a).returns Puppet::Type.type(:user).attrclass(:name) @property_class.stubs(:array_matching).returns :all instance = @class.new(:one => %w{two three}, :a => %w{b c}) instance.properties.should == {:one => %w{two three}, :a => "b"} end it "should discard any properties not valid in the resource class" do @resource_class.expects(:valid_parameter?).with(:a).returns false @property_class.stubs(:array_matching).returns :all instance = @class.new(:one => %w{two three}, :a => %w{b}) instance.properties.should == {:one => %w{two three}} end end end describe "when an instance" do before do @manager = stub "manager", :valid? => true @class.stubs(:manager).returns @manager @instance = @class.new @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:one, :two] @class.stubs(:resource_type).returns @resource_class end it "should have a method for creating the ldap entry" do @instance.should respond_to(:create) end it "should have a method for removing the ldap entry" do @instance.should respond_to(:delete) end it "should have a method for returning the class's manager" do @instance.manager.should equal(@manager) end it "should indicate when the ldap entry already exists" do @instance = @class.new(:ensure => :present) @instance.exists?.should be_true end it "should indicate when the ldap entry does not exist" do @instance = @class.new(:ensure => :absent) @instance.exists?.should be_false end describe "is being flushed" do it "should call the manager's :update method with its name, current attributes, and desired attributes" do @instance.stubs(:name).returns "myname" @instance.stubs(:ldap_properties).returns(:one => :two) @instance.stubs(:properties).returns(:three => :four) @manager.expects(:update).with(@instance.name, {:one => :two}, {:three => :four}) @instance.flush end end describe "is being created" do before do @rclass = mock 'resource_class' @rclass.stubs(:validproperties).returns([:one, :two]) @resource = mock 'resource' @resource.stubs(:class).returns @rclass @resource.stubs(:should).returns nil @instance.stubs(:resource).returns @resource end it "should set its :ensure value to :present" do @instance.create @instance.properties[:ensure].should == :present end it "should set all of the other attributes from the resource" do @resource.expects(:should).with(:one).returns "oneval" @resource.expects(:should).with(:two).returns "twoval" @instance.create @instance.properties[:one].should == "oneval" @instance.properties[:two].should == "twoval" end end describe "is being deleted" do it "should set its :ensure value to :absent" do @instance.delete @instance.properties[:ensure].should == :absent end end end end diff --git a/spec/unit/provider/macauthorization_spec.rb b/spec/unit/provider/macauthorization_spec.rb index 3c74334ba..a76f917f7 100755 --- a/spec/unit/provider/macauthorization_spec.rb +++ b/spec/unit/provider/macauthorization_spec.rb @@ -1,147 +1,147 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the macauthorization provider # -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet' require 'facter/util/plist' provider_class = Puppet::Type.type(:macauthorization).provider(:macauthorization) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' @authname = "foo.spam.eggs.puppettest" @authplist = {} @rules = {@authname => @authplist} authdb = {} authdb["rules"] = { "foorule" => "foo" } authdb["rights"] = { "fooright" => "foo" } # Stub out Plist::parse_xml Plist.stubs(:parse_xml).returns(authdb) # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure @resource.stubs(:[]).with(:name).returns @authname @resource.stubs(:[]).with(:ensure).returns :present @resource.stubs(:ref).returns "MacAuthorization[#{@authname}]" @provider = provider_class.new(@resource) end it "should have a create method" do @provider.should respond_to(:create) end it "should have a destroy method" do @provider.should respond_to(:destroy) end it "should have an exists? method" do @provider.should respond_to(:exists?) end it "should have a flush method" do @provider.should respond_to(:flush) end properties = [ :allow_root, :authenticate_user, :auth_class, :comment, :group, :k_of_n, :mechanisms, :rule, :session_owner, :shared, :timeout, :tries, :auth_type ] properties.each do |prop| it "should have a #{prop.to_s} method" do @provider.should respond_to(prop.to_s) end it "should have a #{prop.to_s}= method" do @provider.should respond_to(prop.to_s + "=") end end describe "when destroying a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method destroy_right" do @provider.expects(:destroy_right) @provider.destroy end it "should call the external command 'security authorizationdb remove @authname" do @provider.expects(:security).with("authorizationdb", :remove, @authname) @provider.destroy end end describe "when destroying a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method destroy_rule" do @provider.expects(:destroy_rule) @provider.destroy end end describe "when flushing a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method flush_right" do @provider.expects(:flush_right) @provider.flush end it "should call the internal method set_right" do @provider.expects(:set_right) @provider.flush end it "should read and write to the auth database with the right arguments" do @provider.expects(:execute).with { |cmds, args| cmds.include?("read") and cmds.include?(@authname) and args[:combine] == false }.once @provider.expects(:execute).with { |cmds, args| cmds.include?("write") and cmds.include?(@authname) and args[:combine] == false and args[:stdinfile] != nil }.once @provider.flush end end describe "when flushing a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method flush_rule" do @provider.expects(:flush_rule) @provider.flush end it "should call the internal method set_rule" do @provider.expects(:set_rule) @provider.flush end end end diff --git a/spec/unit/provider/mcx/mcxcontent_spec.rb b/spec/unit/provider/mcx/mcxcontent_spec.rb index 82dbf9ac7..4676575be 100755 --- a/spec/unit/provider/mcx/mcxcontent_spec.rb +++ b/spec/unit/provider/mcx/mcxcontent_spec.rb @@ -1,175 +1,156 @@ -#! /usr/bin/env ruby -#-- -# Copyright (C) 2008 Jeffrey J McCune. - -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Author: Jeff McCune - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:mcx).provider(:mcxcontent) # describe creates a new ExampleGroup object. describe provider_class do # :each executes before each test. # :all executes once for the test group and before :each. before :each do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @attached_to = "/Users/foobar" @ds_path = "/Local/Default/Users/foobar" # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure and enable @resource.stubs(:[]).with(:name).returns @attached_to @resource.stubs(:[]).with(:ensure).returns :present @resource.stubs(:ref).returns "Mcx[#{@attached_to}]" # stub out the provider methods that actually touch the filesystem # or execute commands @provider.class.stubs(:execute).returns('') @provider.stubs(:execute).returns('') @provider.stubs(:resource).returns @resource end it "should have a create method." do @provider.should respond_to(:create) end it "should have a destroy method." do @provider.should respond_to(:destroy) end it "should have an exists? method." do @provider.should respond_to(:exists?) end it "should have an content method." do @provider.should respond_to(:content) end it "should have an content= method." do @provider.should respond_to(:content=) end describe "when managing the resource" do it "should execute external command dscl from :create" do @provider.class.expects(:dscl).returns('').once @provider.create end it "should execute external command dscl from :destroy" do @provider.class.expects(:dscl).with('localhost', '-mcxdelete', @ds_path).returns('').once @provider.destroy end it "should execute external command dscl from :exists?" do @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('').once @provider.exists? end it "should execute external command dscl from :content" do @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('') @provider.content end it "should execute external command dscl from :content=" do @provider.class.expects(:dscl).returns('') @provider.content='' end end describe "when creating and parsing the name for ds_type" do before :each do @resource.stubs(:[]).with(:name).returns "/Foo/bar" end it "should not accept /Foo/bar" do lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should accept /Foo/bar with ds_type => user" do @resource.stubs(:[]).with(:ds_type).returns "user" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept /Foo/bar with ds_type => group" do @resource.stubs(:[]).with(:ds_type).returns "group" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept /Foo/bar with ds_type => computer" do @resource.stubs(:[]).with(:ds_type).returns "computer" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :name => /Foo/bar with ds_type => computerlist" do @resource.stubs(:[]).with(:ds_type).returns "computerlist" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end end describe "when creating and :name => foobar" do before :each do @resource.stubs(:[]).with(:name).returns "foobar" end it "should not accept unspecified :ds_type and :ds_name" do lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should not accept unspecified :ds_type" do @resource.stubs(:[]).with(:ds_type).returns "user" lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should not accept unspecified :ds_name" do @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should accept :ds_type => user, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "user" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :ds_type => group, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "group" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :ds_type => computer, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "computer" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :ds_type => computerlist, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "computerlist" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should not accept :ds_type => bogustype, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "bogustype" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should raise_error(MCXContentProviderException) end end describe "when gathering existing instances" do it "should define an instances class method." do @provider.class.should respond_to(:instances) end it "should call external command dscl -list /Local/Default/ on each known ds_type" do @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Users").returns('') @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Groups").returns('') @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Computers").returns('') @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/ComputerLists").returns('') @provider.class.instances end end end diff --git a/spec/unit/provider/mount/parsed_spec.rb b/spec/unit/provider/mount/parsed_spec.rb index b63c0751f..0293e0758 100755 --- a/spec/unit/provider/mount/parsed_spec.rb +++ b/spec/unit/provider/mount/parsed_spec.rb @@ -1,252 +1,252 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-12. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' provider_class = Puppet::Type.type(:mount).provider(:parsed) describe provider_class do before :each do @mount_class = Puppet::Type.type(:mount) @provider = @mount_class.provider(:parsed) end # LAK:FIXME I can't mock Facter because this test happens at parse-time. it "should default to /etc/vfstab on Solaris" do pending "This test only works on Solaris" unless Facter.value(:operatingsystem) == 'Solaris' Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/vfstab' end it "should default to /etc/fstab on anything else" do pending "This test does not work on Solaris" if Facter.value(:operatingsystem) == 'Solaris' Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/fstab' end describe "when parsing a line" do it "should not crash on incomplete lines in fstab" do parse = @provider.parse <<-FSTAB /dev/incomplete /dev/device name FSTAB lambda{ @provider.to_line(parse[0]) }.should_not raise_error end # it_should_behave_like "all parsedfile providers", # provider_class, my_fixtures('*.fstab') describe "on Solaris", :if => Facter.value(:operatingsystem) == 'Solaris' do before :each do @example_line = "/dev/dsk/c0d0s0 /dev/rdsk/c0d0s0 \t\t / \t ufs 1 no\t-" end it "should extract device from the first field" do @provider.parse_line(@example_line)[:device].should == '/dev/dsk/c0d0s0' end it "should extract blockdevice from second field" do @provider.parse_line(@example_line)[:blockdevice].should == "/dev/rdsk/c0d0s0" end it "should extract name from third field" do @provider.parse_line(@example_line)[:name].should == "/" end it "should extract fstype from fourth field" do @provider.parse_line(@example_line)[:fstype].should == "ufs" end it "should extract pass from fifth field" do @provider.parse_line(@example_line)[:pass].should == "1" end it "should extract atboot from sixth field" do @provider.parse_line(@example_line)[:atboot].should == "no" end it "should extract options from seventh field" do @provider.parse_line(@example_line)[:options].should == "-" end end describe "on other platforms than Solaris", :if => Facter.value(:operatingsystem) != 'Solaris' do before :each do @example_line = "/dev/vg00/lv01\t/spare \t \t ext3 defaults\t1 2" end it "should extract device from the first field" do @provider.parse_line(@example_line)[:device].should == '/dev/vg00/lv01' end it "should extract name from second field" do @provider.parse_line(@example_line)[:name].should == "/spare" end it "should extract fstype from third field" do @provider.parse_line(@example_line)[:fstype].should == "ext3" end it "should extract options from fourth field" do @provider.parse_line(@example_line)[:options].should == "defaults" end it "should extract dump from fifth field" do @provider.parse_line(@example_line)[:dump].should == "1" end it "should extract options from sixth field" do @provider.parse_line(@example_line)[:pass].should == "2" end end end describe "mountinstances" do it "should get name from mountoutput found on Solaris" do Facter.stubs(:value).with(:operatingsystem).returns 'Solaris' @provider.stubs(:mountcmd).returns(File.read(my_fixture('solaris.mount'))) mounts = @provider.mountinstances mounts.size.should == 6 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/proc', :mounted => :yes } mounts[2].should == { :name => '/etc/mnttab', :mounted => :yes } mounts[3].should == { :name => '/tmp', :mounted => :yes } mounts[4].should == { :name => '/export/home', :mounted => :yes } mounts[5].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on HP-UX" do Facter.stubs(:value).with(:operatingsystem).returns 'HP-UX' @provider.stubs(:mountcmd).returns(File.read(my_fixture('hpux.mount'))) mounts = @provider.mountinstances mounts.size.should == 17 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/devices', :mounted => :yes } mounts[2].should == { :name => '/dev', :mounted => :yes } mounts[3].should == { :name => '/system/contract', :mounted => :yes } mounts[4].should == { :name => '/proc', :mounted => :yes } mounts[5].should == { :name => '/etc/mnttab', :mounted => :yes } mounts[6].should == { :name => '/etc/svc/volatile', :mounted => :yes } mounts[7].should == { :name => '/system/object', :mounted => :yes } mounts[8].should == { :name => '/etc/dfs/sharetab', :mounted => :yes } mounts[9].should == { :name => '/lib/libc.so.1', :mounted => :yes } mounts[10].should == { :name => '/dev/fd', :mounted => :yes } mounts[11].should == { :name => '/tmp', :mounted => :yes } mounts[12].should == { :name => '/var/run', :mounted => :yes } mounts[13].should == { :name => '/export', :mounted => :yes } mounts[14].should == { :name => '/export/home', :mounted => :yes } mounts[15].should == { :name => '/rpool', :mounted => :yes } mounts[16].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on Darwin" do Facter.stubs(:value).with(:operatingsystem).returns 'Darwin' @provider.stubs(:mountcmd).returns(File.read(my_fixture('darwin.mount'))) mounts = @provider.mountinstances mounts.size.should == 6 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/dev', :mounted => :yes } mounts[2].should == { :name => '/net', :mounted => :yes } mounts[3].should == { :name => '/home', :mounted => :yes } mounts[4].should == { :name => '/usr', :mounted => :yes } mounts[5].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on Linux" do Facter.stubs(:value).with(:operatingsystem).returns 'Gentoo' @provider.stubs(:mountcmd).returns(File.read(my_fixture('linux.mount'))) mounts = @provider.mountinstances mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/lib64/rc/init.d', :mounted => :yes } mounts[2].should == { :name => '/sys', :mounted => :yes } mounts[3].should == { :name => '/usr/portage', :mounted => :yes } mounts[4].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on AIX" do Facter.stubs(:value).with(:operatingsystem).returns 'AIX' @provider.stubs(:mountcmd).returns(File.read(my_fixture('aix.mount'))) mounts = @provider.mountinstances mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/tmp', :mounted => :yes } mounts[2].should == { :name => '/home', :mounted => :yes } mounts[3].should == { :name => '/usr', :mounted => :yes } mounts[4].should == { :name => '/usr/code', :mounted => :yes } end it "should raise an error if a line is not understandable" do @provider.stubs(:mountcmd).returns("bazinga!") lambda { @provider.mountinstances }.should raise_error Puppet::Error end end it "should support AIX's paragraph based /etc/filesystems" my_fixtures('*.fstab').each do |fstab| platform = File.basename(fstab, '.fstab') describe "when prefetching on #{platform}" do before :each do if Facter[:operatingsystem] == "Solaris" then platform == 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" else platform != 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" end # Stub the mount output to our fixture. begin mount = my_fixture(platform + '.mount') @provider.stubs(:mountcmd).returns File.read(mount) rescue pending "is #{platform}.mount missing at this point?" end # Note: we have to stub default_target before creating resources # because it is used by Puppet::Type::Mount.new to populate the # :target property. @provider.stubs(:default_target).returns fstab @res_ghost = Puppet::Type::Mount.new(:name => '/ghost') # in no fake fstab @res_mounted = Puppet::Type::Mount.new(:name => '/') # in every fake fstab @res_unmounted = Puppet::Type::Mount.new(:name => '/boot') # in every fake fstab @res_absent = Puppet::Type::Mount.new(:name => '/absent') # in no fake fstab # Simulate transaction.rb:prefetch @resource_hash = {} [@res_ghost, @res_mounted, @res_unmounted, @res_absent].each do |resource| @resource_hash[resource.name] = resource end end it "should set :ensure to :unmounted if found in fstab but not mounted" do @provider.prefetch(@resource_hash) @res_unmounted.provider.get(:ensure).should == :unmounted end it "should set :ensure to :ghost if not found in fstab but mounted" do @provider.prefetch(@resource_hash) @res_ghost.provider.get(:ensure).should == :ghost end it "should set :ensure to :mounted if found in fstab and mounted" do @provider.prefetch(@resource_hash) @res_mounted.provider.get(:ensure).should == :mounted end it "should set :ensure to :absent if not found in fstab and not mounted" do @provider.prefetch(@resource_hash) @res_absent.provider.get(:ensure).should == :absent end end end end diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index 9cadfc403..963bfba7c 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -1,146 +1,145 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/mount' describe Puppet::Provider::Mount do before :each do @mounter = Object.new @mounter.extend(Puppet::Provider::Mount) @name = "/" @resource = stub 'resource' @resource.stubs(:[]).with(:name).returns(@name) @mounter.stubs(:resource).returns(@resource) end describe Puppet::Provider::Mount, " when mounting" do before :each do @mounter.stubs(:get).with(:ensure).returns(:mounted) end it "should use the 'mountcmd' method to mount" do @mounter.stubs(:options).returns(nil) @mounter.expects(:mountcmd) @mounter.mount end it "should add the options following '-o' if they exist and are not set to :absent" do @mounter.stubs(:options).returns("ro") @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } @mounter.mount end it "should specify the filesystem name to the mount command" do @mounter.stubs(:options).returns(nil) @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } @mounter.mount end it "should update the :ensure state to :mounted if it was :unmounted before" do @mounter.expects(:mountcmd) @mounter.stubs(:options).returns(nil) @mounter.expects(:get).with(:ensure).returns(:unmounted) @mounter.expects(:set).with(:ensure => :mounted) @mounter.mount end it "should update the :ensure state to :ghost if it was :absent before" do @mounter.expects(:mountcmd) @mounter.stubs(:options).returns(nil) @mounter.expects(:get).with(:ensure).returns(:absent) @mounter.expects(:set).with(:ensure => :ghost) @mounter.mount end end describe Puppet::Provider::Mount, " when remounting" do it "should use '-o remount' if the resource specifies it supports remounting" do @mounter.stubs(:info) @resource.stubs(:[]).with(:remounts).returns(:true) @mounter.expects(:mountcmd).with("-o", "remount", @name) @mounter.remount end it "should unmount and mount if the resource does not specify it supports remounting" do @mounter.stubs(:info) @resource.stubs(:[]).with(:remounts).returns(false) @mounter.expects(:unmount) @mounter.expects(:mount) @mounter.remount end it "should log that it is remounting" do @resource.stubs(:[]).with(:remounts).returns(:true) @mounter.stubs(:mountcmd) @mounter.expects(:info).with("Remounting") @mounter.remount end end describe Puppet::Provider::Mount, " when unmounting" do before :each do @mounter.stubs(:get).with(:ensure).returns(:unmounted) end it "should call the :umount command with the resource name" do @mounter.expects(:umount).with(@name) @mounter.unmount end it "should update the :ensure state to :absent if it was :ghost before" do @mounter.expects(:umount).with(@name).returns true @mounter.expects(:get).with(:ensure).returns(:ghost) @mounter.expects(:set).with(:ensure => :absent) @mounter.unmount end it "should update the :ensure state to :unmounted if it was :mounted before" do @mounter.expects(:umount).with(@name).returns true @mounter.expects(:get).with(:ensure).returns(:mounted) @mounter.expects(:set).with(:ensure => :unmounted) @mounter.unmount end end describe Puppet::Provider::Mount, " when determining if it is mounted" do it "should query the property_hash" do @mounter.expects(:get).with(:ensure).returns(:mounted) @mounter.mounted? end it "should return true if prefetched value is :mounted" do @mounter.stubs(:get).with(:ensure).returns(:mounted) @mounter.mounted? == true end it "should return true if prefetched value is :ghost" do @mounter.stubs(:get).with(:ensure).returns(:ghost) @mounter.mounted? == true end it "should return false if prefetched value is :absent" do @mounter.stubs(:get).with(:ensure).returns(:absent) @mounter.mounted? == false end it "should return false if prefetched value is :unmounted" do @mounter.stubs(:get).with(:ensure).returns(:unmounted) @mounter.mounted? == false end end end diff --git a/spec/unit/provider/naginator_spec.rb b/spec/unit/provider/naginator_spec.rb index 8ae1895cc..1d8e78015 100755 --- a/spec/unit/provider/naginator_spec.rb +++ b/spec/unit/provider/naginator_spec.rb @@ -1,58 +1,57 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/naginator' describe Puppet::Provider::Naginator do before do @resource_type = stub 'resource_type', :name => :nagios_test @class = Class.new(Puppet::Provider::Naginator) @class.stubs(:resource_type).returns @resource_type end it "should be able to look up the associated Nagios type" do nagios_type = mock "nagios_type" nagios_type.stubs :attr_accessor Nagios::Base.expects(:type).with(:test).returns nagios_type @class.nagios_type.should equal(nagios_type) end it "should use the Nagios type to determine whether an attribute is valid" do nagios_type = mock "nagios_type" nagios_type.stubs :attr_accessor Nagios::Base.expects(:type).with(:test).returns nagios_type nagios_type.expects(:parameters).returns [:foo, :bar] @class.valid_attr?(:test, :foo).should be_true end it "should use Naginator to parse configuration snippets" do parser = mock 'parser' parser.expects(:parse).with("my text").returns "my instances" Nagios::Parser.expects(:new).returns(parser) @class.parse("my text").should == "my instances" end it "should join Nagios::Base records with '\\n' when asked to convert them to text" do @class.expects(:header).returns "myheader\n" @class.to_file([:one, :two]).should == "myheader\none\ntwo" end it "should be able to prefetch instance from configuration files" do @class.should respond_to(:prefetch) end it "should be able to generate a list of instances" do @class.should respond_to(:instances) end it "should never skip records" do @class.should_not be_skip_record("foo") end end diff --git a/spec/unit/provider/nameservice/directoryservice_spec.rb b/spec/unit/provider/nameservice/directoryservice_spec.rb index 661899db9..47f2ad0cd 100755 --- a/spec/unit/provider/nameservice/directoryservice_spec.rb +++ b/spec/unit/provider/nameservice/directoryservice_spec.rb @@ -1,38 +1,37 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' # We use this as a reasonable way to obtain all the support infrastructure. [:user, :group].each do |type_for_this_round| provider_class = Puppet::Type.type(type_for_this_round).provider(:directoryservice) describe provider_class do before do @resource = stub("resource") @provider = provider_class.new(@resource) end it "[#6009] should handle nested arrays of members" do current = ["foo", "bar", "baz"] desired = ["foo", ["quux"], "qorp"] group = 'example' @resource.stubs(:[]).with(:name).returns(group) @resource.stubs(:[]).with(:auth_membership).returns(true) @provider.instance_variable_set(:@property_value_cache_hash, { :members => current }) %w{bar baz}.each do |del| @provider.expects(:execute).once. with([:dseditgroup, '-o', 'edit', '-n', '.', '-d', del, group]) end %w{quux qorp}.each do |add| @provider.expects(:execute).once. with([:dseditgroup, '-o', 'edit', '-n', '.', '-a', add, group]) end expect { @provider.set(:members, desired) }.should_not raise_error end end end diff --git a/spec/unit/provider/network_device_spec.rb b/spec/unit/provider/network_device_spec.rb new file mode 100755 index 000000000..83d2bdc01 --- /dev/null +++ b/spec/unit/provider/network_device_spec.rb @@ -0,0 +1,148 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/network_device' + +Puppet::Type.type(:vlan).provide :test, :parent => Puppet::Provider::NetworkDevice do + mk_resource_methods + def self.lookup(device_url, name) + end +end + +provider_class = Puppet::Type.type(:vlan).provider(:test) + +describe provider_class do + before do + @resource = stub("resource", :name => "test") + @provider = provider_class.new(@resource) + end + + it "should be able to prefetch instances from the device" do + provider_class.should respond_to(:prefetch) + end + + it "should have an instances method" do + provider_class.should respond_to(:instances) + end + + describe "when prefetching" do + before do + @resource = stub_everything 'resource' + @resources = {"200" => @resource} + provider_class.stubs(:lookup) + end + + it "should lookup an entry for each passed resource" do + provider_class.expects(:lookup).with(nil, "200").returns nil + + provider_class.stubs(:new) + @resource.stubs(:provider=) + provider_class.prefetch(@resources) + end + + describe "resources that do not exist" do + it "should create a provider with :ensure => :absent" do + provider_class.stubs(:lookup).returns(nil) + provider_class.expects(:new).with(:ensure => :absent).returns "myprovider" + @resource.expects(:provider=).with("myprovider") + provider_class.prefetch(@resources) + end + end + + describe "resources that exist" do + it "should create a provider with the results of the find and ensure at present" do + provider_class.stubs(:lookup).returns({ :name => "200", :description => "myvlan"}) + + provider_class.expects(:new).with(:name => "200", :description => "myvlan", :ensure => :present).returns "myprovider" + @resource.expects(:provider=).with("myprovider") + + provider_class.prefetch(@resources) + end + end + end + + describe "when being initialized" do + describe "with a hash" do + before do + @resource_class = mock 'resource_class' + provider_class.stubs(:resource_type).returns @resource_class + + @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property + @resource_class.stubs(:attrclass).with(:one).returns(@property_class) + @resource_class.stubs(:valid_parameter?).returns true + end + + it "should store a copy of the hash as its vlan_properties" do + instance = provider_class.new(:one => :two) + instance.former_properties.should == {:one => :two} + end + end + end + + describe "when an instance" do + before do + @instance = provider_class.new + + @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property + @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:description] + provider_class.stubs(:resource_type).returns @resource_class + end + + it "should have a method for creating the instance" do + @instance.should respond_to(:create) + end + + it "should have a method for removing the instance" do + @instance.should respond_to(:destroy) + end + + it "should indicate when the instance already exists" do + @instance = provider_class.new(:ensure => :present) + @instance.exists?.should be_true + end + + it "should indicate when the instance does not exist" do + @instance = provider_class.new(:ensure => :absent) + @instance.exists?.should be_false + end + + describe "is being flushed" do + it "should flush properties" do + @instance = provider_class.new(:ensure => :present, :name => "200", :description => "myvlan") + @instance.flush + @instance.properties.should be_empty + end + end + + describe "is being created" do + before do + @rclass = mock 'resource_class' + @rclass.stubs(:validproperties).returns([:description]) + @resource = stub_everything 'resource' + @resource.stubs(:class).returns @rclass + @resource.stubs(:should).returns nil + @instance.stubs(:resource).returns @resource + end + + it "should set its :ensure value to :present" do + @instance.create + @instance.properties[:ensure].should == :present + end + + it "should set all of the other attributes from the resource" do + @resource.expects(:should).with(:description).returns "myvlan" + + @instance.create + @instance.properties[:description].should == "myvlan" + end + end + + describe "is being destroyed" do + it "should set its :ensure value to :absent" do + @instance.destroy + @instance.properties[:ensure].should == :absent + end + end + end +end diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb index 40e497c27..fba29d7a7 100755 --- a/spec/unit/provider/package/aix_spec.rb +++ b/spec/unit/provider/package/aix_spec.rb @@ -1,66 +1,65 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:aix) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:source).returns "mysource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.resource = @resource end [:install, :uninstall, :latest, :query, :update].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end end it "should uninstall a package" do @provider.expects(:installp).with('-gu', 'mypackage') @provider.uninstall end describe "when installing" do it "should install a package" do @resource.stubs(:should).with(:ensure).returns(:installed) @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage') @provider.install end it "should install a specific package version" do @resource.stubs(:should).with(:ensure).returns("1.2.3.4") @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage 1.2.3.4') @provider.install end end describe "when finding the latest version" do it "should return the current version when no later version is present" do @provider.stubs(:latest_info).returns(nil) @provider.stubs(:properties).returns( { :ensure => "1.2.3.4" } ) @provider.latest.should == "1.2.3.4" end it "should return the latest version of a package" do @provider.stubs(:latest_info).returns( { :version => "1.2.3.5" } ) @provider.latest.should == "1.2.3.5" end end it "update should install a package" do @provider.expects(:install).with(false) @provider.update end end diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb index b0ce4f799..b020b0f4f 100755 --- a/spec/unit/provider/package/apt_spec.rb +++ b/spec/unit/provider/package/apt_spec.rb @@ -1,145 +1,144 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider = Puppet::Type.type(:package).provider(:apt) describe provider do before do @resource = stub 'resource', :[] => "asdf" @provider = provider.new(@resource) @fakeresult = "install ok installed asdf 1.0\n" end it "should be versionable" do provider.should be_versionable end it "should use :install to update" do @provider.expects(:install) @provider.update end it "should use 'apt-get remove' to uninstall" do @provider.expects(:aptget).with("-y", "-q", :remove, "asdf") @provider.uninstall end it "should use 'apt-get purge' and 'dpkg purge' to purge" do @provider.expects(:aptget).with("-y", "-q", :remove, "--purge", "asdf") @provider.expects(:dpkg).with("--purge", "asdf") @provider.purge end it "should use 'apt-cache policy' to determine the latest version of a package" do @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf: Installed: 1:1.0 Candidate: 1:1.1 Version table: 1:1.0 650 http://ftp.osuosl.org testing/main Packages *** 1:1.1 100 /var/lib/dpkg/status" @provider.latest.should == "1:1.1" end it "should print and error and return nil if no policy is found" do @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf:" @provider.expects(:err) @provider.latest.should be_nil end it "should be able to preseed" do @provider.should respond_to(:run_preseed) end it "should preseed with the provided responsefile when preseeding is called for" do @resource.expects(:[]).with(:responsefile).returns "/my/file" FileTest.expects(:exist?).with("/my/file").returns true @provider.expects(:info) @provider.expects(:preseed).with("/my/file") @provider.run_preseed end it "should not preseed if no responsefile is provided" do @resource.expects(:[]).with(:responsefile).returns nil @provider.expects(:info) @provider.expects(:preseed).never @provider.run_preseed end it "should fail if a cdrom is listed in the sources list and :allowcdrom is not specified" describe "when installing" do it "should preseed if a responsefile is provided" do @resource.expects(:[]).with(:responsefile).returns "/my/file" @provider.expects(:run_preseed) @provider.stubs(:aptget) @provider.install end it "should check for a cdrom" do @provider.expects(:checkforcdrom) @provider.stubs(:aptget) @provider.install end it "should use 'apt-get install' with the package name if no version is asked for" do @resource.expects(:[]).with(:ensure).returns :installed @provider.expects(:aptget).with { |*command| command[-1] == "asdf" and command[-2] == :install } @provider.install end it "should specify the package version if one is asked for" do @resource.expects(:[]).with(:ensure).returns "1.0" @provider.expects(:aptget).with { |*command| command[-1] == "asdf=1.0" } @provider.install end it "should use --force-yes if a package version is specified" do @resource.expects(:[]).with(:ensure).returns "1.0" @provider.expects(:aptget).with { |*command| command.include?("--force-yes") } @provider.install end it "should do a quiet install" do @provider.expects(:aptget).with { |*command| command.include?("-q") } @provider.install end it "should default to 'yes' for all questions" do @provider.expects(:aptget).with { |*command| command.include?("-y") } @provider.install end it "should keep config files if asked" do @resource.expects(:[]).with(:configfiles).returns :keep @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confold") } @provider.install end it "should replace config files if asked" do @resource.expects(:[]).with(:configfiles).returns :replace @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confnew") } @provider.install end end end diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb index 444fb31c1..e64146056 100755 --- a/spec/unit/provider/package/dpkg_spec.rb +++ b/spec/unit/provider/package/dpkg_spec.rb @@ -1,225 +1,224 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider = Puppet::Type.type(:package).provider(:dpkg) describe provider do before do @resource = stub 'resource', :[] => "asdf" @provider = provider.new(@resource) @provider.expects(:execute).never # forbid "manual" executions @fakeresult = "install ok installed asdf 1.0\n" end it "should have documentation" do provider.doc.should be_instance_of(String) end describe "when listing all instances" do before do provider.stubs(:command).with(:dpkgquery).returns "myquery" end it "should use dpkg-query" do provider.expects(:command).with(:dpkgquery).returns "myquery" provider.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").returns @fakeresult provider.instances end it "should create and return an instance with each parsed line from dpkg-query" do pipe = mock 'pipe' pipe.expects(:each).yields @fakeresult provider.expects(:execpipe).yields pipe asdf = mock 'pkg1' provider.expects(:new).with(:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg).returns asdf provider.instances.should == [asdf] end it "should warn on and ignore any lines it does not understand" do pipe = mock 'pipe' pipe.expects(:each).yields "foobar" provider.expects(:execpipe).yields pipe Puppet.expects(:warning) provider.expects(:new).never provider.instances.should == [] end end describe "when querying the current state" do it "should use dpkg-query" do @provider.expects(:dpkgquery).with("-W", "--showformat",'${Status} ${Package} ${Version}\\n', "asdf").returns @fakeresult @provider.query end it "should consider the package purged if dpkg-query fails" do @provider.expects(:dpkgquery).raises Puppet::ExecutionFailure.new("eh") @provider.query[:ensure].should == :purged end it "should return a hash of the found status with the desired state, error state, status, name, and 'ensure'" do @provider.expects(:dpkgquery).returns @fakeresult @provider.query.should == {:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg} end it "should consider the package absent if the dpkg-query result cannot be interpreted" do @provider.expects(:dpkgquery).returns "somebaddata" @provider.query[:ensure].should == :absent end it "should fail if an error is discovered" do @provider.expects(:dpkgquery).returns @fakeresult.sub("ok", "error") lambda { @provider.query }.should raise_error(Puppet::Error) end it "should consider the package purged if it is marked 'not-installed'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "not-installed") @provider.query[:ensure].should == :purged end it "should consider the package absent if it is marked 'config-files'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "config-files") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'half-installed'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-installed") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'unpacked'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "unpacked") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'half-configured'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-configured") @provider.query[:ensure].should == :absent end it "should consider the package held if its state is 'hold'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("install", "hold") @provider.query[:ensure].should == :held end end it "should be able to install" do @provider.should respond_to(:install) end describe "when installing" do before do @resource.stubs(:[]).with(:source).returns "mypkg" end it "should fail to install if no source is specified in the resource" do @resource.expects(:[]).with(:source).returns nil lambda { @provider.install }.should raise_error(ArgumentError) end it "should use 'dpkg -i' to install the package" do @resource.expects(:[]).with(:source).returns "mypackagefile" @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[-1] == "mypackagefile" and command[-2] == "-i" } @provider.install end it "should keep old config files if told to do so" do @resource.expects(:[]).with(:configfiles).returns :keep @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[0] == "--force-confold" } @provider.install end it "should replace old config files if told to do so" do @resource.expects(:[]).with(:configfiles).returns :replace @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[0] == "--force-confnew" } @provider.install end it "should ensure any hold is removed" do @provider.expects(:unhold).once @provider.expects(:dpkg) @provider.install end end describe "when holding or unholding" do before do @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" @tempfile.stubs(:write) Tempfile.stubs(:new).returns @tempfile end it "should install first if holding" do @provider.stubs(:execute) @provider.expects(:install).once @provider.hold end it "should execute dpkg --set-selections when holding" do @provider.stubs(:install) @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once @provider.hold end it "should execute dpkg --set-selections when unholding" do @provider.stubs(:install) @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once @provider.hold end end it "should use :install to update" do @provider.expects(:install) @provider.update end describe "when determining latest available version" do it "should return the version found by dpkg-deb" do @resource.expects(:[]).with(:source).returns "myfile" @provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "asdf\t1.0" @provider.latest.should == "1.0" end it "should warn if the package file contains a different package" do @provider.expects(:dpkg_deb).returns("foo\tversion") @provider.expects(:warning) @provider.latest end it "should cope with names containing ++" do @resource = stub 'resource', :[] => "asdf++" @provider = provider.new(@resource) @provider.expects(:dpkg_deb).returns "asdf++\t1.0" @provider.latest.should == "1.0" end end it "should use 'dpkg -r' to uninstall" do @provider.expects(:dpkg).with("-r", "asdf") @provider.uninstall end it "should use 'dpkg --purge' to purge" do @provider.expects(:dpkg).with("--purge", "asdf") @provider.purge end end diff --git a/spec/unit/provider/package/freebsd_spec.rb b/spec/unit/provider/package/freebsd_spec.rb index 0d38a16cf..9c8038791 100755 --- a/spec/unit/provider/package/freebsd_spec.rb +++ b/spec/unit/provider/package/freebsd_spec.rb @@ -1,55 +1,54 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:freebsd) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.resource = @resource end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end describe "when installing" do before :each do @resource.stubs(:should).with(:ensure).returns(:installed) end it "should install a package from a path to a directory" do # For better or worse, trailing '/' is needed. --daniel 2011-01-26 path = '/path/to/directory/' @resource.stubs(:[]).with(:source).returns(path) Puppet::Util::Execution.expects(:withenv).once.with({:PKG_PATH => path}).yields @provider.expects(:pkgadd).once.with("mypackage") expect { @provider.install }.should_not raise_error end %w{http https ftp}.each do |protocol| it "should install a package via #{protocol}" do # For better or worse, trailing '/' is needed. --daniel 2011-01-26 path = "#{protocol}://localhost/" @resource.stubs(:[]).with(:source).returns(path) Puppet::Util::Execution.expects(:withenv).once.with({:PACKAGESITE => path}).yields @provider.expects(:pkgadd).once.with('-r', "mypackage") expect { @provider.install }.should_not raise_error end end end end diff --git a/spec/unit/provider/package/gem_spec.rb b/spec/unit/provider/package/gem_spec.rb old mode 100644 new mode 100755 index c2bd3cc90..284e63c23 --- a/spec/unit/provider/package/gem_spec.rb +++ b/spec/unit/provider/package/gem_spec.rb @@ -1,97 +1,96 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:gem) describe provider_class do it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end describe "when installing" do before do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns nil # We have to set a name, though @resource.stubs(:[]).with(:name).returns "myresource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.stubs(:resource).returns @resource end it "should use the path to the gem" do provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" @provider.expects(:execute).with { |args| args[0] == "/my/gem" }.returns "" @provider.install end it "should specify that the gem is being installed" do @provider.expects(:execute).with { |args| args[1] == "install" }.returns "" @provider.install end it "should specify that dependencies should be included" do @provider.expects(:execute).with { |args| args[2] == "--include-dependencies" }.returns "" @provider.install end it "should specify that documentation should not be included" do @provider.expects(:execute).with { |args| args[3] == "--no-rdoc" }.returns "" @provider.install end it "should specify that RI should not be included" do @provider.expects(:execute).with { |args| args[4] == "--no-ri" }.returns "" @provider.install end it "should specify the package name" do @provider.expects(:execute).with { |args| args[5] == "myresource" }.returns "" @provider.install end describe "when a source is specified" do describe "as a normal file" do it "should use the file name instead of the gem name" do @resource.stubs(:[]).with(:source).returns "/my/file" @provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" @provider.install end end describe "as a file url" do it "should use the file name instead of the gem name" do @resource.stubs(:[]).with(:source).returns "file:///my/file" @provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" @provider.install end end describe "as a puppet url" do it "should fail" do @resource.stubs(:[]).with(:source).returns "puppet://my/file" lambda { @provider.install }.should raise_error(Puppet::Error) end end describe "as a non-file and non-puppet url" do it "should treat the source as a gem repository" do @resource.stubs(:[]).with(:source).returns "http://host/my/file" @provider.expects(:execute).with { |args| args[3..5] == ["--source", "http://host/my/file", "myresource"] }.returns "" @provider.install end end describe "with an invalid uri" do it "should fail" do URI.expects(:parse).raises(ArgumentError) @resource.stubs(:[]).with(:source).returns "http:::::uppet:/:/my/file" lambda { @provider.install }.should raise_error(Puppet::Error) end end end end end diff --git a/spec/unit/provider/package/hpux_spec.rb b/spec/unit/provider/package/hpux_spec.rb old mode 100644 new mode 100755 index e75ae0822..b781f6540 --- a/spec/unit/provider/package/hpux_spec.rb +++ b/spec/unit/provider/package/hpux_spec.rb @@ -1,52 +1,51 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:hpux) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:source).returns "mysource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.stubs(:resource).returns @resource end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end it "should have an uninstall method" do @provider = provider_class.new @provider.should respond_to(:uninstall) end it "should have a swlist method" do @provider = provider_class.new @provider.should respond_to(:swlist) end describe "when installing" do it "should use a command-line like 'swinstall -x mount_all_filesystems=false -s SOURCE PACKAGE-NAME'" do @provider.expects(:swinstall).with('-x', 'mount_all_filesystems=false', '-s', 'mysource', 'mypackage') @provider.install end end describe "when uninstalling" do it "should use a command-line like 'swremove -x mount_all_filesystems=false PACKAGE-NAME'" do @provider.expects(:swremove).with('-x', 'mount_all_filesystems=false', 'mypackage') @provider.uninstall end end end diff --git a/spec/unit/provider/package/macports_spec.rb b/spec/unit/provider/package/macports_spec.rb new file mode 100755 index 000000000..7d1acd537 --- /dev/null +++ b/spec/unit/provider/package/macports_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:macports) + +describe provider_class do + let :resource_name do + "foo" + end + + let :resource do + Puppet::Type.type(:package).new(:name => resource_name, :provider => :macports) + end + + let :provider do + prov = resource.provider + prov.expects(:execute).never + prov + end + + let :current_hash do + {:name => resource_name, :ensure => "1.2.3", :revision => "1", :provider => :macports} + end + + describe "provider features" do + subject { provider } + + it { should be_installable } + it { should be_uninstallable } + it { should be_upgradeable } + it { should be_versionable } + end + + describe "when listing all instances" do + it "should call port -q installed" do + provider_class.expects(:port).with("-q", :installed).returns("") + provider_class.instances + end + + it "should create instances from active ports" do + provider_class.expects(:port).returns("foo @1.234.5_2 (active)") + provider_class.instances.size.should == 1 + end + + it "should ignore ports that aren't activated" do + provider_class.expects(:port).returns("foo @1.234.5_2") + provider_class.instances.size.should == 0 + end + end + + describe "when installing" do + it "should not specify a version when ensure is set to latest" do + resource[:ensure] = :latest + provider.expects(:port).with { |flag, method, name, version| + version.should be_nil + } + provider.install + end + + it "should not specify a version when ensure is set to present" do + resource[:ensure] = :present + provider.expects(:port).with { |flag, method, name, version| + version.should be_nil + } + provider.install + end + + it "should specify a version when ensure is set to a version" do + resource[:ensure] = "1.2.3" + provider.expects(:port).with { |flag, method, name, version| + version.should be + } + provider.install + end + end + + describe "when querying for the latest version" do + let :new_info_line do + "1.2.3 2" + end + let :infoargs do + ["-q", :info, "--line", "--version", "--revision", resource_name] + end + + it "should return nil when the package cannot be found" do + resource[:name] = resource_name + provider.expects(:port).returns("") + provider.latest.should == nil + end + + it "should return the current version if the installed port has the same revision" do + current_hash[:revision] = "2" + provider.expects(:port).with(*infoargs).returns(new_info_line) + provider.expects(:query).returns(current_hash) + provider.latest.should == current_hash[:ensure] + end + + it "should return the new version_revision if the installed port has a lower revision" do + current_hash[:revision] = "1" + provider.expects(:port).with(*infoargs).returns(new_info_line) + provider.expects(:query).returns(current_hash) + provider.latest.should == "1.2.3_2" + end + end + + describe "when updating a port" do + it "should execute port upgrade if the port is installed" do + resource[:name] = resource_name + resource[:ensure] = :present + provider.expects(:query).returns(current_hash) + provider.expects(:port).with("-q", :upgrade, resource_name) + provider.update + end + + it "should execute port install if the port is not installed" do + resource[:name] = resource_name + resource[:ensure] = :present + provider.expects(:query).returns("") + provider.expects(:port).with("-q", :install, resource_name) + provider.update + end + end +end diff --git a/spec/unit/provider/package/nim_spec.rb b/spec/unit/provider/package/nim_spec.rb index 74f903813..0fa9f580d 100755 --- a/spec/unit/provider/package/nim_spec.rb +++ b/spec/unit/provider/package/nim_spec.rb @@ -1,42 +1,41 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:nim) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:source).returns "mysource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.resource = @resource end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end describe "when installing" do it "should install a package" do @resource.stubs(:should).with(:ensure).returns(:installed) @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage'") @provider.install end it "should install a versioned package" do @resource.stubs(:should).with(:ensure).returns("1.2.3.4") @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage 1.2.3.4'") @provider.install end end end diff --git a/spec/unit/provider/package/pip_spec.rb b/spec/unit/provider/package/pip_spec.rb old mode 100644 new mode 100755 index 8953b4b2c..b56271029 --- a/spec/unit/provider/package/pip_spec.rb +++ b/spec/unit/provider/package/pip_spec.rb @@ -1,177 +1,176 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:pip) describe provider_class do before do @resource = Puppet::Resource.new(:package, "sdsfdssdhdfyjymdgfcjdfjxdrssf") @provider = provider_class.new(@resource) end describe "parse" do it "should return a hash on valid input" do provider_class.parse("Django==1.2.5").should == { :ensure => "1.2.5", :name => "Django", :provider => :pip, } end it "should return nil on invalid input" do provider_class.parse("foo").should == nil end end describe "instances" do it "should return an array when pip is present" do provider_class.expects(:which).with('pip').returns("/fake/bin/pip") p = stub("process") p.expects(:collect).yields("Django==1.2.5") provider_class.expects(:execpipe).with("/fake/bin/pip freeze").yields(p) provider_class.instances end it "should return an empty array when pip is missing" do provider_class.expects(:which).with('pip').returns nil provider_class.instances.should == [] end end describe "query" do before do @resource[:name] = "Django" end it "should return a hash when pip and the package are present" do provider_class.expects(:instances).returns [provider_class.new({ :ensure => "1.2.5", :name => "Django", :provider => :pip, })] @provider.query.should == { :ensure => "1.2.5", :name => "Django", :provider => :pip, } end it "should return nil when the package is missing" do provider_class.expects(:instances).returns [] @provider.query.should == nil end end describe "latest" do it "should find a version number for Django" do @resource[:name] = "Django" @provider.latest.should_not == nil end it "should not find a version number for sdsfdssdhdfyjymdgfcjdfjxdrssf" do @resource[:name] = "sdsfdssdhdfyjymdgfcjdfjxdrssf" @provider.latest.should == nil end end describe "install" do before do @resource[:name] = "sdsfdssdhdfyjymdgfcjdfjxdrssf" @url = "git+https://example.com/sdsfdssdhdfyjymdgfcjdfjxdrssf.git" end it "should install" do @resource[:ensure] = :installed @resource[:source] = nil @provider.expects(:lazy_pip). with("install", '-q', "sdsfdssdhdfyjymdgfcjdfjxdrssf") @provider.install end it "should install from SCM" do @resource[:ensure] = :installed @resource[:source] = @url @provider.expects(:lazy_pip). with("install", '-q', '-e', "#{@url}#egg=sdsfdssdhdfyjymdgfcjdfjxdrssf") @provider.install end it "should install a particular SCM revision" do @resource[:ensure] = "0123456" @resource[:source] = @url @provider.expects(:lazy_pip). with("install", "-q", "-e", "#{@url}@0123456#egg=sdsfdssdhdfyjymdgfcjdfjxdrssf") @provider.install end it "should install a particular version" do @resource[:ensure] = "0.0.0" @resource[:source] = nil @provider.expects(:lazy_pip).with("install", "-q", "sdsfdssdhdfyjymdgfcjdfjxdrssf==0.0.0") @provider.install end it "should upgrade" do @resource[:ensure] = :latest @resource[:source] = nil @provider.expects(:lazy_pip). with("install", "-q", "--upgrade", "sdsfdssdhdfyjymdgfcjdfjxdrssf") @provider.install end end describe "uninstall" do it "should uninstall" do @resource[:name] = "sdsfdssdhdfyjymdgfcjdfjxdrssf" @provider.expects(:lazy_pip). with('uninstall', '-y', '-q', 'sdsfdssdhdfyjymdgfcjdfjxdrssf') @provider.uninstall end end describe "update" do it "should just call install" do @provider.expects(:install).returns(nil) @provider.update end end describe "lazy_pip" do it "should succeed if pip is present" do @provider.stubs(:pip).returns(nil) @provider.method(:lazy_pip).call "freeze" end it "should retry if pip has not yet been found" do @provider.expects(:pip).twice.with('freeze').raises(NoMethodError).then.returns(nil) @provider.expects(:which).with('pip').returns("/fake/bin/pip") @provider.method(:lazy_pip).call "freeze" end it "should fail if pip is missing" do @provider.expects(:pip).with('freeze').raises(NoMethodError) @provider.expects(:which).with('pip').returns(nil) expect { @provider.method(:lazy_pip).call("freeze") }.to raise_error(NoMethodError) end end end diff --git a/spec/unit/provider/package/pkg_spec.rb b/spec/unit/provider/package/pkg_spec.rb old mode 100644 new mode 100755 index 3455c4c40..04a4ae607 --- a/spec/unit/provider/package/pkg_spec.rb +++ b/spec/unit/provider/package/pkg_spec.rb @@ -1,63 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider = Puppet::Type.type(:package).provider(:pkg) describe provider do before do @resource = stub 'resource', :[] => "dummy" @provider = provider.new(@resource) @fakeresult = "install ok installed dummy 1.0\n" end def self.it_should_respond_to(*actions) actions.each do |action| it "should respond to :#{action}" do @provider.should respond_to(action) end end end it_should_respond_to :install, :uninstall, :update, :query, :latest it "should be versionable" do provider.should_not be_versionable end it "should use :install to update" do @provider.expects(:install) @provider.update end it "should parse a line correctly" do result = provider.parse_line("dummy 1.0@1.0-1.0 installed ----") result.should == {:name => "dummy", :version => "1.0@1.0-1.0", :ensure => :present, :status => "installed", :provider => :pkg, :error => "ok"} end it "should fail to parse an incorrect line" do result = provider.parse_line("foo") result.should be_nil end it "should fail to list a missing package" do @provider.expects(:pkg).with(:list, "-H", "dummy").returns "1" @provider.query.should == {:status=>"missing", :ensure=>:absent, :name=>"dummy", :error=>"ok"} end it "should fail to list a package when it can't parse the output line" do @provider.expects(:pkg).with(:list, "-H", "dummy").returns "failed" @provider.query.should == {:status=>"missing", :ensure=>:absent, :name=>"dummy", :error=>"ok"} end it "should list package correctly" do @provider.expects(:pkg).with(:list, "-H", "dummy").returns "dummy 1.0@1.0-1.0 installed ----" @provider.query.should == {:name => "dummy", :version => "1.0@1.0-1.0", :ensure => :present, :status => "installed", :provider => :pkg, :error => "ok"} end end diff --git a/spec/unit/provider/package/pkgdmg_spec.rb b/spec/unit/provider/package/pkgdmg_spec.rb index 86631e596..155f12e7b 100755 --- a/spec/unit/provider/package/pkgdmg_spec.rb +++ b/spec/unit/provider/package/pkgdmg_spec.rb @@ -1,84 +1,83 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider = Puppet::Type.type(:package).provider(:pkgdmg) describe provider do before do @resource = stub 'resource', :[] => "dummypkgdmg" @provider = provider.new(@resource) @fakemountpoint = "/tmp/dmg.foo" @fakepkgfile = "/tmp/test.pkg" @fakehdiutilinfo = {"system-entities" => [{"mount-point" => @fakemountpoint}] } @fakehdiutilplist = Plist::Emit.dump(@fakehdiutilinfo) @hdiutilmountargs = ["mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp"] end it "should not be versionable" do provider.versionable?.should be_false end it "should not be uninstallable" do provider.uninstallable?.should be_false end describe "when installing it should fail when" do it "no source is specified" do @resource.stubs(:[]).with(:source).returns nil lambda { @provider.install }.should raise_error(Puppet::Error) end it "no name is specified" do @resource.stubs(:[]).with(:name).returns nil lambda { @provider.install }.should raise_error(Puppet::Error) end it "the source does not end in .dmg or .pkg" do @resource.stubs(:[]).with(:source).returns "notendingindotdmgorpkg" lambda { @provider.install }.should raise_error(Puppet::Error) end it "a disk image with no system entities is mounted" do @provider.stubs(:[]).with(:hdiutil).returns "" lambda { @provider.install }.should raise_error(Puppet::Error) end end # These tests shouldn't be this messy. The pkgdmg provider needs work... describe "when installing a pkgdmg" do before do fh = mock 'filehandle' fh.stubs(:path).yields "/tmp/foo" @resource.stubs(:[]).with(:source).returns "foo.dmg" File.stubs(:open).yields fh end it "should call hdiutil to mount and eject the disk image" do Dir.stubs(:entries).returns [] @provider.class.expects(:hdiutil).with("eject", @fakemountpoint).returns 0 @provider.class.expects(:hdiutil).with("mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", nil).returns @fakehdiutilplist @provider.install end it "should call installpkg if a pkg/mpkg is found on the dmg" do Dir.stubs(:entries).returns ["foo.pkg"] @provider.class.stubs(:hdiutil).returns @fakehdiutilplist @provider.class.expects(:installpkg).with("#{@fakemountpoint}/foo.pkg", @resource[:name], "foo.dmg").returns "" @provider.install end end describe "when installing flat pkg file" do it "should call installpkg if a flat pkg file is found instead of a .dmg image" do @resource.stubs(:[]).with(:source).returns "/tmp/test.pkg" @resource.stubs(:[]).with(:name).returns "testpkg" @provider.class.expects(:installpkgdmg).with("#{@fakepkgfile}", "testpkg").returns "" @provider.install end end end diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb old mode 100644 new mode 100755 index 4e2294a77..601c24009 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -1,67 +1,66 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider = Puppet::Type.type(:package).provider(:yum) describe provider do before do # Create a mock resource @resource = stub 'resource' @resource.stubs(:[]).with(:name).returns 'mypackage' @provider = provider.new(@resource) @provider.stubs(:resource).returns @resource @provider.stubs(:yum).returns 'yum' @provider.stubs(:rpm).returns 'rpm' @provider.stubs(:get).with(:name).returns 'mypackage' @provider.stubs(:get).with(:version).returns '1' @provider.stubs(:get).with(:release).returns '1' @provider.stubs(:get).with(:arch).returns 'i386' end # provider should repond to the following methods [:install, :latest, :update, :purge].each do |method| it "should have a(n) #{method}" do @provider.should respond_to(method) end end describe 'when installing' do it 'should call yum install for :installed' do @resource.stubs(:should).with(:ensure).returns :installed @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage') @provider.install end it 'should use :install to update' do @provider.expects(:install) @provider.update end it 'should be able to set version' do @resource.stubs(:should).with(:ensure).returns '1.2' @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage-1.2') @provider.stubs(:query).returns :ensure => '1.2' @provider.install end it 'should be able to downgrade' do @resource.stubs(:should).with(:ensure).returns '1.0' @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :downgrade, 'mypackage-1.0') @provider.stubs(:query).returns(:ensure => '1.2').then.returns(:ensure => '1.0') @provider.install end end describe 'when uninstalling' do it 'should use erase to purge' do @provider.expects(:yum).with('-y', :erase, 'mypackage') @provider.purge end it 'should use rpm to uninstall' do @provider.expects(:rpm).with('-e', 'mypackage-1-1.i386') @provider.uninstall end end it 'should be versionable' do provider.should be_versionable end end diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb old mode 100644 new mode 100755 index 1bf27ad97..4218b14c7 --- a/spec/unit/provider/package/zypper_spec.rb +++ b/spec/unit/provider/package/zypper_spec.rb @@ -1,81 +1,80 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:zypper) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:ensure).returns :installed @resource.stubs(:command).with(:zypper).returns "/usr/bin/zypper" @provider = provider_class.new(@resource) end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end it "should have a latest method" do @provider = provider_class.new @provider.should respond_to(:uninstall) end it "should have an update method" do @provider = provider_class.new @provider.should respond_to(:update) end it "should have a latest method" do @provider = provider_class.new @provider.should respond_to(:latest) end describe "when installing" do it "should use a command-line with versioned package'" do @resource.stubs(:should).with(:ensure).returns "1.2.3-4.5.6" @provider.expects(:zypper).with('--quiet', :install, '-l', '-y', 'mypackage-1.2.3-4.5.6') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end it "should use a command-line without versioned package" do @resource.stubs(:should).with(:ensure).returns :latest @provider.expects(:zypper).with('--quiet', :install, '-l', '-y', 'mypackage') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end end describe "when updating" do it "should call install method of instance" do @provider.expects(:install) @provider.update end end describe "when getting latest version" do it "should return a version string" do fake_data = "Loading repository data... Reading installed packages... S | Repository | Name | Version | Arch --+----------------+-----------------------+-----------------+------- v | SLES11-Updates | cups | 1.1.1 | x86_64 v | SLES11-Updates | mypackage | 1.3.9h-8.20.1 | x86_64" @provider.expects(:zypper).with("list-updates").returns fake_data @provider.latest.should == "1.3.9h-8.20.1" end end end diff --git a/spec/unit/provider/parsedfile_spec.rb b/spec/unit/provider/parsedfile_spec.rb index 7d95ef1d0..2ff904b7f 100755 --- a/spec/unit/provider/parsedfile_spec.rb +++ b/spec/unit/provider/parsedfile_spec.rb @@ -1,95 +1,94 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/provider/parsedfile' # Most of the tests for this are still in test/ral/provider/parsedfile.rb. describe Puppet::Provider::ParsedFile do before do @class = Class.new(Puppet::Provider::ParsedFile) end describe "when looking up records loaded from disk" do it "should return nil if no records have been loaded" do @class.record?("foo").should be_nil end end describe "when generating a list of instances" do it "should return an instance for each record parsed from all of the registered targets" do @class.expects(:targets).returns %w{/one /two} @class.stubs(:skip_record?).returns false one = [:uno1, :uno2] two = [:dos1, :dos2] @class.expects(:prefetch_target).with("/one").returns one @class.expects(:prefetch_target).with("/two").returns two results = [] (one + two).each do |inst| results << inst.to_s + "_instance" @class.expects(:new).with(inst).returns(results[-1]) end @class.instances.should == results end it "should skip specified records" do @class.expects(:targets).returns %w{/one} @class.expects(:skip_record?).with(:uno).returns false @class.expects(:skip_record?).with(:dos).returns true one = [:uno, :dos] @class.expects(:prefetch_target).returns one @class.expects(:new).with(:uno).returns "eh" @class.expects(:new).with(:dos).never @class.instances end end describe "when flushing a file's records to disk" do before do # This way we start with some @records, like we would in real life. @class.stubs(:retrieve).returns [] @class.default_target = "/foo/bar" @class.initvars @class.prefetch @filetype = Puppet::Util::FileType.filetype(:flat).new("/my/file") Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file").returns @filetype @filetype.stubs(:write) end it "should back up the file being written if the filetype can be backed up" do @filetype.expects(:backup) @class.flush_target("/my/file") end it "should not try to back up the file if the filetype cannot be backed up" do @filetype = Puppet::Util::FileType.filetype(:ram).new("/my/file") Puppet::Util::FileType.filetype(:flat).expects(:new).returns @filetype @filetype.stubs(:write) @class.flush_target("/my/file") end it "should not back up the file more than once between calls to 'prefetch'" do @filetype.expects(:backup).once @class.flush_target("/my/file") @class.flush_target("/my/file") end it "should back the file up again once the file has been reread" do @filetype.expects(:backup).times(2) @class.flush_target("/my/file") @class.prefetch @class.flush_target("/my/file") end end end diff --git a/spec/unit/provider/selboolean_spec.rb b/spec/unit/provider/selboolean_spec.rb index 02a39f2a7..64f925e1e 100755 --- a/spec/unit/provider/selboolean_spec.rb +++ b/spec/unit/provider/selboolean_spec.rb @@ -1,37 +1,36 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:selboolean).provider(:getsetsebool) describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" @provider = provider_class.new(@resource) end it "should return :on when getsebool returns on" do @provider.expects(:getsebool).with("foo").returns "foo --> on\n" @provider.value.should == :on end it "should return :off when getsebool returns on" do @provider.expects(:getsebool).with("foo").returns "foo --> off\n" @provider.value.should == :off end it "should call execpipe when updating boolean setting" do @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" @provider.expects(:execpipe).with("/usr/sbin/setsebool foo off") @provider.value = :off end it "should call execpipe with -P when updating persistent boolean setting" do @resource.stubs(:[]).with(:persistent).returns :true @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" @provider.expects(:execpipe).with("/usr/sbin/setsebool -P foo off") @provider.value = :off end end diff --git a/spec/unit/provider/selmodule_spec.rb b/spec/unit/provider/selmodule_spec.rb index cb143e993..67196667f 100755 --- a/spec/unit/provider/selmodule_spec.rb +++ b/spec/unit/provider/selmodule_spec.rb @@ -1,66 +1,66 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # Note: This unit test depends on having a sample SELinux policy file # in the same directory as this test called selmodule-example.pp # with version 1.5.0. The provided selmodule-example.pp is the first # 256 bytes taken from /usr/share/selinux/targeted/nagios.pp on Fedora 9 -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:selmodule).provider(:semodule) describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" @provider = provider_class.new(@resource) end describe "exists? method" do it "should find a module if it is already loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "foo\t4.4.4\n", "bang\t1.0.0\n"] @provider.exists?.should == :true end it "should return nil if not loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields ["bar\t1.2.3\n", "bang\t1.0.0\n"] @provider.exists?.should be_nil end it "should return nil if no modules are loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields [] @provider.exists?.should be_nil end end describe "selmodversion_file" do it "should return 1.5.0 for the example policy file" do @provider.expects(:selmod_name_to_filename).returns "#{File.dirname(__FILE__)}/selmodule-example.pp" @provider.selmodversion_file.should == "1.5.0" end end describe "syncversion" do it "should return :true if loaded and file modules are in sync" do @provider.expects(:selmodversion_loaded).returns "1.5.0" @provider.expects(:selmodversion_file).returns "1.5.0" @provider.syncversion.should == :true end it "should return :false if loaded and file modules are not in sync" do @provider.expects(:selmodversion_loaded).returns "1.4.0" @provider.expects(:selmodversion_file).returns "1.5.0" @provider.syncversion.should == :false end it "should return before checking file version if no loaded policy" do @provider.expects(:selmodversion_loaded).returns nil @provider.syncversion.should == :false end end end diff --git a/spec/unit/provider/service/daemontools_spec.rb b/spec/unit/provider/service/daemontools_spec.rb index 0bdb0a85d..64eeb9fa4 100755 --- a/spec/unit/provider/service/daemontools_spec.rb +++ b/spec/unit/provider/service/daemontools_spec.rb @@ -1,166 +1,166 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the Daemontools service Provider # # author Brice Figureau # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:daemontools) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @servicedir = "/etc/service" @provider.servicedir=@servicedir @daemondir = "/var/lib/service" @provider.class.defpath=@daemondir # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, source and path (because we won't run # the thing that will fetch the resource path from the provider) @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:path).returns @daemondir @resource.stubs(:ref).returns "Service[myservice]" @provider.resource = @resource @provider.stubs(:command).with(:svc).returns "svc" @provider.stubs(:command).with(:svstat).returns "svstat" @provider.stubs(:svc) @provider.stubs(:svstat) end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when starting" do it "should use 'svc' to start the service" do @provider.stubs(:enabled?).returns :true @provider.expects(:svc).with("-u", "/etc/service/myservice") @provider.start end it "should enable the service if it is not enabled" do @provider.stubs(:svc) @provider.expects(:enabled?).returns :false @provider.expects(:enable) @provider.start end end describe "when stopping" do it "should use 'svc' to stop the service" do @provider.stubs(:disable) @provider.expects(:svc).with("-d", "/etc/service/myservice") @provider.stop end end describe "when restarting" do it "should use 'svc' to restart the service" do @provider.expects(:svc).with("-t", "/etc/service/myservice") @provider.restart end end describe "when enabling" do it "should create a symlink between daemon dir and service dir" do FileTest.stubs(:symlink?).returns(false) File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) @provider.enable end end describe "when disabling" do it "should remove the symlink between daemon dir and service dir" do FileTest.stubs(:directory?).returns(false) FileTest.stubs(:symlink?).returns(true) File.expects(:unlink).with(File.join(@servicedir,"myservice")) @provider.stubs(:texecute).returns("") @provider.disable end it "should stop the service" do FileTest.stubs(:directory?).returns(false) FileTest.stubs(:symlink?).returns(true) File.stubs(:unlink) @provider.expects(:stop) @provider.disable end end describe "when checking if the service is enabled?" do it "should return true if it is running" do @provider.stubs(:status).returns(:running) @provider.enabled?.should == :true end [true, false].each do |t| it "should return #{t} if the symlink exists" do @provider.stubs(:status).returns(:stopped) FileTest.stubs(:symlink?).returns(t) @provider.enabled?.should == "#{t}".to_sym end end end describe "when checking status" do it "should call the external command 'svstat /etc/service/myservice'" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")) @provider.status end end describe "when checking status" do it "and svstat fails, properly raise a Puppet::Error" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).raises(Puppet::ExecutionFailure, "failure") lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: failure') end it "and svstat returns up, then return :running" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: up (pid 454) 954326 seconds") @provider.status.should == :running end it "and svstat returns not running, then return :stopped" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: supervise not running") @provider.status.should == :stopped end end end diff --git a/spec/unit/provider/service/debian_spec.rb b/spec/unit/provider/service/debian_spec.rb index b5edf6882..4e3d30d61 100755 --- a/spec/unit/provider/service/debian_spec.rb +++ b/spec/unit/provider/service/debian_spec.rb @@ -1,101 +1,101 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the debian service provider # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:debian) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, source and path @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:ref).returns "Service[myservice]" @provider.resource = @resource @provider.stubs(:command).with(:update_rc).returns "update_rc" @provider.stubs(:command).with(:invoke_rc).returns "invoke_rc" @provider.stubs(:update_rc) @provider.stubs(:invoke_rc) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when enabling" do it "should call update-rc.d twice" do @provider.expects(:update_rc).twice @provider.enable end end describe "when disabling" do it "should be able to disable services with newer sysv-rc versions" do @provider.stubs(:`).with("dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?").returns "0" @provider.expects(:update_rc).with(@resource[:name], "disable") @provider.disable end it "should be able to enable services with older sysv-rc versions" do @provider.stubs(:`).with("dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?").returns "1" @provider.expects(:update_rc).with("-f", @resource[:name], "remove") @provider.expects(:update_rc).with(@resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", ".") @provider.disable end end describe "when checking whether it is enabled" do it "should call Kernel.system() with the appropriate parameters" do @provider.expects(:system).with("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start").once @provider.enabled? end it "should return true when invoke-rc.d exits with 104 status" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(104) @provider.enabled?.should == :true end it "should return true when invoke-rc.d exits with 106 status" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(106) @provider.enabled?.should == :true end # pick a range of non-[104.106] numbers, strings and booleans to test with. [-100, -1, 0, 1, 100, "foo", "", :true, :false].each do |exitstatus| it "should return false when invoke-rc.d exits with #{exitstatus} status" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(exitstatus) @provider.enabled?.should == :false end end end end diff --git a/spec/unit/provider/service/freebsd_spec.rb b/spec/unit/provider/service/freebsd_spec.rb old mode 100644 new mode 100755 index d8b751108..c1a6d26f7 --- a/spec/unit/provider/service/freebsd_spec.rb +++ b/spec/unit/provider/service/freebsd_spec.rb @@ -1,50 +1,49 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:freebsd) describe provider_class do before :each do @provider = provider_class.new @provider.stubs(:initscript) end it "should correctly parse rcvar for FreeBSD < 7" do @provider.stubs(:execute).returns <= 8.1" do @provider.stubs(:execute).returns <= 7" do @provider.stubs(:rcvar).returns(['# ntpd', 'ntpd_enable="YES"', '# (default: "")']) @provider.rcvar_value.should == "YES" end end diff --git a/spec/unit/provider/service/init_spec.rb b/spec/unit/provider/service/init_spec.rb index b54c27e8e..d64e0fc5d 100755 --- a/spec/unit/provider/service/init_spec.rb +++ b/spec/unit/provider/service/init_spec.rb @@ -1,170 +1,170 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the Init service Provider # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:init) describe provider_class do before :each do @class = Puppet::Type.type(:service).provider(:init) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" # @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:path).returns ["/service/path","/alt/service/path"] # @resource.stubs(:ref).returns "Service[myservice]" File.stubs(:directory?).returns(true) @provider = provider_class.new @provider.resource = @resource end describe "when getting all service instances" do before :each do @services = ['one', 'two', 'three', 'four'] Dir.stubs(:entries).returns @services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) @class.stubs(:defpath).returns('tmp') end it "should return instances for all services" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = @services.collect {|x| "#{x}_instance"} @class.instances.should == results end it "should omit an array of services from exclude list" do exclude = ['two', 'four'] (@services-exclude).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = (@services-exclude).collect {|x| "#{x}_instance"} @class.get_services(@class.defpath, exclude).should == results end it "should omit a single service from the exclude list" do exclude = 'two' (@services-exclude.to_a).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = @services.reject{|x| x==exclude }.collect {|x| "#{x}_instance"} @class.get_services(@class.defpath, exclude).should == results end it "should use defpath" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:path] == @class.defpath}.returns("#{inst}_instance") end results = @services.sort.collect {|x| "#{x}_instance"} @class.instances.sort.should == results end it "should set hasstatus to true for providers" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:hasstatus] == true}.returns("#{inst}_instance") end results = @services.collect {|x| "#{x}_instance"} @class.instances.should == results end end describe "when searching for the init script" do it "should discard paths that do not exist" do File.stubs(:exist?).returns(false) File.stubs(:directory?).returns(false) @provider.paths.should be_empty end it "should discard paths that are not directories" do File.stubs(:exist?).returns(true) File.stubs(:directory?).returns(false) @provider.paths.should be_empty end it "should be able to find the init script in the service path" do File.stubs(:stat).raises(Errno::ENOENT.new('No such file or directory')) File.expects(:stat).with("/service/path/myservice").returns true @provider.initscript.should == "/service/path/myservice" end it "should be able to find the init script in the service path" do File.stubs(:stat).raises(Errno::ENOENT.new('No such file or directory')) File.expects(:stat).with("/alt/service/path/myservice").returns true @provider.initscript.should == "/alt/service/path/myservice" end it "should fail if the service isn't there" do lambda { @provider.initscript }.should raise_error(Puppet::Error, "Could not find init script for 'myservice'") end end describe "if the init script is present" do before :each do File.stubs(:stat).with("/service/path/myservice").returns true end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do @resource.stubs(:[]).with(method).returns "/user/specified/command" @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } @provider.send(method) end it "should pass #{method} to the init script when no explicit command is provided" do @resource.stubs(:[]).with("has#{method}".intern).returns :true @provider.expects(:execute).with { |command, *args| command == ["/service/path/myservice",method]} @provider.send(method) end end end describe "when checking status" do describe "when hasstatus is :true" do before :each do @resource.stubs(:[]).with(:hasstatus).returns :true end it "should execute the command" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") @provider.status end it "should consider the process running if the command returns 0" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.status.should == :running end [-10,-1,1,10].each { |ec| it "should consider the process stopped if the command returns something non-0" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(ec) @provider.status.should == :stopped end } end describe "when hasstatus is not :true" do it "should consider the service :running if it has a pid" do @provider.expects(:getpid).returns "1234" @provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do @provider.expects(:getpid).returns nil @provider.status.should == :stopped end end end describe "when restarting and hasrestart is not :true" do it "should stop and restart the process" do @provider.expects(:texecute).with(:stop, ['/service/path/myservice', :stop ], true).returns("") @provider.expects(:texecute).with(:start,['/service/path/myservice', :start], true).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.restart end end end end diff --git a/spec/unit/provider/service/launchd_spec.rb b/spec/unit/provider/service/launchd_spec.rb index dfcb58fdc..8ae7f003d 100755 --- a/spec/unit/provider/service/launchd_spec.rb +++ b/spec/unit/provider/service/launchd_spec.rb @@ -1,203 +1,203 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the launchd service provider # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet' provider_class = Puppet::Type.type(:service).provider(:launchd) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @joblabel = "com.foo.food" @jobplist = {} # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure and enable @resource.stubs(:[]).with(:name).returns @joblabel @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:enable).returns :true @resource.stubs(:ref).returns "Service[#{@joblabel}]" # stub out the provider methods that actually touch the filesystem # or execute commands @provider.stubs(:plist_from_label).returns([@joblabel, @jobplist]) @provider.stubs(:execute).returns("") @provider.stubs(:resource).returns @resource # We stub this out for the normal case as 10.6 is "special". provider_class.stubs(:get_macosx_version_major).returns("10.5") end it "should have a start method for #{@provider.object_id}" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end it "should have a status method" do @provider.should respond_to(:status) end describe "when checking status" do it "should call the external command 'launchctl list' once" do @provider.expects(:launchctl).with(:list).returns("rotating-strawberry-madonnas") @provider.status end it "should return stopped if not listed in launchctl list output" do @provider.stubs(:launchctl).with(:list).returns("rotating-strawberry-madonnas") @provider.status.should == :stopped end it "should return running if listed in launchctl list output" do @provider.stubs(:launchctl).with(:list).returns(@joblabel) @provider.status.should == :running end end describe "when checking whether the service is enabled" do it "should return true if the job plist says disabled is false" do @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) @provider.enabled?.should == :true end it "should return true if the job plist has no disabled key" do @provider.stubs(:plist_from_label).returns(["foo", {}]) @provider.enabled?.should == :true end it "should return false if the job plist says disabled is true" do @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) @provider.enabled?.should == :false end end describe "when checking whether the service is enabled on OS X 10.6" do it "should return true if the job plist says disabled is true and the global overrides says disabled is false" do provider_class.stubs(:get_macosx_version_major).returns("10.6") @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => true}]) @provider.class.stubs(:read_plist).returns({@resource[:name] => {"Disabled" => false}}) FileTest.expects(:file?).with(Launchd_Overrides).returns(true) @provider.enabled?.should == :true end it "should return false if the job plist says disabled is false and the global overrides says disabled is true" do provider_class.stubs(:get_macosx_version_major).returns("10.6") @provider.stubs(:plist_from_label).returns(["foo", {"Disabled" => false}]) @provider.class.stubs(:read_plist).returns({@resource[:name] => {"Disabled" => true}}) FileTest.expects(:file?).with(Launchd_Overrides).returns(true) @provider.enabled?.should == :false end it "should return true if the job plist and the global overrides have no disabled keys" do provider_class.stubs(:get_macosx_version_major).returns("10.6") @provider.stubs(:plist_from_label).returns(["foo", {}]) @provider.class.stubs(:read_plist).returns({}) FileTest.expects(:file?).with(Launchd_Overrides).returns(true) @provider.enabled?.should == :true end end describe "when starting the service" do it "should look for the relevant plist once" do @provider.expects(:plist_from_label).once @provider.start end it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do @provider.stubs(:enabled?).returns :true @provider.expects(:execute).with([:launchctl, :load, @resource[:name]]).once @provider.start end it "should execute 'launchctl load' with writing to the plist once if the job is disabled" do @provider.stubs(:enabled?).returns :false @provider.expects(:execute).with([:launchctl, :load, "-w", @resource[:name]]).once @provider.start end it "should disable the job once if the job is disabled and should be disabled at boot" do @provider.stubs(:enabled?).returns :false @resource.stubs(:[]).with(:enable).returns :false @provider.expects(:disable).once @provider.start end end describe "when stopping the service" do it "should look for the relevant plist once" do @provider.expects(:plist_from_label).once @provider.stop end it "should execute 'launchctl unload' once without writing to the plist if the job is disabled" do @provider.stubs(:enabled?).returns :false @provider.expects(:execute).with([:launchctl, :unload, @resource[:name]]).once @provider.stop end it "should execute 'launchctl unload' with writing to the plist once if the job is enabled" do @provider.stubs(:enabled?).returns :true @provider.expects(:execute).with([:launchctl, :unload, "-w", @resource[:name]]).once @provider.stop end it "should enable the job once if the job is enabled and should be enabled at boot" do @provider.stubs(:enabled?).returns :true @resource.stubs(:[]).with(:enable).returns :true @provider.expects(:enable).once @provider.stop end end describe "when enabling the service" do it "should look for the relevant plist once" do @provider.expects(:plist_from_label).once @provider.stop end it "should check if the job is enabled once" do @provider.expects(:enabled?).once @provider.stop end end describe "when disabling the service" do it "should look for the relevant plist once" do @provider.expects(:plist_from_label).once @provider.stop end end describe "when enabling the service on OS X 10.6" do it "should write to the global launchd overrides file once" do provider_class.stubs(:get_macosx_version_major).returns("10.6") @provider.class.stubs(:read_plist).returns({}) Plist::Emit.expects(:save_plist).once @provider.enable end end describe "when disabling the service on OS X 10.6" do it "should write to the global launchd overrides file once" do provider_class.stubs(:get_macosx_version_major).returns("10.6") @provider.class.stubs(:read_plist).returns({}) Plist::Emit.expects(:save_plist).once @provider.enable end end end diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb index a3c6247dd..b7f56e089 100755 --- a/spec/unit/provider/service/redhat_spec.rb +++ b/spec/unit/provider/service/redhat_spec.rb @@ -1,121 +1,121 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the RedHat service Provider # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:redhat) describe provider_class do before :each do @class = Puppet::Type.type(:service).provider(:redhat) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" @provider = provider_class.new @resource.stubs(:provider).returns @provider @provider.resource = @resource @provider.stubs(:get).with(:hasstatus).returns false FileTest.stubs(:file?).with('/sbin/service').returns true FileTest.stubs(:executable?).with('/sbin/service').returns true end # test self.instances describe "when getting all service instances" do before :each do @services = ['one', 'two', 'three', 'four', 'kudzu', 'functions', 'halt', 'killall', 'single', 'linuxconf'] @not_services = ['functions', 'halt', 'killall', 'single', 'linuxconf'] Dir.stubs(:entries).returns @services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) end it "should return instances for all services" do (@services-@not_services).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:path] == '/etc/init.d'}.returns("#{inst}_instance") end results = (@services-@not_services).collect {|x| "#{x}_instance"} @class.instances.should == results end it "should call service status when initialized from provider" do @resource.stubs(:[]).with(:status).returns nil @provider.stubs(:get).with(:hasstatus).returns true @provider.expects(:execute).with{|command, *args| command == ['/sbin/service', 'myservice', 'status']} @provider.send(:status) end end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do @resource.stubs(:[]).with(method).returns "/user/specified/command" @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } @provider.send(method) end it "should execute the service script with #{method} when no explicit command is provided" do @resource.stubs(:[]).with("has#{method}".intern).returns :true @provider.expects(:execute).with { |command, *args| command == ['/sbin/service', 'myservice', method.to_s]} @provider.send(method) end end end describe "when checking status" do describe "when hasstatus is :true" do before :each do @resource.stubs(:[]).with(:hasstatus).returns :true end it "should execute the service script with fail_on_failure false" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) @provider.status end it "should consider the process running if the command returns 0" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.status.should == :running end [-10,-1,1,10].each { |ec| it "should consider the process stopped if the command returns something non-0" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) $CHILD_STATUS.stubs(:exitstatus).returns(ec) @provider.status.should == :stopped end } end describe "when hasstatus is not :true" do it "should consider the service :running if it has a pid" do @provider.expects(:getpid).returns "1234" @provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do @provider.expects(:getpid).returns nil @provider.status.should == :stopped end end end describe "when restarting and hasrestart is not :true" do it "should stop and restart the process with the server script" do @provider.expects(:texecute).with(:stop, ['/sbin/service', 'myservice', 'stop'], true) @provider.expects(:texecute).with(:start, ['/sbin/service', 'myservice', 'start'], true) @provider.restart end end end diff --git a/spec/unit/provider/service/runit_spec.rb b/spec/unit/provider/service/runit_spec.rb index 12dfeeb35..38855a451 100755 --- a/spec/unit/provider/service/runit_spec.rb +++ b/spec/unit/provider/service/runit_spec.rb @@ -1,140 +1,140 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the Runit service Provider # # author Brice Figureau # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:runit) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @servicedir = "/etc/service" @provider.servicedir=@servicedir @daemondir = "/etc/sv" @provider.class.defpath=@daemondir # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, source and path (because we won't run # the thing that will fetch the resource path from the provider) @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:path).returns @daemondir @resource.stubs(:ref).returns "Service[myservice]" @provider.stubs(:sv) @provider.stubs(:resource).returns @resource end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a restartcmd method" do @provider.should respond_to(:restartcmd) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when starting" do it "should enable the service if it is not enabled" do @provider.stubs(:sv) @provider.expects(:enabled?).returns :false @provider.expects(:enable) @provider.start end it "should execute external command 'sv start /etc/service/myservice'" do @provider.stubs(:enabled?).returns :true @provider.expects(:sv).with("start", "/etc/service/myservice") @provider.start end end describe "when stopping" do it "should execute external command 'sv stop /etc/service/myservice'" do @provider.expects(:sv).with("stop", "/etc/service/myservice") @provider.stop end end describe "when restarting" do it "should call 'sv restart /etc/service/myservice'" do @provider.expects(:sv).with("restart","/etc/service/myservice") @provider.restart end end describe "when enabling" do it "should create a symlink between daemon dir and service dir" do FileTest.stubs(:symlink?).returns(false) File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) @provider.enable end end describe "when disabling" do it "should remove the '/etc/service/myservice' symlink" do FileTest.stubs(:directory?).returns(false) FileTest.stubs(:symlink?).returns(true) File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) @provider.disable end end describe "when checking status" do it "should call the external command 'sv status /etc/sv/myservice'" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")) @provider.status end end describe "when checking status" do it "and sv status fails, properly raise a Puppet::Error" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).raises(Puppet::ExecutionFailure, "fail: /etc/sv/myservice: file not found") lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found') end it "and sv status returns up, then return :running" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("run: /etc/sv/myservice: (pid 9029) 6s") @provider.status.should == :running end it "and sv status returns not running, then return :stopped" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("fail: /etc/sv/myservice: runsv not running") @provider.status.should == :stopped end it "and sv status returns a warning, then return :stopped" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist") @provider.status.should == :stopped end end end diff --git a/spec/unit/provider/service/smf_spec.rb b/spec/unit/provider/service/smf_spec.rb index 40e96ab23..5212d540a 100755 --- a/spec/unit/provider/service/smf_spec.rb +++ b/spec/unit/provider/service/smf_spec.rb @@ -1,137 +1,137 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the SMF service Provider # # author Dominic Cleal # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:smf) describe provider_class do before(:each) do # Create a mock resource @resource = Puppet::Type.type(:service).new( :name => "/system/myservice", :ensure => :running, :enable => :true) @provider = provider_class.new(@resource) FileTest.stubs(:file?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:executable?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:file?).with('/usr/bin/svcs').returns true FileTest.stubs(:executable?).with('/usr/bin/svcs').returns true end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a restartcmd method" do @provider.should respond_to(:restartcmd) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when checking status" do it "should call the external command 'svcs /system/myservice' once" do @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.status end it "should return stopped if svcs can't find the service" do @provider.stubs(:svcs).raises(Puppet::ExecutionFailure.new("no svc found")) @provider.status.should == :stopped end it "should return running if online in svcs output" do @provider.stubs(:svcs).returns("online\t-") @provider.status.should == :running end it "should return stopped if disabled in svcs output" do @provider.stubs(:svcs).returns("disabled\t-") @provider.status.should == :stopped end it "should return maintenance if in maintenance in svcs output" do @provider.stubs(:svcs).returns("maintenance\t-") @provider.status.should == :maintenance end it "should return target state if transitioning in svcs output" do @provider.stubs(:svcs).returns("online\tdisabled") @provider.status.should == :stopped end it "should throw error if it's a legacy service in svcs output" do @provider.stubs(:svcs).returns("legacy_run\t-") lambda { @provider.status }.should raise_error(Puppet::Error, "Cannot manage legacy services through SMF") end end describe "when starting" do it "should enable the service if it is not enabled" do @provider.expects(:status).returns :stopped @provider.expects(:texecute) @provider.start end it "should always execute external command 'svcadm enable /system/myservice'" do @provider.stubs(:status).returns :running @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "/system/myservice"], true) @provider.start end it "should execute external command 'svcadm clear /system/myservice' if in maintenance" do @provider.stubs(:status).returns :maintenance @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :clear, "/system/myservice"], true) @provider.start end end describe "when starting a service with a manifest" do before(:each) do @resource = Puppet::Type.type(:service).new(:name => "/system/myservice", :ensure => :running, :enable => :true, :manifest => "/tmp/myservice.xml") @provider = provider_class.new(@resource) $CHILD_STATUS.stubs(:exitstatus).returns(1) end it "should import the manifest if service is missing" do @provider.expects(:svccfg).with(:import, "/tmp/myservice.xml") @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "/system/myservice"], true) @provider.start end it "should handle failures if importing a manifest" do @provider.expects(:svccfg).raises(Puppet::ExecutionFailure.new("can't svccfg import")) lambda { @provider.start }.should raise_error(Puppet::Error, "Cannot config /system/myservice to enable it: can't svccfg import") end end describe "when stopping" do it "should execute external command 'svcadm disable /system/myservice'" do @provider.expects(:texecute).with(:stop, ["/usr/sbin/svcadm", :disable, "/system/myservice"], true) @provider.stop end end describe "when restarting" do it "should call 'svcadm restart /system/myservice'" do @provider.expects(:texecute).with(:restart, ["/usr/sbin/svcadm", :restart, "/system/myservice"], true) @provider.restart end end end diff --git a/spec/unit/provider/service/src_spec.rb b/spec/unit/provider/service/src_spec.rb index dbe073513..17f49994e 100755 --- a/spec/unit/provider/service/src_spec.rb +++ b/spec/unit/provider/service/src_spec.rb @@ -1,97 +1,97 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Unit testing for the AIX System Resource Controller (src) provider # -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:src) describe provider_class do before :each do @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" @provider = provider_class.new @provider.resource = @resource @provider.stubs(:command).with(:stopsrc).returns "/usr/bin/stopsrc" @provider.stubs(:command).with(:startsrc).returns "/usr/bin/startsrc" @provider.stubs(:command).with(:lssrc).returns "/usr/bin/lssrc" @provider.stubs(:command).with(:refresh).returns "/usr/bin/refresh" @provider.stubs(:stopsrc) @provider.stubs(:startsrc) @provider.stubs(:lssrc) @provider.stubs(:refresh) end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end end it "should execute the startsrc command" do @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.start end it "should execute the stopsrc command" do @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.stop end it "should execute status and return running if the subsystem is active" do sample_output = <<_EOF_ Subsystem Group PID Status myservice tcpip 1234 active _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output @provider.status.should == :running end it "should execute status and return stopped if the subsystem is inoperative" do sample_output = <<_EOF_ Subsystem Group PID Status myservice tcpip inoperative _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output @provider.status.should == :stopped end it "should execute status and return nil if the status is not known" do sample_output = <<_EOF_ Subsystem Group PID Status myservice tcpip randomdata _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output @provider.status.should == nil end it "should execute restart which runs refresh" do sample_output = <<_EOF_ #subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: myservice:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip: _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output @provider.expects(:execute).with(['/usr/bin/refresh', '-s', "myservice"]) @provider.restart end it "should execute restart which runs stopsrc then startsrc" do sample_output = <<_EOF_ #subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: myservice::--no-daemonize:/usr/sbin/puppetd:0:0:/dev/null:/var/log/puppet.log:/var/log/puppet.log:-O:-Q:-S:0:0:20:15:9:-d:20::" _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.restart end end diff --git a/spec/unit/provider/service/upstart.rb b/spec/unit/provider/service/upstart.rb old mode 100644 new mode 100755 index 22cf2bdeb..0febc939d --- a/spec/unit/provider/service/upstart.rb +++ b/spec/unit/provider/service/upstart.rb @@ -1,49 +1,48 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:upstart) describe provider_class do describe "#instances" do it "should be able to find all instances" do processes = ["rc stop/waiting", "ssh start/running, process 712"] provider_class.stubs(:execpipe).yields(processes) provider_class.instances.map {|provider| provider.name}.should =~ ["rc","ssh"] end it "should attach the interface name for network interfaces" do processes = ["network-interface (eth0)"] provider_class.stubs(:execpipe).yields(processes) provider_class.instances.first.name.should == "network-interface INTERFACE=eth0" end end describe "#status" do it "should allow the user to override the status command" do resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart, :status => "/bin/foo") provider = provider_class.new(resource) provider.expects(:ucommand).with { `true`; true } provider.status.should == :running end it "should use the default status command if none is specified" do resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) provider = provider_class.new(resource) provider.expects(:status_exec).with(["foo"]).returns("foo start/running, process 1000") Process::Status.any_instance.stubs(:exitstatus).returns(0) provider.status.should == :running end it "should properly handle services with 'start' in their name" do resource = Puppet::Type.type(:service).new(:name => "foostartbar", :provider => :upstart) provider = provider_class.new(resource) provider.expects(:status_exec).with(["foostartbar"]).returns("foostartbar stop/waiting") Process::Status.any_instance.stubs(:exitstatus).returns(0) provider.status.should == :stopped end end end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index 3ac57facc..69d29c674 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -1,212 +1,211 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' require 'puppet_spec/files' provider_class = Puppet::Type.type(:ssh_authorized_key).provider(:parsed) describe provider_class do include PuppetSpec::Files before :each do @sshauthkey_class = Puppet::Type.type(:ssh_authorized_key) @provider = @sshauthkey_class.provider(:parsed) @keyfile = tmpfile('authorized_keys') @provider.any_instance.stubs(:target).returns @keyfile @user = 'random_bob' Puppet::Util.stubs(:uid).with(@user).returns 12345 end after :each do @provider.initvars end def mkkey(args) args[:target] = @keyfile args[:user] = @user resource = Puppet::Type.type(:ssh_authorized_key).new(args) key = @provider.new(resource) args.each do |p,v| key.send(p.to_s + "=", v) end key end def genkey(key) @provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields key.flush @provider.target_object(@keyfile).read end it_should_behave_like "all parsedfile providers", provider_class it "should be able to generate a basic authorized_keys file" do key = mkkey(:name => "Just Testing", :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", :type => "ssh-dss", :ensure => :present, :options => [:absent] ) genkey(key).should == "ssh-dss AAAAfsfddsjldjgksdflgkjsfdlgkj Just Testing\n" end it "should be able to generate a authorized_keys file with options" do key = mkkey(:name => "root@localhost", :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", :type => "ssh-rsa", :ensure => :present, :options => ['from="192.168.1.1"', "no-pty", "no-X11-forwarding"] ) genkey(key).should == "from=\"192.168.1.1\",no-pty,no-X11-forwarding ssh-rsa AAAAfsfddsjldjgksdflgkjsfdlgkj root@localhost\n" end it "should be able to parse options containing commas via its parse_options method" do options = %w{from="host1.reductlivelabs.com,host.reductivelabs.com" command="/usr/local/bin/run" ssh-pty} optionstr = options.join(", ") @provider.parse_options(optionstr).should == options end it "should use '' as name for entries that lack a comment" do line = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAut8aOSxenjOqF527dlsdHWV4MNoAsX14l9M297+SQXaQ5Z3BedIxZaoQthkDALlV/25A1COELrg9J2MqJNQc8Xe9XQOIkBQWWinUlD/BXwoOTWEy8C8zSZPHZ3getMMNhGTBO+q/O+qiJx3y5cA4MTbw2zSxukfWC87qWwcZ64UUlegIM056vPsdZWFclS9hsROVEa57YUMrehQ1EGxT4Z5j6zIopufGFiAPjZigq/vqgcAqhAKP6yu4/gwO6S9tatBeEjZ8fafvj1pmvvIplZeMr96gHE7xS3pEEQqnB3nd4RY7AF6j9kFixnsytAUO7STPh/M3pLiVQBN89TvWPQ==" @provider.parse(line)[0][:name].should == "" end end describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" @resource.class.stubs(:key_attributes).returns( [:name] ) @provider = provider_class.new(@resource) provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) Puppet::Util::SUIDManager.stubs(:asuser).yields end describe "when flushing" do before :each do # Stub file and directory operations Dir.stubs(:mkdir) File.stubs(:chmod) File.stubs(:chown) end describe "and both a user and a target have been specified" do before :each do Puppet::Util.stubs(:uid).with("random_bob").returns 12345 @resource.stubs(:should).with(:user).returns "random_bob" target = "/tmp/.ssh_dir/place_to_put_authorized_keys" @resource.stubs(:should).with(:target).returns target end it "should create the directory" do File.stubs(:exist?).with("/tmp/.ssh_dir").returns false Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700) @provider.flush end it "should chown the directory to the user" do uid = Puppet::Util.uid("random_bob") File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir") @provider.flush end it "should chown the key file to the user" do uid = Puppet::Util.uid("random_bob") File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider.flush end it "should chmod the key file to 0600" do File.expects(:chmod).with(0600, "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider.flush end end describe "and a user has been specified with no target" do before :each do @resource.stubs(:should).with(:user).returns "nobody" @resource.stubs(:should).with(:target).returns nil # # I'd like to use random_bob here and something like # # File.stubs(:expand_path).with("~random_bob/.ssh").returns "/users/r/random_bob/.ssh" # # but mocha objects strenuously to stubbing File.expand_path # so I'm left with using nobody. @dir = File.expand_path("~nobody/.ssh") end it "should create the directory if it doesn't exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).with(@dir,0700) @provider.flush end it "should not create or chown the directory if it already exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never @provider.flush end it "should chown the directory to the user if it creates it" do File.stubs(:exist?).with(@dir).returns false Dir.stubs(:mkdir).with(@dir,0700) uid = Puppet::Util.uid("nobody") File.expects(:chown).with(uid, nil, @dir) @provider.flush end it "should not create or chown the directory if it already exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never File.expects(:chown).never @provider.flush end it "should chown the key file to the user" do uid = Puppet::Util.uid("nobody") File.expects(:chown).with(uid, nil, File.expand_path("~nobody/.ssh/authorized_keys")) @provider.flush end it "should chmod the key file to 0600" do File.expects(:chmod).with(0600, File.expand_path("~nobody/.ssh/authorized_keys")) @provider.flush end end describe "and a target has been specified with no user" do before :each do @resource.stubs(:should).with(:user).returns nil @resource.stubs(:should).with(:target).returns("/tmp/.ssh_dir/place_to_put_authorized_keys") end it "should raise an error" do proc { @provider.flush }.should raise_error end end describe "and a invalid user has been specified with no target" do before :each do @resource.stubs(:should).with(:user).returns "thisusershouldnotexist" @resource.stubs(:should).with(:target).returns nil end it "should catch an exception and raise a Puppet error" do lambda { @provider.flush }.should raise_error(Puppet::Error) end end end end diff --git a/spec/unit/provider/sshkey/parsed_spec.rb b/spec/unit/provider/sshkey/parsed_spec.rb index 7a76b02d6..e66032bc4 100755 --- a/spec/unit/provider/sshkey/parsed_spec.rb +++ b/spec/unit/provider/sshkey/parsed_spec.rb @@ -1,38 +1,37 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:sshkey).provider(:parsed) describe provider_class do before do @sshkey_class = Puppet::Type.type(:sshkey) @provider_class = @sshkey_class.provider(:parsed) @key = 'AAAAB3NzaC1yc2EAAAABIwAAAQEAzwHhxXvIrtfIwrudFqc8yQcIfMudrgpnuh1F3AV6d2BrLgu/yQE7W5UyJMUjfj427sQudRwKW45O0Jsnr33F4mUw+GIMlAAmp9g24/OcrTiB8ZUKIjoPy/cO4coxGi8/NECtRzpD/ZUPFh6OEpyOwJPMb7/EC2Az6Otw4StHdXUYw22zHazBcPFnv6zCgPx1hA7QlQDWTu4YcL0WmTYQCtMUb3FUqrcFtzGDD0ytosgwSd+JyN5vj5UwIABjnNOHPZ62EY1OFixnfqX/+dUwrFSs5tPgBF/KkC6R7tmbUfnBON6RrGEmu+ajOTOLy23qUZB4CQ53V7nyAWhzqSK+hw==' end it "should parse the name from the first field" do @provider_class.parse_line('test ssh-rsa '+@key)[:name].should == "test" end it "should parse the first component of the first field as the name" do @provider_class.parse_line('test,alias ssh-rsa '+@key)[:name].should == "test" end it "should parse host_aliases from the remaining components of the first field" do @provider_class.parse_line('test,alias ssh-rsa '+@key)[:host_aliases].should == ["alias"] end it "should parse multiple host_aliases" do @provider_class.parse_line('test,alias1,alias2,alias3 ssh-rsa '+@key)[:host_aliases].should == ["alias1","alias2","alias3"] end it "should not drop an empty host_alias" do @provider_class.parse_line('test,alias, ssh-rsa '+@key)[:host_aliases].should == ["alias",""] end it "should recognise when there are no host aliases" do @provider_class.parse_line('test ssh-rsa '+@key)[:host_aliases].should == [] end end diff --git a/spec/unit/provider/user/hpux_spec.rb b/spec/unit/provider/user/hpux_spec.rb index b10f2847c..f7779a98d 100755 --- a/spec/unit/provider/user/hpux_spec.rb +++ b/spec/unit/provider/user/hpux_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:hpuxuseradd) describe provider_class do # left from the useradd test... I have no clue what I'm doing. before do @resource = stub("resource", :name => "myuser", :managehome? => nil, :should => "fakeval", :[] => "fakeval") @provider = provider_class.new(@resource) end it "should add -F when modifying a user" do @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-F") } @provider.uid = 1000 end it "should add -F when deleting a user" do @provider.stubs(:exists?).returns(true) @provider.expects(:execute).with { |args| args.include?("-F") } @provider.delete end end diff --git a/spec/unit/provider/user/ldap_spec.rb b/spec/unit/provider/user/ldap_spec.rb index 5b2dc98b5..065b3b423 100755 --- a/spec/unit/provider/user/ldap_spec.rb +++ b/spec/unit/provider/user/ldap_spec.rb @@ -1,279 +1,279 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-10. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:ldap) describe provider_class do it "should have the Ldap provider class as its baseclass" do provider_class.superclass.should equal(Puppet::Provider::Ldap) end it "should manage :posixAccount and :person objectclasses" do provider_class.manager.objectclasses.should == [:posixAccount, :person] end it "should use 'ou=People' as its relative base" do provider_class.manager.location.should == "ou=People" end it "should use :uid as its rdn" do provider_class.manager.rdn.should == :uid end it "should be able to manage passwords" do provider_class.should be_manages_passwords end it "should use the ldap group provider to convert group names to numbers" do provider = provider_class.new(:name => "foo") Puppet::Type.type(:group).provider(:ldap).expects(:name2id).with("bar").returns 10 provider.gid = 'bar' provider.gid.should == 10 end {:name => "uid", :password => "userPassword", :comment => "cn", :uid => "uidNumber", :gid => "gidNumber", :home => "homeDirectory", :shell => "loginShell" }.each do |puppet, ldap| it "should map :#{puppet.to_s} to '#{ldap}'" do provider_class.manager.ldap_name(puppet).should == ldap end end describe "when being created" do before do # So we don't try to actually talk to ldap @connection = mock 'connection' provider_class.manager.stubs(:connect).yields @connection end it "should generate the sn as the last field of the cn" do resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:comment).returns ["Luke Kanies"] resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["sn"] == ["Kanies"] } instance.create instance.flush end describe "with no uid specified" do it "should pick the first available UID after the largest existing UID" do low = {:name=>["luke"], :shell=>:absent, :uid=>["600"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["l k"]} high = {:name=>["testing"], :shell=>:absent, :uid=>["640"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["t u"]} provider_class.manager.expects(:search).returns([low, high]) resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:uid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["641"] } instance.create instance.flush end it "should pick 501 of no users exist" do provider_class.manager.expects(:search).returns nil resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:uid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["501"] } instance.create instance.flush end end end describe "when flushing" do before do provider_class.stubs(:suitable?).returns true @instance = provider_class.new(:name => "myname", :groups => %w{whatever}, :uid => "400") end it "should remove the :groups value before updating" do @instance.class.manager.expects(:update).with { |name, ldap, puppet| puppet[:groups].nil? } @instance.flush end it "should empty the property hash" do @instance.class.manager.stubs(:update) @instance.flush @instance.uid.should == :absent end it "should empty the ldap property hash" do @instance.class.manager.stubs(:update) @instance.flush @instance.ldap_properties[:uid].should be_nil end end describe "when checking group membership" do before do @groups = Puppet::Type.type(:group).provider(:ldap) @group_manager = @groups.manager provider_class.stubs(:suitable?).returns true @instance = provider_class.new(:name => "myname") end it "should show its group membership as the sorted list of all groups returned by an ldap query of group memberships" do one = {:name => "one"} two = {:name => "two"} @group_manager.expects(:search).with("memberUid=myname").returns([two, one]) @instance.groups.should == "one,two" end it "should show its group membership as :absent if no matching groups are found in ldap" do @group_manager.expects(:search).with("memberUid=myname").returns(nil) @instance.groups.should == :absent end it "should cache the group value" do @group_manager.expects(:search).with("memberUid=myname").once.returns nil @instance.groups @instance.groups.should == :absent end end describe "when modifying group membership" do before do @groups = Puppet::Type.type(:group).provider(:ldap) @group_manager = @groups.manager provider_class.stubs(:suitable?).returns true @one = {:name => "one", :gid => "500"} @group_manager.stubs(:find).with("one").returns(@one) @two = {:name => "one", :gid => "600"} @group_manager.stubs(:find).with("two").returns(@two) @instance = provider_class.new(:name => "myname") @instance.stubs(:groups).returns :absent end it "should fail if the group does not exist" do @group_manager.expects(:find).with("mygroup").returns nil lambda { @instance.groups = "mygroup" }.should raise_error(Puppet::Error) end it "should only pass the attributes it cares about to the group manager" do @group_manager.expects(:update).with { |name, attrs| attrs[:gid].nil? } @instance.groups = "one" end it "should always include :ensure => :present in the current values" do @group_manager.expects(:update).with { |name, is, should| is[:ensure] == :present } @instance.groups = "one" end it "should always include :ensure => :present in the desired values" do @group_manager.expects(:update).with { |name, is, should| should[:ensure] == :present } @instance.groups = "one" end it "should always pass the group's original member list" do @one[:members] = %w{yay ness} @group_manager.expects(:update).with { |name, is, should| is[:members] == %w{yay ness} } @instance.groups = "one" end it "should find the group again when resetting its member list, so it has the full member list" do @group_manager.expects(:find).with("one").returns(@one) @group_manager.stubs(:update) @instance.groups = "one" end describe "for groups that have no members" do it "should create a new members attribute with its value being the user's name" do @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{myname} } @instance.groups = "one" end end describe "for groups it is being removed from" do it "should replace the group's member list with one missing the user's name" do @one[:members] = %w{myname a} @two[:members] = %w{myname b} @group_manager.expects(:update).with { |name, is, should| name == "two" and should[:members] == %w{b} } @instance.stubs(:groups).returns "one,two" @instance.groups = "one" end it "should mark the member list as empty if there are no remaining members" do @one[:members] = %w{myname} @two[:members] = %w{myname b} @group_manager.expects(:update).with { |name, is, should| name == "one" and should[:members] == :absent } @instance.stubs(:groups).returns "one,two" @instance.groups = "two" end end describe "for groups that already have members" do it "should replace each group's member list with a new list including the user's name" do @one[:members] = %w{a b} @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } @two[:members] = %w{b c} @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{b c myname} } @instance.groups = "one,two" end end describe "for groups of which it is a member" do it "should do nothing" do @one[:members] = %w{a b} @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } @two[:members] = %w{c myname} @group_manager.expects(:update).with { |name, *other| name == "two" }.never @instance.stubs(:groups).returns "two" @instance.groups = "one,two" end end end end diff --git a/spec/unit/provider/user/user_role_add_spec.rb b/spec/unit/provider/user/user_role_add_spec.rb old mode 100644 new mode 100755 index 12a71d25a..b17ba68c8 --- a/spec/unit/provider/user/user_role_add_spec.rb +++ b/spec/unit/provider/user/user_role_add_spec.rb @@ -1,267 +1,266 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:user_role_add) describe provider_class do before do @resource = stub("resource", :name => "myuser", :managehome? => nil) @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @resource.stubs(:allowdupe?).returns false @provider = provider_class.new(@resource) end describe "when calling command" do before do klass = stub("provider") klass.stubs(:command).with(:foo).returns("userfoo") klass.stubs(:command).with(:role_foo).returns("rolefoo") @provider.stubs(:class).returns(klass) end it "should use the command if not a role and ensure!=role" do @provider.stubs(:is_role?).returns(false) @provider.stubs(:exists?).returns(false) @resource.stubs(:[]).with(:ensure).returns(:present) @provider.command(:foo).should == "userfoo" end it "should use the role command when a role" do @provider.stubs(:is_role?).returns(true) @provider.command(:foo).should == "rolefoo" end it "should use the role command when !exists and ensure=role" do @provider.stubs(:is_role?).returns(false) @provider.stubs(:exists?).returns(false) @resource.stubs(:[]).with(:ensure).returns(:role) @provider.command(:foo).should == "rolefoo" end end describe "when calling transition" do it "should return the type set to whatever is passed in" do @provider.expects(:command).with(:modify).returns("foomod") @provider.transition("bar").include?("type=bar") end end describe "when calling create" do before do @provider.stubs(:password=) end it "should use the add command when the user is not a role" do @provider.stubs(:is_role?).returns(false) @provider.expects(:addcmd).returns("useradd") @provider.expects(:run).at_least_once @provider.create end it "should use transition(normal) when the user is a role" do @provider.stubs(:is_role?).returns(true) @provider.expects(:transition).with("normal") @provider.expects(:run) @provider.create end it "should set password age rules" do @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :user_role_add @provider = provider_class.new(@resource) @provider.stubs(:user_attributes) @provider.stubs(:execute) @provider.expects(:execute).with { |cmd, *args| args == ["-n", 5, "-x", 10, "myuser"] } @provider.create end end describe "when calling destroy" do it "should use the delete command if the user exists and is not a role" do @provider.stubs(:exists?).returns(true) @provider.stubs(:is_role?).returns(false) @provider.expects(:deletecmd) @provider.expects(:run) @provider.destroy end it "should use the delete command if the user is a role" do @provider.stubs(:exists?).returns(true) @provider.stubs(:is_role?).returns(true) @provider.expects(:deletecmd) @provider.expects(:run) @provider.destroy end end describe "when calling create_role" do it "should use the transition(role) if the user exists" do @provider.stubs(:exists?).returns(true) @provider.stubs(:is_role?).returns(false) @provider.expects(:transition).with("role") @provider.expects(:run) @provider.create_role end it "should use the add command when role doesn't exists" do @provider.stubs(:exists?).returns(false) @provider.expects(:addcmd) @provider.expects(:run) @provider.create_role end end describe "when allow duplicate is enabled" do before do @resource.expects(:allowdupe?).returns true @resource.stubs(:system?) @provider.stubs(:is_role?).returns(false) @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } end it "should add -o when the user is being created" do @provider.stubs(:password=) @provider.create end it "should add -o when the uid is being modified" do @provider.uid = 150 end end [:roles, :auths, :profiles].each do |val| describe "when getting #{val}" do it "should get the user_attributes" do @provider.expects(:user_attributes) @provider.send(val) end it "should get the #{val} attribute" do attributes = mock("attributes") attributes.expects(:[]).with(val) @provider.stubs(:user_attributes).returns(attributes) @provider.send(val) end end end describe "when getting the keys" do it "should get the user_attributes" do @provider.expects(:user_attributes) @provider.keys end it "should call removed_managed_attributes" do @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) @provider.expects(:remove_managed_attributes) @provider.keys end it "should removed managed attribute (type, auths, roles, etc)" do @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) @provider.keys.should == { :foo => "something" } end end describe "when adding properties" do it "should call build_keys_cmd" do @resource.stubs(:should).returns "" @resource.expects(:should).with(:keys).returns({ :foo => "bar" }) @provider.expects(:build_keys_cmd).returns([]) @provider.add_properties end it "should add the elements of the keys hash to an array" do @resource.stubs(:should).returns "" @resource.expects(:should).with(:keys).returns({ :foo => "bar"}) @provider.add_properties.must == ["-K", "foo=bar"] end end describe "when calling build_keys_cmd" do it "should build cmd array with keypairs seperated by -K ending with user" do @provider.build_keys_cmd({"foo" => "bar", "baz" => "boo"}).should.eql? ["-K", "foo=bar", "-K", "baz=boo"] end end describe "when setting the keys" do before do @provider.stubs(:is_role?).returns(false) end it "should run a command" do @provider.expects(:run) @provider.keys=({}) end it "should build the command" do @resource.stubs(:[]).with(:name).returns("someuser") @provider.stubs(:command).returns("usermod") @provider.expects(:build_keys_cmd).returns(["-K", "foo=bar"]) @provider.expects(:run).with(["usermod", "-K", "foo=bar", "someuser"], "modify attribute key pairs") @provider.keys=({}) end end describe "when getting the hashed password" do before do @array = mock "array" end it "should readlines of /etc/shadow" do File.expects(:readlines).with("/etc/shadow").returns([]) @provider.password end it "should reject anything that doesn't start with alpha numerics" do @array.expects(:reject).returns([]) File.stubs(:readlines).with("/etc/shadow").returns(@array) @provider.password end it "should collect splitting on ':'" do @array.stubs(:reject).returns(@array) @array.expects(:collect).returns([]) File.stubs(:readlines).with("/etc/shadow").returns(@array) @provider.password end it "should find the matching user" do @resource.stubs(:[]).with(:name).returns("username") @array.stubs(:reject).returns(@array) @array.stubs(:collect).returns([["username", "hashedpassword"], ["someoneelse", "theirpassword"]]) File.stubs(:readlines).with("/etc/shadow").returns(@array) @provider.password.must == "hashedpassword" end it "should get the right password" do @resource.stubs(:[]).with(:name).returns("username") File.stubs(:readlines).with("/etc/shadow").returns(["#comment", " nonsense", " ", "username:hashedpassword:stuff:foo:bar:::", "other:pword:yay:::"]) @provider.password.must == "hashedpassword" end end describe "when setting the password" do #how can you mock these blocks up? it "should open /etc/shadow for reading and /etc/shadow_tmp for writing" do File.expects(:open).with("/etc/shadow", "r") File.stubs(:rename) @provider.password=("hashedpassword") end it "should rename the /etc/shadow_tmp to /etc/shadow" do File.stubs(:open).with("/etc/shadow", "r") File.expects(:rename).with("/etc/shadow_tmp", "/etc/shadow") @provider.password=("hashedpassword") end end describe "#shadow_entry" do it "should return the line for the right user" do File.stubs(:readlines).returns(["someuser:!:10:5:20:7:1::\n", "fakeval:*:20:10:30:7:2::\n", "testuser:*:30:15:40:7:3::\n"]) @provider.shadow_entry.should == ["fakeval", "*", "20", "10", "30", "7", "2"] end end end diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index b1719eeaf..724fc12c0 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -1,216 +1,215 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:useradd) describe provider_class do before do @resource = stub("resource", :name => "myuser", :managehome? => nil) @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @provider = provider_class.new(@resource) end # #1360 it "should add -o when allowdupe is enabled and the user is being created" do @resource.expects(:allowdupe?).returns true @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } @provider.create end it "should add -o when allowdupe is enabled and the uid is being modified" do @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.uid = 150 end it "should add -r when system is enabled" do @resource.expects(:allowdupe?).returns true @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-r") } @provider.create end it "should set password age rules" do provider_class.has_feature :manages_password_age @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :useradd @provider = provider_class.new(@resource) @provider.stubs(:execute) @provider.expects(:execute).with { |cmd, *args| args == ["-m", 5, "-M", 10, "myuser"] } @provider.create end describe "when checking to add allow dup" do it "should check allow dup" do @resource.expects(:allowdupe?) @provider.check_allow_dup end it "should return an array with a flag if dup is allowed" do @resource.stubs(:allowdupe?).returns true @provider.check_allow_dup.must == ["-o"] end it "should return an empty array if no dup is allowed" do @resource.stubs(:allowdupe?).returns false @provider.check_allow_dup.must == [] end end describe "when checking to add system users" do it "should check system users" do @resource.expects(:system?) @provider.check_system_users end it "should return an array with a flag if it's a system user" do @resource.stubs(:system?).returns true @provider.check_system_users.must == ["-r"] end it "should return an empty array if it's not a system user" do @resource.stubs(:system?).returns false @provider.check_system_users.must == [] end end describe "when checking manage home" do it "should check manage home" do @resource.expects(:managehome?) @provider.check_manage_home end it "should return an array with -m flag if home is managed" do @resource.stubs(:managehome?).returns true @provider.check_manage_home.must == ["-m"] end it "should return an array with -M if home is not managed and on Redhat" do Facter.stubs(:value).with("operatingsystem").returns("RedHat") @resource.stubs(:managehome?).returns false @provider.check_manage_home.must == ["-M"] end it "should return an empty array if home is not managed and not on Redhat" do Facter.stubs(:value).with("operatingsystem").returns("some OS") @resource.stubs(:managehome?).returns false @provider.check_manage_home.must == [] end end describe "when adding properties" do it "should get the valid properties" it "should not add the ensure property" it "should add the flag and value to an array" it "should return and array of flags and values" end describe "when calling addcmd" do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true @resource.stubs(:system?).returns true end it "should call command with :add" do @provider.expects(:command).with(:add) @provider.addcmd end it "should add properties" do @provider.expects(:add_properties).returns([]) @provider.addcmd end it "should check and add if dup allowed" do @provider.expects(:check_allow_dup).returns([]) @provider.addcmd end it "should check and add if home is managed" do @provider.expects(:check_manage_home).returns([]) @provider.addcmd end it "should add the resource :name" do @resource.expects(:[]).with(:name) @provider.addcmd end it "should return an array with -r if system? is true" do resource = Puppet::Type.type(:user).new( :name => "bob", :system => true) provider_class.new( resource ).addcmd.should include("-r") end it "should return an array without -r if system? is false" do resource = Puppet::Type.type(:user).new( :name => "bob", :system => false) provider_class.new( resource ).addcmd.should_not include("-r") end it "should return an array with full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns("somedate") @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", '-e somedate', "-r", "someuser"] end it "should return an array without -e if expiry is undefined full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns nil @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "-r", "someuser"] end end describe "when calling passcmd" do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true @resource.stubs(:system?).returns true end it "should call command with :pass" do @provider.expects(:command).with(:password) @provider.passcmd end it "should return nil if neither min nor max is set" do @resource.stubs(:should).with(:password_min_age).returns nil @resource.stubs(:should).with(:password_max_age).returns nil @provider.passcmd.must == nil end it "should return a chage command array with -m and the user name if password_min_age is set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns 123 @resource.stubs(:should).with(:password_max_age).returns nil @provider.passcmd.must == ['chage','-m',123,'someuser'] end it "should return a chage command array with -M if password_max_age is set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns nil @resource.stubs(:should).with(:password_max_age).returns 999 @provider.passcmd.must == ['chage','-M',999,'someuser'] end it "should return a chage command array with -M -m if both password_min_age and password_max_age are set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns 123 @resource.stubs(:should).with(:password_max_age).returns 999 @provider.passcmd.must == ['chage','-m',123,'-M',999,'someuser'] end end end diff --git a/spec/unit/provider/vlan/cisco_spec.rb b/spec/unit/provider/vlan/cisco_spec.rb new file mode 100755 index 000000000..bb243a75e --- /dev/null +++ b/spec/unit/provider/vlan/cisco_spec.rb @@ -0,0 +1,62 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/vlan/cisco' + +provider_class = Puppet::Type.type(:vlan).provider(:cisco) + +describe provider_class do + before do + @resource = stub("resource", :name => "200") + @provider = provider_class.new(@resource) + end + + it "should have a parent of Puppet::Provider::NetworkDevice" do + provider_class.should < Puppet::Provider::NetworkDevice + end + + it "should have an instances method" do + provider_class.should respond_to(:instances) + end + + describe "when looking up instances at prefetch" do + before do + @device = stub_everything 'device' + Puppet::Util::NetworkDevice::Cisco::Device.stubs(:new).returns(@device) + @device.stubs(:command).yields(@device) + end + + it "should initialize the network device with the given url" do + Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).with(:url).returns(@device) + provider_class.lookup(:url, "200") + end + + it "should delegate to the device vlans" do + @device.expects(:parse_vlans) + provider_class.lookup("", "200") + end + + it "should return only the given vlan" do + @device.expects(:parse_vlans).returns({"200" => { :description => "thisone" }, "1" => { :description => "nothisone" }}) + provider_class.lookup("", "200").should == {:description => "thisone" } + end + + end + + describe "when an instance is being flushed" do + it "should call the device update_vlan method with its vlan id, current attributes, and desired attributes" do + @instance = provider_class.new(:ensure => :present, :name => "200", :description => "myvlan") + @instance.description = "myvlan2" + @instance.resource = @resource + @resource.stubs(:[]).with(:name).returns("200") + device = stub_everything 'device' + @instance.stubs(:device).returns(device) + device.expects(:command).yields(device) + device.expects(:update_vlan).with(@instance.name, {:ensure => :present, :name => "200", :description => "myvlan"}, + {:ensure => :present, :name => "200", :description => "myvlan2"}) + + @instance.flush + end + end +end diff --git a/spec/unit/provider/zfs/solaris_spec.rb b/spec/unit/provider/zfs/solaris_spec.rb index 84a2be9f6..8a0cd23b1 100755 --- a/spec/unit/provider/zfs/solaris_spec.rb +++ b/spec/unit/provider/zfs/solaris_spec.rb @@ -1,100 +1,99 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:zfs).provider(:solaris) describe provider_class do before do @resource = stub("resource", :name => "myzfs") @resource.stubs(:[]).with(:name).returns "myzfs" @resource.stubs(:[]).returns "shouldvalue" @provider = provider_class.new(@resource) end it "should have a create method" do @provider.should respond_to(:create) end it "should have a destroy method" do @provider.should respond_to(:destroy) end it "should have an exists? method" do @provider.should respond_to(:exists?) end describe "when calling add_properties" do it "should add -o and the key=value for each properties with a value" do @resource.stubs(:[]).with(:quota).returns "" @resource.stubs(:[]).with(:refquota).returns "" @resource.stubs(:[]).with(:mountpoint).returns "/foo" properties = @provider.add_properties properties.include?("-o").should == true properties.include?("mountpoint=/foo").should == true properties.detect { |a| a.include?("quota") }.should == nil end end describe "when calling create" do it "should call add_properties" do @provider.stubs(:zfs) @provider.expects(:add_properties).returns([]) @provider.create end it "should call zfs with create, properties and this zfs" do @provider.stubs(:add_properties).returns(%w{a b}) @provider.expects(:zfs).with(:create, "a", "b", @resource[:name]) @provider.create end end describe "when calling destroy" do it "should call zfs with :destroy and this zfs" do @provider.expects(:zfs).with(:destroy, @resource[:name]) @provider.destroy end end describe "when calling exist?" do it "should call zfs with :list" do #return stuff because we have to slice and dice it @provider.expects(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\nmyzfs 100K 27.4M /myzfs") @provider.exists? end it "should return true if returned values match the name" do @provider.stubs(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\n#{@resource[:name]} 100K 27.4M /myzfs") @provider.exists?.should == true end it "should return false if returned values don't match the name" do @provider.stubs(:zfs).with(:list).returns("no soup for you") @provider.exists?.should == false end end [:mountpoint, :recordsize, :aclmode, :aclinherit, :primarycache, :secondarycache, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |prop| describe "when getting the #{prop} value" do it "should call zfs with :get, #{prop} and this zfs" do @provider.expects(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") @provider.send(prop) end it "should get the third value of the second line from the output" do @provider.stubs(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") @provider.send(prop).should == "value" end end describe "when setting the #{prop} value" do it "should call zfs with :set, #{prop}=value and this zfs" do @provider.expects(:zfs).with(:set, "#{prop}=value", @resource[:name]) @provider.send("#{prop}=".intern, "value") end end end end diff --git a/spec/unit/provider/zone/solaris_spec.rb b/spec/unit/provider/zone/solaris_spec.rb index 4a1d03272..17ec8f68f 100755 --- a/spec/unit/provider/zone/solaris_spec.rb +++ b/spec/unit/provider/zone/solaris_spec.rb @@ -1,55 +1,54 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:zone).provider(:solaris) describe provider_class do before do @resource = stub("resource", :name => "mypool") @resource.stubs(:[]).returns "shouldvalue" @provider = provider_class.new(@resource) end describe "when calling configure" do it "should add the create args to the create str" do @resource.stubs(:properties).returns([]) @resource.stubs(:[]).with(:create_args).returns("create_args") @provider.expects(:setconfig).with("create -b create_args\nset zonepath=shouldvalue\ncommit\n") @provider.configure end end describe "when installing" do it "should call zoneadm" do @provider.expects(:zoneadm) @provider.install end describe "when cloning" do before { @resource.stubs(:[]).with(:clone).returns(:clone_argument) } it "sohuld clone with the resource's clone attribute" do @provider.expects(:zoneadm).with(:clone, :clone_argument) @provider.install end end describe "when not cloning" do before { @resource.stubs(:[]).with(:clone).returns(nil)} it "should just install if there are no install args" do @resource.stubs(:[]).with(:install_args).returns(nil) @provider.expects(:zoneadm).with(:install) @provider.install end it "should add the install args to the command if they exist" do @resource.stubs(:[]).with(:install_args).returns("install args") @provider.expects(:zoneadm).with(:install, ["install", "args"]) @provider.install end end end end diff --git a/spec/unit/provider/zpool/solaris_spec.rb b/spec/unit/provider/zpool/solaris_spec.rb index e2a0e16c8..7e3048a7a 100755 --- a/spec/unit/provider/zpool/solaris_spec.rb +++ b/spec/unit/provider/zpool/solaris_spec.rb @@ -1,209 +1,208 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' provider_class = Puppet::Type.type(:zpool).provider(:solaris) describe provider_class do before do @resource = stub("resource", :name => "mypool") @resource.stubs(:[]).returns "shouldvalue" @provider = provider_class.new(@resource) end describe "when getting the instance" do it "should call process_zpool_data with the result of get_pool_data only once" do @provider.stubs(:get_pool_data).returns(["foo", "disk"]) @provider.expects(:process_zpool_data).with(["foo", "disk"]).returns("stuff").once @provider.current_pool @provider.current_pool end end describe "when calling flush" do it "should need to reload the pool" do @provider.stubs(:get_pool_data) @provider.expects(:process_zpool_data).returns("stuff").times(2) @provider.current_pool @provider.flush @provider.current_pool end end describe "when procesing zpool data" do before do @zpool_data = ["foo", "disk"] end describe "when there is no data" do it "should return a hash with ensure=>:absent" do @provider.process_zpool_data([])[:ensure].should == :absent end end describe "when there is a spare" do it "should add the spare disk to the hash" do @zpool_data += ["spares", "spare_disk"] @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk"] end end describe "when there are two spares" do it "should add the spare disk to the hash as a single string" do @zpool_data += ["spares", "spare_disk", "spare_disk2"] @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] end end describe "when there is a log" do it "should add the log disk to the hash" do @zpool_data += ["logs", "log_disk"] @provider.process_zpool_data(@zpool_data)[:log].should == ["log_disk"] end end describe "when there are two logs" do it "should add the log disks to the hash as a single string" do @zpool_data += ["spares", "spare_disk", "spare_disk2"] @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] end end describe "when the vdev is a single mirror" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2"] end end describe "when the vdev is a single mirror on solaris 10u9 or later" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror-0", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2"] end end describe "when the vdev is a double mirror" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2", "mirror", "disk3", "disk4"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"] end end describe "when the vdev is a double mirror on solaris 10u9 or later" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror-0", "disk1", "disk2", "mirror-1", "disk3", "disk4"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"] end end describe "when the vdev is a raidz1" do it "should call create_multi_array with raidz1" do @zpool_data = ["mirrorpool", "raidz1", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"] end end describe "when the vdev is a raidz1 on solaris 10u9 or later" do it "should call create_multi_array with raidz1" do @zpool_data = ["mirrorpool", "raidz1-0", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"] end end describe "when the vdev is a raidz2" do it "should call create_multi_array with raidz2 and set the raid_parity" do @zpool_data = ["mirrorpool", "raidz2", "disk1", "disk2"] pool = @provider.process_zpool_data(@zpool_data) pool[:raidz].should == ["disk1 disk2"] pool[:raid_parity].should == "raidz2" end end describe "when the vdev is a raidz2 on solaris 10u9 or later" do it "should call create_multi_array with raidz2 and set the raid_parity" do @zpool_data = ["mirrorpool", "raidz2-0", "disk1", "disk2"] pool = @provider.process_zpool_data(@zpool_data) pool[:raidz].should == ["disk1 disk2"] pool[:raid_parity].should == "raidz2" end end end describe "when calling the getters and setters" do [:disk, :mirror, :raidz, :log, :spare].each do |field| describe "when calling #{field}" do it "should get the #{field} value from the current_pool hash" do pool_hash = mock "pool hash" pool_hash.expects(:[]).with(field) @provider.stubs(:current_pool).returns(pool_hash) @provider.send(field) end end describe "when setting the #{field}" do it "should warn the #{field} values were not in sync" do Puppet.expects(:warning).with("NO CHANGES BEING MADE: zpool #{field} does not match, should be 'shouldvalue' currently is 'currentvalue'") @provider.stubs(:current_pool).returns(Hash.new("currentvalue")) @provider.send((field.to_s + "=").intern, "shouldvalue") end end end end describe "when calling create" do before do @resource.stubs(:[]).with(:pool).returns("mypool") @provider.stubs(:zpool) end it "should call build_vdevs" do @provider.expects(:build_vdevs).returns([]) @provider.create end it "should call build_named with 'spares' and 'log" do @provider.expects(:build_named).with("spare").returns([]) @provider.expects(:build_named).with("log").returns([]) @provider.create end it "should call zpool with arguments from build_vdevs and build_named" do @provider.expects(:zpool).with(:create, 'mypool', 'shouldvalue', 'spare', 'shouldvalue', 'log', 'shouldvalue') @provider.create end end describe "when calling delete" do it "should call zpool with destroy and the pool name" do @resource.stubs(:[]).with(:pool).returns("poolname") @provider.expects(:zpool).with(:destroy, "poolname") @provider.delete end end describe "when calling exists?" do before do @current_pool = Hash.new(:absent) @provider.stubs(:get_pool_data).returns([]) @provider.stubs(:process_zpool_data).returns(@current_pool) end it "should get the current pool" do @provider.expects(:process_zpool_data).returns(@current_pool) @provider.exists? end it "should return false if the current_pool is absent" do #the before sets it up @provider.exists?.should == false end it "should return true if the current_pool has values" do @current_pool[:pool] = "mypool" @provider.exists?.should == true end end end diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 24f5216fe..4eb5e12de 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -1,31 +1,30 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Provider do it "should have a specifity class method" do Puppet::Provider.should respond_to(:specificity) end it "should consider two defaults to be higher specificity than one default" do one = Class.new(Puppet::Provider) one.initvars one.defaultfor :operatingsystem => "solaris" two = Class.new(Puppet::Provider) two.initvars two.defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" two.specificity.should > one.specificity end it "should consider a subclass more specific than its parent class" do one = Class.new(Puppet::Provider) one.initvars two = Class.new(one) two.initvars two.specificity.should > one.specificity end end diff --git a/spec/unit/puppet/provider/README.markdown b/spec/unit/puppet/provider/README.markdown new file mode 100644 index 000000000..702585021 --- /dev/null +++ b/spec/unit/puppet/provider/README.markdown @@ -0,0 +1,4 @@ +Provider Specs +============== + +Define specs for your providers under this directory. diff --git a/spec/unit/puppet/type/README.markdown b/spec/unit/puppet/type/README.markdown new file mode 100644 index 000000000..1ee19ac84 --- /dev/null +++ b/spec/unit/puppet/type/README.markdown @@ -0,0 +1,4 @@ +Resource Type Specs +=================== + +Define specs for your resource types in this directory. diff --git a/spec/unit/puppet_spec.rb b/spec/unit/puppet_spec.rb index 28dbd5104..50d3a4718 100755 --- a/spec/unit/puppet_spec.rb +++ b/spec/unit/puppet_spec.rb @@ -1,12 +1,12 @@ -#!/usr/bin/env ruby" +#!/usr/bin/env rspec" -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet' describe Puppet do Puppet::Util::Log.eachlevel do |level| it "should have a method for sending '#{level}' logs" do Puppet.should respond_to(level) end end end diff --git a/spec/unit/rails/host_spec.rb b/spec/unit/rails/host_spec.rb index e83135c91..df0b2fa1d 100755 --- a/spec/unit/rails/host_spec.rb +++ b/spec/unit/rails/host_spec.rb @@ -1,161 +1,160 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe "Puppet::Rails::Host", :if => Puppet.features.rails? do def column(name, type) ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) end before do require 'puppet/rails/host' # Stub this so we don't need access to the DB. Puppet::Rails::Host.stubs(:columns).returns([column("name", "string"), column("environment", "string"), column("ip", "string")]) @node = Puppet::Node.new("foo") @node.environment = "production" @node.ipaddress = "127.0.0.1" @host = stub 'host', :environment= => nil, :ip= => nil end describe "when converting a Puppet::Node instance into a Rails instance" do it "should modify any existing instance in the database" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host Puppet::Rails::Host.from_puppet(@node) end it "should create a new instance in the database if none can be found" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil Puppet::Rails::Host.expects(:new).with(:name => "foo").returns @host Puppet::Rails::Host.from_puppet(@node) end it "should copy the environment from the Puppet instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @node.environment = "production" @host.expects(:environment=).with {|x| x.name.to_s == 'production' } Puppet::Rails::Host.from_puppet(@node) end it "should copy the ipaddress from the Puppet instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @node.ipaddress = "192.168.0.1" @host.expects(:ip=).with "192.168.0.1" Puppet::Rails::Host.from_puppet(@node) end it "should not save the Rails instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @host.expects(:save).never Puppet::Rails::Host.from_puppet(@node) end end describe "when converting a Puppet::Rails::Host instance into a Puppet::Node instance" do before do @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @node = Puppet::Node.new("foo") Puppet::Node.stubs(:new).with("foo").returns @node end it "should create a new instance with the correct name" do Puppet::Node.expects(:new).with("foo").returns @node @host.to_puppet end it "should copy the environment from the Rails instance" do @host.environment = "prod" @node.expects(:environment=).with "prod" @host.to_puppet end it "should copy the ipaddress from the Rails instance" do @host.ip = "192.168.0.1" @node.expects(:ipaddress=).with "192.168.0.1" @host.to_puppet end end describe "when merging catalog resources and database resources" do before :each do Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(false) @resource1 = stub_everything 'res1' @resource2 = stub_everything 'res2' @resources = [ @resource1, @resource2 ] @dbresource1 = stub_everything 'dbres1' @dbresource2 = stub_everything 'dbres2' @dbresources = { 1 => @dbresource1, 2 => @dbresource2 } @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @host.stubs(:find_resources).returns(@dbresources) @host.stubs(:find_resources_parameters_tags) @host.stubs(:compare_to_catalog) @host.stubs(:id).returns(1) end it "should find all database resources" do @host.expects(:find_resources) @host.merge_resources(@resources) end it "should find all paramaters and tags for those database resources" do @host.expects(:find_resources_parameters_tags).with(@dbresources) @host.merge_resources(@resources) end it "should compare all database resources to catalog" do @host.expects(:compare_to_catalog).with(@dbresources, @resources) @host.merge_resources(@resources) end it "should compare only exported resources in thin_storeconfigs mode" do Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(true) @resource1.stubs(:exported?).returns(true) @host.expects(:compare_to_catalog).with(@dbresources, [ @resource1 ]) @host.merge_resources(@resources) end end describe "when searching the database for host resources" do before :each do Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(false) @resource1 = stub_everything 'res1', :id => 1 @resource2 = stub_everything 'res2', :id => 2 @resources = [ @resource1, @resource2 ] @dbresources = stub 'resources' @dbresources.stubs(:find).returns(@resources) @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @host.stubs(:resources).returns(@dbresources) end it "should return a hash keyed by id of all resources" do @host.find_resources.should == { 1 => @resource1, 2 => @resource2 } end it "should return a hash keyed by id of only exported resources in thin_storeconfigs mode" do Puppet.settings.stubs(:[]).with(:thin_storeconfigs).returns(true) @dbresources.expects(:find).with { |*h| h[1][:conditions] == { :exported => true } }.returns([]) @host.find_resources end end end diff --git a/spec/unit/rails/param_value_spec.rb b/spec/unit/rails/param_value_spec.rb index 9a725cfc8..7f0086252 100755 --- a/spec/unit/rails/param_value_spec.rb +++ b/spec/unit/rails/param_value_spec.rb @@ -1,48 +1,47 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/rails' describe "Puppet::Rails::ParamValue", :if => Puppet.features.rails? do def column(name, type) ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) end before do require 'puppet/rails/param_value' name = stub 'param_name', :name => "foo" # Stub this so we don't need access to the DB. Puppet::Rails::ParamValue.stubs(:columns).returns([column("value", "string")]) Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(name) end describe "when creating initial parameter values" do it "should return an array of hashes" do Puppet::Rails::ParamValue.from_parser_param(:myparam, %w{a b})[0].should be_instance_of(Hash) end it "should return hashes for each value with the parameter name set as the ParamName instance" do name = stub 'param_name', :name => "foo" Puppet::Rails::ParamName.expects(:find_or_create_by_name).returns(name) result = Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0] result[:value].should == "a" result[:param_name].should == name end it "should return an array of hashes even when only one parameter is provided" do Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0].should be_instance_of(Hash) end it "should convert all arguments into strings" do Puppet::Rails::ParamValue.from_parser_param(:myparam, 50)[0][:value].should == "50" end it "should not convert Resource References into strings" do ref = Puppet::Resource.new(:file, "/file") Puppet::Rails::ParamValue.from_parser_param(:myparam, ref)[0][:value].should == ref end end end diff --git a/spec/unit/rails/resource_spec.rb b/spec/unit/rails/resource_spec.rb index 3fbbbc7b7..22e5267f4 100755 --- a/spec/unit/rails/resource_spec.rb +++ b/spec/unit/rails/resource_spec.rb @@ -1,122 +1,121 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/rails' describe "Puppet::Rails::Resource", :if => Puppet.features.rails? do def column(name, type) ActiveRecord::ConnectionAdapters::Column.new(name, nil, type, false) end before do require 'puppet/rails/resource' # Stub this so we don't need access to the DB. Puppet::Rails::Resource.stubs(:columns).returns([column("title", "string"), column("restype", "string"), column("exported", "boolean")]) end describe "when creating initial resource arguments" do it "should set the restype to the resource's type" do Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:restype].should == "File" end it "should set the title to the resource's title" do Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:title].should == "/file" end it "should set the line to the resource's line if one is available" do resource = Puppet::Resource.new(:file, "/file") resource.line = 50 Puppet::Rails::Resource.rails_resource_initial_args(resource)[:line].should == 50 end it "should set 'exported' to true of the resource is exported" do resource = Puppet::Resource.new(:file, "/file") resource.exported = true Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_true end it "should set 'exported' to false of the resource is not exported" do resource = Puppet::Resource.new(:file, "/file") resource.exported = false Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false resource = Puppet::Resource.new(:file, "/file") resource.exported = nil Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false end end describe "when merging in a parser resource" do before do @parser = mock 'parser resource' @resource = Puppet::Rails::Resource.new [:merge_attributes, :merge_parameters, :merge_tags, :save].each { |m| @resource.stubs(m) } end it "should merge the attributes" do @resource.expects(:merge_attributes).with(@parser) @resource.merge_parser_resource(@parser) end it "should merge the parameters" do @resource.expects(:merge_parameters).with(@parser) @resource.merge_parser_resource(@parser) end it "should merge the tags" do @resource.expects(:merge_tags).with(@parser) @resource.merge_parser_resource(@parser) end it "should save itself" do @resource.expects(:save) @resource.merge_parser_resource(@parser) end end describe "merge_parameters" do it "should replace values that have changed" do @resource = Puppet::Rails::Resource.new @resource.params_list = [{"name" => "replace", "value" => 1, "id" => 100 }] Puppet::Rails::ParamValue.expects(:delete).with([100]) param_values = stub "param_values" param_values.expects(:build).with({:value=>nil, :param_name=>nil, :line=>{"replace"=>2}}) @resource.stubs(:param_values).returns(param_values) Puppet::Rails::ParamName.stubs(:accumulate_by_name) merge_resource = stub "merge_resource" merge_resource.expects(:line).returns({ "replace" => 2 }) merge_resource.stubs(:each).yields([["replace", 2]]) @resource.merge_parameters(merge_resource) end end describe "#to_resource" do it "should instantiate a Puppet::Parser::Resource" do scope = stub "scope", :source => nil, :environment => nil, :namespaces => nil @resource = Puppet::Rails::Resource.new @resource.stubs(:attributes).returns({ "restype" => 'notify', "title" => 'hello' }) @resource.stubs(:param_names).returns([]) @resource.to_resource(scope).should be_a(Puppet::Parser::Resource) end end end diff --git a/spec/unit/rails_spec.rb b/spec/unit/rails_spec.rb index a08485e4c..fe7fd8e29 100755 --- a/spec/unit/rails_spec.rb +++ b/spec/unit/rails_spec.rb @@ -1,314 +1,313 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/rails' describe Puppet::Rails, "when initializing any connection", :if => Puppet.features.rails? do before do Puppet.settings.stubs(:use) @logger = mock 'logger' @logger.stub_everything Logger.stubs(:new).returns(@logger) ActiveRecord::Base.stubs(:logger).returns(@logger) ActiveRecord::Base.stubs(:connected?).returns(false) end it "should use settings" do Puppet.settings.expects(:use).with(:main, :rails, :master) Puppet::Rails.connect end it "should set up a logger with the appropriate Rails log file" do logger = mock 'logger' Logger.expects(:new).with(Puppet[:railslog]).returns(logger) ActiveRecord::Base.expects(:logger=).with(logger) Puppet::Rails.connect end it "should set the log level to whatever the value is in the settings" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).with(:rails_loglevel).returns("debug") Puppet.settings.stubs(:value).with(:railslog).returns("/my/file") logger = mock 'logger' Logger.stubs(:new).returns(logger) ActiveRecord::Base.stubs(:logger).returns(logger) logger.expects(:level=).with(Logger::DEBUG) ActiveRecord::Base.stubs(:allow_concurrency=) ActiveRecord::Base.stubs(:verify_active_connections!) ActiveRecord::Base.stubs(:establish_connection) Puppet::Rails.stubs(:database_arguments).returns({}) Puppet::Rails.connect end describe "ActiveRecord Version" do it "should set ActiveRecord::Base.allow_concurrency if ActiveRecord is 2.1" do Puppet::Util.stubs(:activerecord_version).returns(2.1) ActiveRecord::Base.expects(:allow_concurrency=).with(true) Puppet::Rails.connect end it "should not set ActiveRecord::Base.allow_concurrency if ActiveRecord is >= 2.2" do Puppet::Util.stubs(:activerecord_version).returns(2.2) ActiveRecord::Base.expects(:allow_concurrency=).never Puppet::Rails.connect end end it "should call ActiveRecord::Base.verify_active_connections!" do ActiveRecord::Base.expects(:verify_active_connections!) Puppet::Rails.connect end it "should call ActiveRecord::Base.establish_connection with database_arguments" do Puppet::Rails.expects(:database_arguments).returns({}) ActiveRecord::Base.expects(:establish_connection) Puppet::Rails.connect end end describe Puppet::Rails, "when initializing a sqlite3 connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and database arguments" do Puppet.settings.expects(:value).with(:dbadapter).returns("sqlite3") Puppet.settings.expects(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.expects(:value).with(:dblocation).returns("testlocation") Puppet::Rails.database_arguments.should == { :adapter => "sqlite3", :log_level => "testlevel", :database => "testlocation" } end end describe Puppet::Rails, "when initializing a mysql connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and host, port, username, password, database, and reconnect arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 45).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("") Puppet::Rails.database_arguments.should == { :adapter => "mysql", :log_level => "testlevel", :host => "testserver", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :reconnect => true } end it "should provide the adapter, log_level, and host, port, username, password, database, socket, connections, and reconnect arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 12).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet::Rails.database_arguments.should == { :adapter => "mysql", :log_level => "testlevel", :host => "testserver", :port => "9999", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :socket => "testsocket", :reconnect => true } end it "should provide the adapter, log_level, and host, port, username, password, database, socket, and connections arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 23).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet::Rails.database_arguments.should == { :adapter => "mysql", :log_level => "testlevel", :host => "testserver", :port => "9999", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :socket => "testsocket", :reconnect => true } end it "should not provide the pool if dbconnections is 0, '0', or ''" do Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet.settings.stubs(:value).with(:dbconnections).returns(0) Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('0') Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('') Puppet::Rails.database_arguments.should_not be_include(:pool) end end describe Puppet::Rails, "when initializing a postgresql connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and host, port, username, password, connections, and database arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 200).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("") Puppet::Rails.database_arguments.should == { :adapter => "postgresql", :log_level => "testlevel", :host => "testserver", :port => "9999", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :reconnect => true } end it "should provide the adapter, log_level, and host, port, username, password, database, connections, and socket arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("postgresql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 122).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet::Rails.database_arguments.should == { :adapter => "postgresql", :log_level => "testlevel", :host => "testserver", :port => "9999", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :socket => "testsocket", :reconnect => true } end it "should not provide the pool if dbconnections is 0, '0', or ''" do Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet.settings.stubs(:value).with(:dbconnections).returns(0) Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('0') Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('') Puppet::Rails.database_arguments.should_not be_include(:pool) end end describe Puppet::Rails, "when initializing an Oracle connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and username, password, and database arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 123).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet::Rails.database_arguments.should == { :adapter => "oracle_enhanced", :log_level => "testlevel", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname" } end it "should provide the adapter, log_level, and host, username, password, database and socket arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 124).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet::Rails.database_arguments.should == { :adapter => "oracle_enhanced", :log_level => "testlevel", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname" } end it "should not provide the pool if dbconnections is 0, '0', or ''" do Puppet.settings.stubs(:value).with(:dbadapter).returns("mysql") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet.settings.stubs(:value).with(:dbconnections).returns(0) Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('0') Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('') Puppet::Rails.database_arguments.should_not be_include(:pool) end end diff --git a/spec/unit/relationship_spec.rb b/spec/unit/relationship_spec.rb index 362d74c74..a7e787b46 100755 --- a/spec/unit/relationship_spec.rb +++ b/spec/unit/relationship_spec.rb @@ -1,232 +1,232 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-1. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet/relationship' describe Puppet::Relationship do before do @edge = Puppet::Relationship.new(:a, :b) end it "should have a :source attribute" do @edge.should respond_to(:source) end it "should have a :target attribute" do @edge.should respond_to(:target) end it "should have a :callback attribute" do @edge.callback = :foo @edge.callback.should == :foo end it "should have an :event attribute" do @edge.event = :NONE @edge.event.should == :NONE end it "should require a callback if a non-NONE event is specified" do proc { @edge.event = :something }.should raise_error(ArgumentError) end it "should have a :label attribute" do @edge.should respond_to(:label) end it "should provide a :ref method that describes the edge" do @edge = Puppet::Relationship.new("a", "b") @edge.ref.should == "a => b" end it "should be able to produce a label as a hash with its event and callback" do @edge.callback = :foo @edge.event = :bar @edge.label.should == {:callback => :foo, :event => :bar} end it "should work if nil options are provided" do lambda { Puppet::Relationship.new("a", "b", nil) }.should_not raise_error end end describe Puppet::Relationship, " when initializing" do before do @edge = Puppet::Relationship.new(:a, :b) end it "should use the first argument as the source" do @edge.source.should == :a end it "should use the second argument as the target" do @edge.target.should == :b end it "should set the rest of the arguments as the event and callback" do @edge = Puppet::Relationship.new(:a, :b, :callback => :foo, :event => :bar) @edge.callback.should == :foo @edge.event.should == :bar end it "should accept events specified as strings" do @edge = Puppet::Relationship.new(:a, :b, "event" => :NONE) @edge.event.should == :NONE end it "should accept callbacks specified as strings" do @edge = Puppet::Relationship.new(:a, :b, "callback" => :foo) @edge.callback.should == :foo end end describe Puppet::Relationship, " when matching edges with no specified event" do before do @edge = Puppet::Relationship.new(:a, :b) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should not match :ALL_EVENTS" do @edge.should_not be_match(:NONE) end it "should not match any other events" do @edge.should_not be_match(:whatever) end end describe Puppet::Relationship, " when matching edges with :NONE as the event" do before do @edge = Puppet::Relationship.new(:a, :b, :event => :NONE) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should not match :ALL_EVENTS" do @edge.should_not be_match(:ALL_EVENTS) end it "should not match other events" do @edge.should_not be_match(:yayness) end end describe Puppet::Relationship, " when matching edges with :ALL as the event" do before do @edge = Puppet::Relationship.new(:a, :b, :event => :ALL_EVENTS, :callback => :whatever) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should match :ALL_EVENTS" do @edge.should be_match(:ALLEVENTS) end it "should match all other events" do @edge.should be_match(:foo) end end describe Puppet::Relationship, " when matching edges with a non-standard event" do before do @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should not match :ALL_EVENTS" do @edge.should_not be_match(:ALL_EVENTS) end it "should match events with the same name" do @edge.should be_match(:random) end end describe Puppet::Relationship, "when converting to pson", :if => Puppet.features.pson? do before do @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) end it "should store the stringified source as the source in the data" do PSON.parse(@edge.to_pson)["source"].should == "a" end it "should store the stringified target as the target in the data" do PSON.parse(@edge.to_pson)['target'].should == "b" end it "should store the psonified event as the event in the data" do PSON.parse(@edge.to_pson)["event"].should == "random" end it "should not store an event when none is set" do @edge.event = nil PSON.parse(@edge.to_pson)["event"].should be_nil end it "should store the psonified callback as the callback in the data" do @edge.callback = "whatever" PSON.parse(@edge.to_pson)["callback"].should == "whatever" end it "should not store a callback when none is set in the edge" do @edge.callback = nil PSON.parse(@edge.to_pson)["callback"].should be_nil end end describe Puppet::Relationship, "when converting from pson", :if => Puppet.features.pson? do before do @event = "random" @callback = "whatever" @data = { "source" => "mysource", "target" => "mytarget", "event" => @event, "callback" => @callback } @pson = { "type" => "Puppet::Relationship", "data" => @data } end def pson_result_should Puppet::Relationship.expects(:new).with { |*args| yield args } end it "should be extended with the PSON utility module" do Puppet::Relationship.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end # LAK:NOTE For all of these tests, we convert back to the edge so we can # trap the actual data structure then. it "should pass the source in as the first argument" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").source.should == "mysource" end it "should pass the target in as the second argument" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").target.should == "mytarget" end it "should pass the event as an argument if it's provided" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "event" => "myevent", "callback" => "eh").event.should == "myevent" end it "should pass the callback as an argument if it's provided" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "callback" => "mycallback").callback.should == "mycallback" end end diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb old mode 100644 new mode 100755 index a62793ed5..d7c37bfdd --- a/spec/unit/reports/http_spec.rb +++ b/spec/unit/reports/http_spec.rb @@ -1,56 +1,55 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/reports' # FakeHTTP fakes the behavior of Net::HTTP#request and acts as a sensor for an # otherwise difficult to trace method call. # class FakeHTTP REQUESTS = {} def self.request(req) REQUESTS[req.path] = req end end processor = Puppet::Reports.report(:http) describe processor do before { Net::HTTP.any_instance.stubs(:start).yields(FakeHTTP) } subject { Puppet::Transaction::Report.new("apply").extend(processor) } it { should respond_to(:process) } it "should use the reporturl setting's host and port" do uri = URI.parse(Puppet[:reporturl]) Net::HTTP.expects(:new).with(uri.host, uri.port).returns(stub_everything('http')) subject.process end describe "request" do before { subject.process } describe "path" do it "should use the path specified by the 'reporturl' setting" do reports_request.path.should == URI.parse(Puppet[:reporturl]).path end end describe "body" do it "should be the report as YAML" do reports_request.body.should == subject.to_yaml end end describe "content type" do it "should be 'application/x-yaml'" do reports_request.content_type.should == "application/x-yaml" end end end private def reports_request; FakeHTTP::REQUESTS[URI.parse(Puppet[:reporturl]).path] end end diff --git a/spec/unit/reports/rrdgraph_spec.rb b/spec/unit/reports/rrdgraph_spec.rb old mode 100644 new mode 100755 index 5215f1dcc..3c2704a7a --- a/spec/unit/reports/rrdgraph_spec.rb +++ b/spec/unit/reports/rrdgraph_spec.rb @@ -1,31 +1,30 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/reports' processor = Puppet::Reports.report(:rrdgraph) describe processor do include PuppetSpec::Files before do Puppet[:rrddir] = tmpdir('rrdgraph') Puppet.settings.use :master end after do FileUtils.rm_rf(Puppet[:rrddir]) end it "should not error on 0.25.x report format" do report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report0.25.x.yaml')).extend processor report.expects(:mkhtml) lambda{ report.process }.should_not raise_error end it "should not error on 2.6.x report format" do report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor report.expects(:mkhtml) lambda{ report.process }.should_not raise_error end end diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb old mode 100644 new mode 100755 index d48f6a846..73a7e353f --- a/spec/unit/reports/store_spec.rb +++ b/spec/unit/reports/store_spec.rb @@ -1,31 +1,30 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/reports' require 'time' processor = Puppet::Reports.report(:store) describe processor do describe "#process" do include PuppetSpec::Files before :each do Puppet[:reportdir] = tmpdir('reports') << '/reports' @report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor end it "should create a report directory for the client if one doesn't exist" do @report.process File.should be_directory(File.join(Puppet[:reportdir], @report.host)) end it "should write the report to the file in YAML" do Time.stubs(:now).returns(Time.parse("2011-01-06 12:00:00 UTC")) @report.process File.read(File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")).should == @report.to_yaml end end end diff --git a/spec/unit/reports/tagmail_spec.rb b/spec/unit/reports/tagmail_spec.rb index 716bcbc04..a53d11978 100755 --- a/spec/unit/reports/tagmail_spec.rb +++ b/spec/unit/reports/tagmail_spec.rb @@ -1,92 +1,91 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/reports' tagmail = Puppet::Reports.report(:tagmail) describe tagmail do before do @processor = Puppet::Transaction::Report.new("apply") @processor.extend(Puppet::Reports.report(:tagmail)) end passers = my_fixture "tagmail_passers.conf" File.readlines(passers).each do |line| it "should be able to parse '#{line.inspect}'" do @processor.parse(line) end end failers = my_fixture "tagmail_failers.conf" File.readlines(failers).each do |line| it "should not be able to parse '#{line.inspect}'" do lambda { @processor.parse(line) }.should raise_error(ArgumentError) end end { "tag: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, []], "tag.localhost: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag.localhost}, []], "tag, other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag other}, []], "tag-other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag-other}, []], "tag, !other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, %w{other}], "tag, !other, one, !two: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag one}, %w{other two}], "tag: abuse@domain.com, other@domain.com" => [%w{abuse@domain.com other@domain.com}, %w{tag}, []] }.each do |line, results| it "should parse '#{line}' as #{results.inspect}" do @processor.parse(line).shift.should == results end end describe "when matching logs" do before do @processor << Puppet::Util::Log.new(:level => :notice, :message => "first", :tags => %w{one}) @processor << Puppet::Util::Log.new(:level => :notice, :message => "second", :tags => %w{one two}) @processor << Puppet::Util::Log.new(:level => :notice, :message => "third", :tags => %w{one two three}) end def match(pos = [], neg = []) pos = Array(pos) neg = Array(neg) result = @processor.match([[%w{abuse@domain.com}, pos, neg]]) actual_result = result.shift if actual_result actual_result[1] else nil end end it "should match all messages when provided the 'all' tag as a positive matcher" do results = match("all") %w{first second third}.each do |str| results.should be_include(str) end end it "should remove messages that match a negated tag" do match("all", "three").should_not be_include("third") end it "should find any messages tagged with a provided tag" do results = match("two") results.should be_include("second") results.should be_include("third") results.should_not be_include("first") end it "should allow negation of specific tags from a specific tag list" do results = match("two", "three") results.should be_include("second") results.should_not be_include("third") end it "should allow a tag to negate all matches" do results = match([], "one") results.should be_nil end end end diff --git a/spec/unit/reports_spec.rb b/spec/unit/reports_spec.rb index a9d10be17..a4b2e04a9 100755 --- a/spec/unit/reports_spec.rb +++ b/spec/unit/reports_spec.rb @@ -1,61 +1,60 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/reports' describe Puppet::Reports do it "should instance-load report types" do Puppet::Reports.instance_loader(:report).should be_instance_of(Puppet::Util::Autoload) end it "should have a method for registering report types" do Puppet::Reports.should respond_to(:register_report) end it "should have a method for retrieving report types by name" do Puppet::Reports.should respond_to(:report) end it "should provide a method for returning documentation for all reports" do Puppet::Reports.expects(:loaded_instances).with(:report).returns([:one, :two]) one = mock 'one', :doc => "onedoc" two = mock 'two', :doc => "twodoc" Puppet::Reports.expects(:report).with(:one).returns(one) Puppet::Reports.expects(:report).with(:two).returns(two) doc = Puppet::Reports.reportdocs doc.include?("onedoc").should be_true doc.include?("twodoc").should be_true end end describe Puppet::Reports, " when loading report types" do it "should use the instance loader to retrieve report types" do Puppet::Reports.expects(:loaded_instance).with(:report, :myreporttype) Puppet::Reports.report(:myreporttype) end end describe Puppet::Reports, " when registering report types" do it "should evaluate the supplied block as code for a module" do Puppet::Reports.expects(:genmodule).returns(Module.new) Puppet::Reports.register_report(:testing) { } end it "should extend the report type with the Puppet::Util::Docs module" do mod = stub 'module', :define_method => true Puppet::Reports.expects(:genmodule).with { |name, options, block| options[:extend] == Puppet::Util::Docs }.returns(mod) Puppet::Reports.register_report(:testing) { } end it "should define a :report_name method in the module that returns the name of the report" do mod = mock 'module' mod.expects(:define_method).with(:report_name) Puppet::Reports.expects(:genmodule).returns(mod) Puppet::Reports.register_report(:testing) { } end end diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb index 78d1b3223..ae65aa91a 100755 --- a/spec/unit/resource/catalog_spec.rb +++ b/spec/unit/resource/catalog_spec.rb @@ -1,1062 +1,1061 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Resource::Catalog, "when compiling" do before do @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end it "should be an Expirer" do Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) end it "should always be expired if it's not applying" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.expects(:applying?).returns false @catalog.should be_dependent_data_expired(Time.now) end it "should not be expired if it's applying and the timestamp is late enough" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.expire @catalog.expects(:applying?).returns true @catalog.should_not be_dependent_data_expired(Time.now) end it "should be able to write its list of classes to the class file" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.add_class "foo", "bar" Puppet.settings.expects(:value).with(:classfile).returns "/class/file" fh = mock 'filehandle' File.expects(:open).with("/class/file", "w").yields fh fh.expects(:puts).with "foo\nbar" @catalog.write_class_file end it "should have a client_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.client_version = 5 @catalog.client_version.should == 5 end it "should have a server_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.server_version = 5 @catalog.server_version.should == 5 end describe "when compiling" do it "should accept tags" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one") config.tags.should == %w{one} end it "should accept multiple tags at once" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", "two") config.tags.should == %w{one two} end it "should convert all tags to strings" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", :two) config.tags.should == %w{one two} end it "should tag with both the qualified name and the split name" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one::two") config.tags.include?("one").should be_true config.tags.include?("one::two").should be_true end it "should accept classes" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.classes.should == %w{one} config.add_class("two", "three") config.classes.should == %w{one two three} end it "should tag itself with passed class names" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.tags.should == %w{one} end end describe "when extracting transobjects" do def mkscope @node = Puppet::Node.new("mynode") @compiler = Puppet::Parser::Compiler.new(@node) # XXX This is ridiculous. @compiler.send(:evaluate_main) @scope = @compiler.topscope end def mkresource(type, name) Puppet::Parser::Resource.new(type, name, :source => @source, :scope => @scope) end it "should fail if no 'main' stage can be found" do lambda { Puppet::Resource::Catalog.new("mynode").extract }.should raise_error(Puppet::DevError) end it "should warn if any non-main stages are present" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @source = mock 'source' main = mkresource("stage", "main") config.add_resource(main) other = mkresource("stage", "other") config.add_resource(other) Puppet.expects(:warning) config.extract end it "should always create a TransBucket for the 'main' stage" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @source = mock 'source' main = mkresource("stage", "main") config.add_resource(main) result = config.extract result.type.should == "Stage" result.name.should == "main" end # Now try it with a more complicated graph -- a three tier graph, each tier it "should transform arbitrarily deep graphs into isomorphic trees" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @scope.stubs(:tags).returns([]) @source = mock 'source' # Create our scopes. top = mkresource "stage", "main" config.add_resource top topbucket = [] topbucket.expects(:classes=).with([]) top.expects(:to_trans).returns(topbucket) topres = mkresource "file", "/top" topres.expects(:to_trans).returns(:topres) config.add_edge top, topres middle = mkresource "class", "middle" middle.expects(:to_trans).returns([]) config.add_edge top, middle midres = mkresource "file", "/mid" midres.expects(:to_trans).returns(:midres) config.add_edge middle, midres bottom = mkresource "class", "bottom" bottom.expects(:to_trans).returns([]) config.add_edge middle, bottom botres = mkresource "file", "/bot" botres.expects(:to_trans).returns(:botres) config.add_edge bottom, botres toparray = config.extract # This is annoying; it should look like: # [[[:botres], :midres], :topres] # but we can't guarantee sort order. toparray.include?(:topres).should be_true midarray = toparray.find { |t| t.is_a?(Array) } midarray.include?(:midres).should be_true botarray = midarray.find { |t| t.is_a?(Array) } botarray.include?(:botres).should be_true end end describe " when converting to a Puppet::Resource catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::TransObject.new 'top', "class" @topobject = Puppet::TransObject.new '/topobject', "file" @middle = Puppet::TransObject.new 'middle', "class" @middleobject = Puppet::TransObject.new '/middleobject', "file" @bottom = Puppet::TransObject.new 'bottom', "class" @bottomobject = Puppet::TransObject.new '/bottomobject', "file" @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_resource end it "should copy over the version" do @original.version = "foo" @original.to_resource.version.should == "foo" end it "should convert parser resources to plain resources" do resource = Puppet::Parser::Resource.new(:file, "foo", :scope => stub("scope", :environment => nil, :namespaces => nil), :source => stub("source")) catalog = Puppet::Resource::Catalog.new("whev") catalog.add_resource(resource) new = catalog.to_resource new.resource(:file, "foo").class.should == Puppet::Resource end it "should add all resources as Puppet::Resource instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end end describe "when converting to a RAL catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::Resource.new :class, 'top' @topobject = Puppet::Resource.new :file, @basepath+'/topobject' @middle = Puppet::Resource.new :class, 'middle' @middleobject = Puppet::Resource.new :file, @basepath+'/middleobject' @bottom = Puppet::Resource.new :class, 'bottom' @bottomobject = Puppet::Resource.new :file, @basepath+'/bottomobject' @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_ral end it "should add all resources as RAL instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end # This tests #931. it "should not lose track of resources whose names vary" do changer = Puppet::TransObject.new 'changer', 'test' config = Puppet::Resource::Catalog.new('test') config.add_resource(changer) config.add_resource(@top) config.add_edge(@top, changer) resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil #changer is going to get duplicated as part of a fix for aliases 1094 changer.expects(:dup).returns(changer) changer.expects(:to_ral).returns(resource) newconfig = nil proc { @catalog = config.to_ral }.should_not raise_error @catalog.resource("Test[changer2]").should equal(resource) end after do # Remove all resource instances. @catalog.clear(true) end end describe "when filtering" do before :each do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @r1 = stub_everything 'r1', :ref => "File[/a]" @r1.stubs(:respond_to?).with(:ref).returns(true) @r1.stubs(:dup).returns(@r1) @r1.stubs(:is_a?).returns(Puppet::Resource).returns(true) @r2 = stub_everything 'r2', :ref => "File[/b]" @r2.stubs(:respond_to?).with(:ref).returns(true) @r2.stubs(:dup).returns(@r2) @r2.stubs(:is_a?).returns(Puppet::Resource).returns(true) @resources = [@r1,@r2] @original.add_resource(@r1,@r2) end it "should transform the catalog to a resource catalog" do @original.expects(:to_catalog).with { |h,b| h == :to_resource } @original.filter end it "should scan each catalog resource in turn and apply filtering block" do @resources.each { |r| r.expects(:test?) } @original.filter do |r| r.test? end end it "should filter out resources which produce true when the filter block is evaluated" do @original.filter do |r| r == @r1 end.resource("File[/a]").should be_nil end it "should not consider edges against resources that were filtered out" do @original.add_edge(@r1,@r2) @original.filter do |r| r == @r1 end.edge?(@r1,@r2).should_not be end end describe "when functioning as a resource container" do before do @catalog = Puppet::Resource::Catalog.new("host") @one = Puppet::Type.type(:notify).new :name => "one" @two = Puppet::Type.type(:notify).new :name => "two" @dupe = Puppet::Type.type(:notify).new :name => "one" end it "should provide a method to add one or more resources" do @catalog.add_resource @one, @two @catalog.resource(@one.ref).should equal(@one) @catalog.resource(@two.ref).should equal(@two) end it "should add resources to the relationship graph if it exists" do relgraph = @catalog.relationship_graph @catalog.add_resource @one relgraph.should be_vertex(@one) end it "should set itself as the resource's catalog if it is not a relationship graph" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should make all vertices available by resource reference" do @catalog.add_resource(@one) @catalog.resource(@one.ref).should equal(@one) @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) end it "should canonize how resources are referred to during retrieval when both type and title are provided" do @catalog.add_resource(@one) @catalog.resource("notify", "one").should equal(@one) end it "should canonize how resources are referred to during retrieval when just the title is provided" do @catalog.add_resource(@one) @catalog.resource("notify[one]", nil).should equal(@one) end it "should not allow two resources with the same resource reference" do @catalog.add_resource(@one) proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should not store objects that do not respond to :ref" do proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) end it "should remove all resources when asked" do @catalog.add_resource @one @catalog.add_resource @two @one.expects :remove @two.expects :remove @catalog.clear(true) end it "should support a mechanism for finishing resources" do @one.expects :finish @two.expects :finish @catalog.add_resource @one @catalog.add_resource @two @catalog.finalize end it "should make default resources when finalizing" do @catalog.expects(:make_default_resources) @catalog.finalize end it "should add default resources to the catalog upon creation" do @catalog.make_default_resources @catalog.resource(:schedule, "daily").should_not be_nil end it "should optionally support an initialization block and should finalize after such blocks" do @one.expects :finish @two.expects :finish config = Puppet::Resource::Catalog.new("host") do |conf| conf.add_resource @one conf.add_resource @two end end it "should inform the resource that it is the resource's catalog" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should be able to find resources by reference" do @catalog.add_resource @one @catalog.resource(@one.ref).should equal(@one) end it "should be able to find resources by reference or by type/title tuple" do @catalog.add_resource @one @catalog.resource("notify", "one").should equal(@one) end it "should have a mechanism for removing resources" do @catalog.add_resource @one @one.expects :remove @catalog.remove_resource(@one) @catalog.resource(@one.ref).should be_nil @catalog.vertex?(@one).should be_false end it "should have a method for creating aliases for resources" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should create aliases for resources isomorphic resources whose names do not match their titles" do resource = Puppet::Type::File.new(:title => "testing", :path => @basepath+"/something") @catalog.add_resource(resource) @catalog.resource(:file, @basepath+"/something").should equal(resource) end it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) @catalog.add_resource(resource) # Yay, I've already got a 'should' method @catalog.resource(:exec, "echo").object_id.should == nil.object_id end # This test is the same as the previous, but the behaviour should be explicit. it "should alias using the class name from the resource reference, not the resource class name" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should fail to add an alias if the aliased name already exists" do @catalog.add_resource @one proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) end it "should not fail when a resource has duplicate aliases created" do @catalog.add_resource @one proc { @catalog.alias @one, "one" }.should_not raise_error end it "should not create aliases that point back to the resource" do @catalog.alias(@one, "one") @catalog.resource(:notify, "one").should be_nil end it "should be able to look resources up by their aliases" do @catalog.add_resource @one @catalog.alias @one, "two" @catalog.resource(:notify, "two").should equal(@one) end it "should remove resource aliases when the target resource is removed" do @catalog.add_resource @one @catalog.alias(@one, "other") @one.expects :remove @catalog.remove_resource(@one) @catalog.resource("notify", "other").should be_nil end it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(true) @catalog.add_resource(resource) @catalog.resource(:file, "other").should equal(resource) @catalog.resource(:file, @basepath+"/something").ref.should == resource.ref end it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(false) @catalog.add_resource(resource) @catalog.resource(:file, resource.title).should equal(resource) # We can't use .should here, because the resources respond to that method. raise "Aliased non-isomorphic resource" if @catalog.resource(:file, resource.name) end it "should provide a method to create additional resources that also registers the resource" do args = {:name => "/yay", :ensure => :file} resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" Puppet::Type.type(:file).expects(:new).with(args).returns(resource) @catalog.create_resource :file, args @catalog.resource("File[/yay]").should equal(resource) end end describe "when applying" do before :each do @catalog = Puppet::Resource::Catalog.new("host") @transaction = mock 'transaction' Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:add_times) Puppet.settings.stubs(:use) end it "should create and evaluate a transaction" do @transaction.expects(:evaluate) @catalog.apply end it "should provide the catalog retrieval time to the transaction" do @catalog.retrieval_duration = 5 @transaction.expects(:add_times).with(:config_retrieval => 5) @catalog.apply end it "should use a retrieval time of 0 if none is set in the catalog" do @catalog.retrieval_duration = nil @transaction.expects(:add_times).with(:config_retrieval => 0) @catalog.apply end it "should return the transaction" do @catalog.apply.should equal(@transaction) end it "should yield the transaction if a block is provided" do @catalog.apply do |trans| trans.should equal(@transaction) end end it "should default to being a host catalog" do @catalog.host_config.should be_true end it "should be able to be set to a non-host_config" do @catalog.host_config = false @catalog.host_config.should be_false end it "should pass supplied tags on to the transaction" do @transaction.expects(:tags=).with(%w{one two}) @catalog.apply(:tags => %w{one two}) end it "should set ignoreschedules on the transaction if specified in apply()" do @transaction.expects(:ignoreschedules=).with(true) @catalog.apply(:ignoreschedules => true) end it "should expire cached data in the resources both before and after the transaction" do @catalog.expects(:expire).times(2) @catalog.apply end describe "host catalogs" do # super() doesn't work in the setup method for some reason before do @catalog.host_config = true Puppet::Util::Storage.stubs(:store) end it "should initialize the state database before applying a catalog" do Puppet::Util::Storage.expects(:load) # Short-circuit the apply, so we know we're loading before the transaction Puppet::Transaction.expects(:new).raises ArgumentError proc { @catalog.apply }.should raise_error(ArgumentError) end it "should sync the state database after applying" do Puppet::Util::Storage.expects(:store) @transaction.stubs :any_failed? => false @catalog.apply end after { Puppet.settings.clear } end describe "non-host catalogs" do before do @catalog.host_config = false end it "should never send reports" do Puppet[:report] = true Puppet[:summarize] = true @catalog.apply end it "should never modify the state database" do Puppet::Util::Storage.expects(:load).never Puppet::Util::Storage.expects(:store).never @catalog.apply end after { Puppet.settings.clear } end end describe "when creating a relationship graph" do before do Puppet::Type.type(:component) @catalog = Puppet::Resource::Catalog.new("host") @compone = Puppet::Type::Component.new :name => "one" @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]" @file = Puppet::Type.type(:file) @one = @file.new :path => @basepath+"/one" @two = @file.new :path => @basepath+"/two" @sub = @file.new :path => @basepath+"/two/subdir" @catalog.add_edge @compone, @one @catalog.add_edge @comptwo, @two @three = @file.new :path => @basepath+"/three" @four = @file.new :path => @basepath+"/four", :require => "File[#{@basepath}/three]" @five = @file.new :path => @basepath+"/five" @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub @relationships = @catalog.relationship_graph end it "should be able to create a relationship graph" do @relationships.should be_instance_of(Puppet::SimpleGraph) end it "should not have any components" do @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil end it "should have all non-component resources from the catalog" do # The failures print out too much info, so i just do a class comparison @relationships.vertex?(@five).should be_true end it "should have all resource relationships set as edges" do @relationships.edge?(@three, @four).should be_true end it "should copy component relationships to all contained resources" do @relationships.path_between(@one, @two).should be end it "should add automatic relationships to the relationship graph" do @relationships.edge?(@two, @sub).should be_true end it "should get removed when the catalog is cleaned up" do @relationships.expects(:clear) @catalog.clear @catalog.instance_variable_get("@relationship_graph").should be_nil end it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).with(:relationships) graph.expects(:write_graph).with(:expanded_relationships) @catalog.host_config = true @catalog.relationship_graph end it "should not write graph files if the catalog is not a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).never @catalog.host_config = false @catalog.relationship_graph end it "should create a new relationship graph after clearing the old one" do @relationships.expects(:clear) @catalog.clear @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) end it "should remove removed resources from the relationship graph if it exists" do @catalog.remove_resource(@one) @catalog.relationship_graph.vertex?(@one).should be_false end end describe "when writing dot files" do before do @catalog = Puppet::Resource::Catalog.new("host") @name = :test @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") end it "should only write when it is a host catalog" do File.expects(:open).with(@file).never @catalog.host_config = false Puppet[:graph] = true @catalog.write_graph(@name) end after do Puppet.settings.clear end end describe "when indirecting" do before do @real_indirection = Puppet::Resource::Catalog.indirection @indirection = stub 'indirection', :name => :catalog Puppet::Util::Cacher.expire end it "should use the value of the 'catalog_terminus' setting to determine its terminus class" do # Puppet only checks the terminus setting the first time you ask # so this returns the object to the clean state # at the expense of making this test less pure Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet.settings[:catalog_terminus] = "rest" Puppet::Resource::Catalog.indirection.terminus_class.should == :rest end it "should allow the terminus class to be set manually" do Puppet::Resource::Catalog.indirection.terminus_class = :rest Puppet::Resource::Catalog.indirection.terminus_class.should == :rest end after do Puppet::Util::Cacher.expire @real_indirection.reset_terminus_class end end describe "when converting to yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") end it "should be able to be dumped to yaml" do YAML.dump(@catalog).should be_instance_of(String) end end describe "when converting from yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") text = YAML.dump(@catalog) @newcatalog = YAML.load(text) end it "should get converted back to a catalog" do @newcatalog.should be_instance_of(Puppet::Resource::Catalog) end it "should have all vertices" do @newcatalog.vertex?("one").should be_true @newcatalog.vertex?("two").should be_true end it "should have all edges" do @newcatalog.edge?("one", "two").should be_true end end end describe Puppet::Resource::Catalog, "when converting to pson", :if => Puppet.features.pson? do before do @catalog = Puppet::Resource::Catalog.new("myhost") end def pson_output_should @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its document_type to 'Catalog'" do pson_output_should { |hash| hash['document_type'] == "Catalog" } PSON.parse @catalog.to_pson end it "should set its data as a hash" do pson_output_should { |hash| hash['data'].is_a?(Hash) } PSON.parse @catalog.to_pson end [:name, :version, :tags, :classes].each do |param| it "should set its #{param} to the #{param} of the resource" do @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param) pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) } PSON.parse @catalog.to_pson end end it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]" two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]" @catalog.add_resource(one) @catalog.add_resource(two) # TODO this should really guarantee sort order PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort end it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]' two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]' three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]' @catalog.add_edge(one, two) @catalog.add_edge(two, three) @catalog.edges_between(one, two )[0].expects(:to_pson_data_hash).returns "one_two_pson" @catalog.edges_between(two, three)[0].expects(:to_pson_data_hash).returns "two_three_pson" PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort end end describe Puppet::Resource::Catalog, "when converting from pson", :if => Puppet.features.pson? do def pson_result_should Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash } end before do @data = { 'name' => "myhost" } @pson = { 'document_type' => 'Puppet::Resource::Catalog', 'data' => @data, 'metadata' => {} } @catalog = Puppet::Resource::Catalog.new("myhost") Puppet::Resource::Catalog.stubs(:new).returns @catalog end it "should be extended with the PSON utility module" do Puppet::Resource::Catalog.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end it "should create it with the provided name" do Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog PSON.parse @pson.to_pson end it "should set the provided version on the catalog if one is set" do @data['version'] = 50 PSON.parse @pson.to_pson @catalog.version.should == @data['version'] end it "should set any provided tags on the catalog" do @data['tags'] = %w{one two} PSON.parse @pson.to_pson @catalog.tags.should == @data['tags'] end it "should set any provided classes on the catalog" do @data['classes'] = %w{one two} PSON.parse @pson.to_pson @catalog.classes.should == @data['classes'] end it 'should convert the resources list into resources and add each of them' do @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")] @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert resources even if they do not include "type" information' do @data['resources'] = [Puppet::Resource.new(:file, "/foo")] @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}' @catalog.expects(:add_resource).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert the edges list into edges and add each of them' do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh") @data['edges'] = [one, two] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } @catalog.expects(:add_edge).with { |edge| edge.event == "two" } PSON.parse @pson.to_pson end it "should be able to convert relationships that do not include 'type' information" do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}" @data['edges'] = [one] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } PSON.parse @pson.to_pson end it "should set the source and target for each edge to the actual resource" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns("target_resource") @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" } PSON.parse @pson.to_pson end it "should fail if the source resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns(nil) @catalog.stubs(:resource).with("target").returns("target_resource") lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.stubs(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns(nil) lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end describe "#title_key_for_ref" do it "should parse a resource ref string into a pair" do @catalog.title_key_for_ref("Title[name]").should == ["Title", "name"] end it "should parse a resource ref string into a pair, even if there's a newline inside the name" do @catalog.title_key_for_ref("Title[na\nme]").should == ["Title", "na\nme"] end end end diff --git a/spec/unit/resource/status_spec.rb b/spec/unit/resource/status_spec.rb index 343b8d318..bb88518c0 100755 --- a/spec/unit/resource/status_spec.rb +++ b/spec/unit/resource/status_spec.rb @@ -1,153 +1,152 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/status' describe Puppet::Resource::Status do before do @resource = Puppet::Type.type(:file).new :path => "/my/file" @status = Puppet::Resource::Status.new(@resource) end it "should compute type and title correctly" do @status.resource_type.should == "File" @status.title.should == "/my/file" end [:node, :file, :line, :current_values, :status, :evaluation_time].each do |attr| it "should support #{attr}" do @status.send(attr.to_s + "=", "foo") @status.send(attr).should == "foo" end end [:skipped, :failed, :restarted, :failed_to_restart, :changed, :out_of_sync, :scheduled].each do |attr| it "should support #{attr}" do @status.send(attr.to_s + "=", "foo") @status.send(attr).should == "foo" end it "should have a boolean method for determining whehter it was #{attr}" do @status.send(attr.to_s + "=", "foo") @status.should send("be_#{attr}") end end it "should accept a resource at initialization" do Puppet::Resource::Status.new(@resource).resource.should_not be_nil end it "should set its source description to the resource's path" do @resource.expects(:path).returns "/my/path" Puppet::Resource::Status.new(@resource).source_description.should == "/my/path" end [:file, :line].each do |attr| it "should copy the resource's #{attr}" do @resource.expects(attr).returns "foo" Puppet::Resource::Status.new(@resource).send(attr).should == "foo" end end it "should copy the resource's tags" do @resource.expects(:tags).returns %w{foo bar} Puppet::Resource::Status.new(@resource).tags.should == %w{foo bar} end it "should always convert the resource to a string" do @resource.expects(:to_s).returns "foo" Puppet::Resource::Status.new(@resource).resource.should == "foo" end it "should support tags" do Puppet::Resource::Status.ancestors.should include(Puppet::Util::Tagging) end it "should create a timestamp at its creation time" do @status.time.should be_instance_of(Time) end describe "when sending logs" do before do Puppet::Util::Log.stubs(:new) end it "should set the tags to the event tags" do Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } @status.stubs(:tags).returns %w{one two} @status.send_log :notice, "my message" end [:file, :line].each do |attr| it "should pass the #{attr}" do Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } @status.send(attr.to_s + "=", "my val") @status.send_log :notice, "my message" end end it "should use the source description as the source" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "my source" } @status.stubs(:source_description).returns "my source" @status.send_log :notice, "my message" end end it "should support adding events" do event = Puppet::Transaction::Event.new(:name => :foobar) @status.add_event(event) @status.events.should == [event] end it "should use '<<' to add events" do event = Puppet::Transaction::Event.new(:name => :foobar) (@status << event).should equal(@status) @status.events.should == [event] end it "should count the number of successful events and set changed" do 3.times{ @status << Puppet::Transaction::Event.new(:status => 'success') } @status.change_count.should == 3 @status.changed.should == true @status.out_of_sync.should == true end it "should not start with any changes" do @status.change_count.should == 0 @status.changed.should == false @status.out_of_sync.should == false end it "should not treat failure, audit, or noop events as changed" do ['failure', 'audit', 'noop'].each do |s| @status << Puppet::Transaction::Event.new(:status => s) end @status.change_count.should == 0 @status.changed.should == false end it "should not treat audit events as out of sync" do @status << Puppet::Transaction::Event.new(:status => 'audit') @status.out_of_sync_count.should == 0 @status.out_of_sync.should == false end ['failure', 'noop', 'success'].each do |event_status| it "should treat #{event_status} events as out of sync" do 3.times do @status << Puppet::Transaction::Event.new(:status => event_status) end @status.out_of_sync_count.should == 3 @status.out_of_sync.should == true end end describe "When converting to YAML" do it "should include only documented attributes" do @status.file = "/foo.rb" @status.line = 27 @status.evaluation_time = 2.7 @status.tags = %w{one two} @status.to_yaml_properties.should == Puppet::Resource::Status::YAML_ATTRIBUTES.sort end end end diff --git a/spec/unit/resource/type_collection_helper_spec.rb b/spec/unit/resource/type_collection_helper_spec.rb old mode 100644 new mode 100755 index 442cfd076..ad8d75271 --- a/spec/unit/resource/type_collection_helper_spec.rb +++ b/spec/unit/resource/type_collection_helper_spec.rb @@ -1,25 +1,24 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/type_collection_helper' class RTCHelperTester include Puppet::Resource::TypeCollectionHelper end describe Puppet::Resource::TypeCollectionHelper do before do @helper = RTCHelperTester.new end it "should use its current environment to retrieve the known resource type collection" do env = stub 'environment' @helper.expects(:environment).returns env rtc = stub 'known_resource_types' env.expects(:known_resource_types).returns rtc @helper.known_resource_types.should equal(rtc) end end diff --git a/spec/unit/resource/type_collection_spec.rb b/spec/unit/resource/type_collection_spec.rb old mode 100644 new mode 100755 index 278e629bd..0c997c2ad --- a/spec/unit/resource/type_collection_spec.rb +++ b/spec/unit/resource/type_collection_spec.rb @@ -1,447 +1,451 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/type_collection' require 'puppet/resource/type' describe Puppet::Resource::TypeCollection do + include PuppetSpec::Files + before do @instance = Puppet::Resource::Type.new(:hostclass, "foo") @code = Puppet::Resource::TypeCollection.new("env") end it "should require an environment at initialization" do env = Puppet::Node::Environment.new("testing") Puppet::Resource::TypeCollection.new(env).environment.should equal(env) end it "should convert the environment into an environment instance if a string is provided" do env = Puppet::Node::Environment.new("testing") Puppet::Resource::TypeCollection.new("testing").environment.should equal(env) end it "should create a 'loader' at initialization" do Puppet::Resource::TypeCollection.new("testing").loader.should be_instance_of(Puppet::Parser::TypeLoader) end it "should be able to add a resource type" do Puppet::Resource::TypeCollection.new("env").should respond_to(:add) end it "should consider '<<' to be an alias to 'add' but should return self" do loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:add).with "foo" loader.expects(:add).with "bar" loader << "foo" << "bar" end it "should set itself as the code collection for added resource types" do loader = Puppet::Resource::TypeCollection.new("env") node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) node.resource_type_collection.should equal(@code) end it "should store node resource types as nodes" do node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) end it "should store hostclasses as hostclasses" do klass = Puppet::Resource::Type.new(:hostclass, "foo") @code.add(klass) @code.hostclass("foo").should equal(klass) end it "should store definitions as definitions" do define = Puppet::Resource::Type.new(:definition, "foo") @code.add(define) @code.definition("foo").should equal(define) end it "should merge new classes with existing classes of the same name" do loader = Puppet::Resource::TypeCollection.new("env") first = Puppet::Resource::Type.new(:hostclass, "foo") second = Puppet::Resource::Type.new(:hostclass, "foo") loader.add first first.expects(:merge).with(second) loader.add(second) end it "should remove all nodes, classes, and definitions when cleared" do loader = Puppet::Resource::TypeCollection.new("env") loader.add Puppet::Resource::Type.new(:hostclass, "class") loader.add Puppet::Resource::Type.new(:definition, "define") loader.add Puppet::Resource::Type.new(:node, "node") + watched_file = tmpfile('watched_file') + loader.watch_file(watched_file) loader.clear loader.hostclass("class").should be_nil loader.definition("define").should be_nil loader.node("node").should be_nil + loader.should_not be_watching_file(watched_file) end describe "when resolving namespaces" do [ ['', '::foo', ['foo']], ['a', '::foo', ['foo']], ['a::b', '::foo', ['foo']], [['a::b'], '::foo', ['foo']], [['a::b', 'c'], '::foo', ['foo']], [['A::B', 'C'], '::Foo', ['foo']], ['', '', ['']], ['a', '', ['']], ['a::b', '', ['']], [['a::b'], '', ['']], [['a::b', 'c'], '', ['']], [['A::B', 'C'], '', ['']], ['', 'foo', ['foo']], ['a', 'foo', ['a::foo', 'foo']], ['a::b', 'foo', ['a::b::foo', 'a::foo', 'foo']], ['A::B', 'Foo', ['a::b::foo', 'a::foo', 'foo']], [['a::b'], 'foo', ['a::b::foo', 'a::foo', 'foo']], [['a', 'b'], 'foo', ['a::foo', 'foo', 'b::foo']], [['a::b', 'c::d'], 'foo', ['a::b::foo', 'a::foo', 'foo', 'c::d::foo', 'c::foo']], [['a::b', 'a::c'], 'foo', ['a::b::foo', 'a::foo', 'foo', 'a::c::foo']], ].each do |namespaces, name, expected_result| it "should resolve #{name.inspect} in namespaces #{namespaces.inspect} correctly" do @code.instance_eval { resolve_namespaces(namespaces, name) }.should == expected_result end end end describe "when looking up names" do before do @type = Puppet::Resource::Type.new(:hostclass, "ns::klass") end it "should support looking up with multiple namespaces" do @code.add @type @code.find_hostclass(%w{boo baz ns}, "klass").should equal(@type) end it "should not attempt to import anything when the type is already defined" do @code.add @type @code.loader.expects(:import).never @code.find_hostclass(%w{ns}, "klass").should equal(@type) end describe "that need to be loaded" do it "should use the loader to load the files" do @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass") @code.loader.expects(:try_load_fqname).with(:hostclass, "klass") @code.find_hostclass(["ns"], "klass") end it "should downcase the name and downcase and array-fy the namespaces before passing to the loader" do @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass") @code.loader.expects(:try_load_fqname).with(:hostclass, "klass") @code.find_hostclass("Ns", "Klass") end it "should use the class returned by the loader" do @code.loader.expects(:try_load_fqname).returns(:klass) @code.expects(:hostclass).with("ns::klass").returns(false) @code.find_hostclass("ns", "klass").should == :klass end it "should return nil if the name isn't found" do @code.stubs(:try_load_fqname).returns(nil) @code.find_hostclass("Ns", "Klass").should be_nil end it "already-loaded names at broader scopes should not shadow autoloaded names" do @code.add Puppet::Resource::Type.new(:hostclass, "bar") @code.loader.expects(:try_load_fqname).with(:hostclass, "foo::bar").returns(:foobar) @code.find_hostclass("foo", "bar").should == :foobar end end end %w{hostclass node definition}.each do |data| before do @instance = Puppet::Resource::Type.new(data, "foo") end it "should have a method for adding a #{data}" do Puppet::Resource::TypeCollection.new("env").should respond_to("add_#{data}") end it "should use the name of the instance to add it" do loader = Puppet::Resource::TypeCollection.new("env") loader.send("add_#{data}", @instance) loader.send(data, @instance.name).should equal(@instance) end unless data == "hostclass" it "should fail to add a #{data} when one already exists" do loader = Puppet::Resource::TypeCollection.new("env") loader.add @instance lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError) end end it "should return the added #{data}" do loader = Puppet::Resource::TypeCollection.new("env") loader.add(@instance).should equal(@instance) end it "should be able to retrieve #{data} by name" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(data, "bar") loader.add instance loader.send(data, "bar").should equal(instance) end it "should retrieve #{data} insensitive to case" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(data, "Bar") loader.add instance loader.send(data, "bAr").should equal(instance) end it "should return nil when asked for a #{data} that has not been added" do Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil end it "should be able to retrieve all #{data}s" do plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" } loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(data, "foo") loader.add instance loader.send(plurals[data]).should == { "foo" => instance } end end describe "when finding a qualified instance" do it "should return any found instance if the instance name is fully qualified" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("namespace", "::foo::bar").should equal(instance) end it "should return nil if the instance name is fully qualified and no such instance exists" do loader = Puppet::Resource::TypeCollection.new("env") loader.find_hostclass("namespace", "::foo::bar").should be_nil end it "should be able to find classes in the base namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo") loader.add instance loader.find_hostclass("", "foo").should equal(instance) end it "should return the partially qualified object if it exists in a provided namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo", "bar::baz").should equal(instance) end it "should be able to find partially qualified objects in any of the provided namespaces" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass(["nons", "foo", "otherns"], "bar::baz").should equal(instance) end it "should return the unqualified object if it exists in a provided namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo", "bar").should equal(instance) end it "should return the unqualified object if it exists in the parent namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo::bar::baz", "bar").should equal(instance) end it "should should return the partially qualified object if it exists in the parent namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "bar::baz").should equal(instance) end it "should return the qualified object if it exists in the root namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "foo::bar::baz").should equal(instance) end it "should return nil if the object cannot be found" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "eh").should be_nil end describe "when topscope has a class that has the same name as a local class" do before do @loader = Puppet::Resource::TypeCollection.new("env") [ "foo::bar", "bar" ].each do |name| @loader.add Puppet::Resource::Type.new(:hostclass, name) end end it "should favor the local class, if the name is unqualified" do @loader.find_hostclass("foo", "bar").name.should == 'foo::bar' end it "should only look in the topclass, if the name is qualified" do @loader.find_hostclass("foo", "::bar").name.should == 'bar' end end it "should not look in the local scope for classes when the name is qualified" do @loader = Puppet::Resource::TypeCollection.new("env") @loader.add Puppet::Resource::Type.new(:hostclass, "foo::bar") @loader.find_hostclass("foo", "::bar").should == nil end end it "should be able to find nodes" do node = Puppet::Resource::Type.new(:node, "bar") loader = Puppet::Resource::TypeCollection.new("env") loader.add(node) loader.find_node(stub("ignored"), "bar").should == node end it "should use the 'find_or_load' method to find hostclasses" do loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find_or_load).with("foo", "bar", :hostclass) loader.find_hostclass("foo", "bar") end it "should use the 'find_or_load' method to find definitions" do loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find_or_load).with("foo", "bar", :definition) loader.find_definition("foo", "bar") end it "should indicate whether any nodes are defined" do loader = Puppet::Resource::TypeCollection.new("env") loader.add_node(Puppet::Resource::Type.new(:node, "foo")) loader.should be_nodes end it "should indicate whether no nodes are defined" do Puppet::Resource::TypeCollection.new("env").should_not be_nodes end describe "when finding nodes" do before :each do @loader = Puppet::Resource::TypeCollection.new("env") end it "should return any node whose name exactly matches the provided node name" do node = Puppet::Resource::Type.new(:node, "foo") @loader << node @loader.node("foo").should equal(node) end it "should return the first regex node whose regex matches the provided node name" do node1 = Puppet::Resource::Type.new(:node, /\w/) node2 = Puppet::Resource::Type.new(:node, /\d/) @loader << node1 << node2 @loader.node("foo10").should equal(node1) end it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do node1 = Puppet::Resource::Type.new(:node, /\w/) node2 = Puppet::Resource::Type.new(:node, "foo") @loader << node1 << node2 @loader.node("foo").should equal(node2) end end describe "when managing files" do before do @loader = Puppet::Resource::TypeCollection.new("env") Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file") end it "should have a method for specifying a file should be watched" do @loader.should respond_to(:watch_file) end it "should have a method for determining if a file is being watched" do @loader.watch_file("/foo/bar") @loader.should be_watching_file("/foo/bar") end it "should use LoadedFile to watch files" do Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file") @loader.watch_file("/foo/bar") end it "should be considered stale if any files have changed" do file1 = stub 'file1', :changed? => false file2 = stub 'file2', :changed? => true Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) @loader.watch_file("/foo/bar") @loader.watch_file("/other/bar") @loader.should be_stale end it "should not be considered stable if no files have changed" do file1 = stub 'file1', :changed? => false file2 = stub 'file2', :changed? => false Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) @loader.watch_file("/foo/bar") @loader.watch_file("/other/bar") @loader.should_not be_stale end end describe "when determining the configuration version" do before do @code = Puppet::Resource::TypeCollection.new("env") end it "should default to the current time" do time = Time.now Time.stubs(:now).returns time @code.version.should == time.to_i end it "should use the output of the environment's config_version setting if one is provided" do @code.environment.stubs(:[]).with(:config_version).returns("/my/foo") Puppet::Util.expects(:execute).with(["/my/foo"]).returns "output\n" @code.version.should == "output" end it "should raise a puppet parser error if executing config_version fails" do @code.environment.stubs(:[]).with(:config_version).returns("test") Puppet::Util.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) lambda { @code.version }.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index 41b5554d9..352f767e4 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -1,797 +1,768 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/type' describe Puppet::Resource::Type do it "should have a 'name' attribute" do Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" end [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr| it "should have a '#{attr}' attribute" do type = Puppet::Resource::Type.new(:hostclass, "foo") type.send(attr.to_s + "=", "yay") type.send(attr).should == "yay" end end [:hostclass, :node, :definition].each do |type| it "should know when it is a #{type}" do Puppet::Resource::Type.new(type, "foo").send("#{type}?").should be_true end end it "should indirect 'resource_type'" do Puppet::Resource::Type.indirection.name.should == :resource_type end it "should default to 'parser' for its terminus class" do Puppet::Resource::Type.indirection.terminus_class.should == :parser end describe "when converting to json" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo") end def from_json(json) Puppet::Resource::Type.from_pson(json) end def double_convert Puppet::Resource::Type.from_pson(PSON.parse(@type.to_pson)) end it "should include the name and type" do double_convert.name.should == @type.name double_convert.type.should == @type.type end it "should include any arguments" do @type.set_arguments("one" => nil, "two" => "foo") double_convert.arguments.should == {"one" => nil, "two" => "foo"} end it "should not include arguments if none are present" do @type.to_pson["arguments"].should be_nil end [:line, :doc, :file, :parent].each do |attr| it "should include #{attr} when set" do @type.send(attr.to_s + "=", "value") double_convert.send(attr).should == "value" end it "should not include #{attr} when not set" do @type.to_pson[attr.to_s].should be_nil end end it "should not include docs if they are empty" do @type.doc = "" @type.to_pson["doc"].should be_nil end end describe "when a node" do it "should allow a regex as its name" do lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error end it "should allow a AST::HostName instance as its name" do regex = Puppet::Parser::AST::Regex.new(:value => /foo/) name = Puppet::Parser::AST::HostName.new(:value => regex) lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error end it "should match against the regexp in the AST::HostName when a HostName instance is provided" do regex = Puppet::Parser::AST::Regex.new(:value => /\w/) name = Puppet::Parser::AST::HostName.new(:value => regex) node = Puppet::Resource::Type.new(:node, name) node.match("foo").should be_true end it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do name = Puppet::Parser::AST::HostName.new(:value => "foo") node = Puppet::Resource::Type.new(:node, name) node.name.should == "foo" end describe "and the name is a regex" do it "should have a method that indicates that this is the case" do Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex end it "should set its namespace to ''" do Puppet::Resource::Type.new(:node, /w/).namespace.should == "" end it "should return the regex converted to a string when asked for its name" do Puppet::Resource::Type.new(:node, /ww/).name.should == "ww" end it "should downcase the regex when returning the name as a string" do Puppet::Resource::Type.new(:node, /W/).name.should == "w" end it "should remove non-alpha characters when returning the name as a string" do Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*") end it "should remove leading dots when returning the name as a string" do Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./ end it "should have a method for matching its regex name against a provided name" do Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match) end it "should return true when its regex matches the provided name" do Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true end it "should return false when its regex does not match the provided name" do (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false end it "should return true when its name, as a string, is matched against an equal string" do Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true end it "should return false when its name is matched against an unequal string" do Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false end it "should match names insensitive to case" do Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true end end it "should return the name converted to a string when the name is not a regex" do pending "Need to define LoadedCode behaviour first" name = Puppet::Parser::AST::HostName.new(:value => "foo") Puppet::Resource::Type.new(:node, name).name.should == "foo" end it "should return the name converted to a string when the name is a regex" do pending "Need to define LoadedCode behaviour first" name = Puppet::Parser::AST::HostName.new(:value => /regex/) Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s end it "should mark any created scopes as a node scope" do pending "Need to define LoadedCode behaviour first" name = Puppet::Parser::AST::HostName.new(:value => /regex/) Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s end end describe "when initializing" do it "should require a resource super type" do Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass end it "should fail if provided an invalid resource super type" do lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError) end it "should set its name to the downcased, stringified provided name" do Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" end it "should set its namespace to the downcased, stringified qualified name for classes" do Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar::baz" end [:definition, :node].each do |type| it "should set its namespace to the downcased, stringified qualified portion of the name for #{type}s" do Puppet::Resource::Type.new(type, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" end end %w{code line file doc}.each do |arg| it "should set #{arg} if provided" do type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something") type.send(arg).should == "something" end end it "should set any provided arguments with the keys as symbols" do type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) type.should be_valid_parameter("foo") type.should be_valid_parameter("baz") end it "should set any provided arguments with they keys as strings" do type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) type.should be_valid_parameter(:foo) type.should be_valid_parameter(:baz) end it "should function if provided no arguments" do type = Puppet::Resource::Type.new(:hostclass, "foo") type.should_not be_valid_parameter(:foo) end end describe "when testing the validity of an attribute" do it "should return true if the parameter was typed at initialization" do Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_valid_parameter("foo") end it "should return true if it is a metaparam" do Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("require") end it "should return true if the parameter is named 'name'" do Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("name") end it "should return false if it is not a metaparam and was not provided at initialization" do Puppet::Resource::Type.new(:hostclass, "foo").should_not be_valid_parameter("yayness") end end - describe "when creating a subscope" do - before do - @scope = stub 'scope', :newscope => nil - @resource = stub 'resource' - @type = Puppet::Resource::Type.new(:hostclass, "foo") - end - - it "should return a new scope created with the provided scope as the parent" do - @scope.expects(:newscope).returns "foo" - @type.subscope(@scope, @resource).should == "foo" - end - - it "should set the source as itself" do - @scope.expects(:newscope).with { |args| args[:source] == @type } - @type.subscope(@scope, @resource) - end - - it "should set the scope's namespace to its namespace" do - @type.expects(:namespace).returns "yayness" - @scope.expects(:newscope).with { |args| args[:namespace] == "yayness" } - @type.subscope(@scope, @resource) - end - - it "should set the scope's resource to the provided resource" do - @scope.expects(:newscope).with { |args| args[:resource] == @resource } - @type.subscope(@scope, @resource) - end - end - describe "when setting its parameters in the scope" do before do @scope = Puppet::Parser::Scope.new(:compiler => stub("compiler", :environment => Puppet::Node::Environment.new), :source => stub("source")) @resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope) @type = Puppet::Resource::Type.new(:hostclass, "foo") end ['module_name', 'name', 'title'].each do |variable| it "should allow #{variable} to be evaluated as param default" do @type.instance_eval { @module_name = "bar" } var = Puppet::Parser::AST::Variable.new({'value' => variable}) @type.set_arguments :foo => var @type.set_resource_parameters(@resource, @scope) @scope.lookupvar('foo').should == 'bar' end end # this test is to clarify a crazy edge case # if you specify these special names as params, the resource # will override the special variables it "resource should override defaults" do @type.set_arguments :name => nil @resource[:name] = 'foobar' var = Puppet::Parser::AST::Variable.new({'value' => 'name'}) @type.set_arguments :foo => var @type.set_resource_parameters(@resource, @scope) @scope.lookupvar('foo').should == 'foobar' end it "should set each of the resource's parameters as variables in the scope" do @type.set_arguments :foo => nil, :boo => nil @resource[:foo] = "bar" @resource[:boo] = "baz" @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("foo").should == "bar" @scope.lookupvar("boo").should == "baz" end it "should set the variables as strings" do @type.set_arguments :foo => nil @resource[:foo] = "bar" @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("foo").should == "bar" end it "should fail if any of the resource's parameters are not valid attributes" do @type.set_arguments :foo => nil @resource[:boo] = "baz" lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) end it "should evaluate and set its default values as variables for parameters not provided by the resource" do @type.set_arguments :foo => stub("value", :safeevaluate => "something") @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("foo").should == "something" end it "should set all default values as parameters in the resource" do @type.set_arguments :foo => stub("value", :safeevaluate => "something") @type.set_resource_parameters(@resource, @scope) @resource[:foo].should == "something" end it "should fail if the resource does not provide a value for a required argument" do @type.set_arguments :foo => nil @resource.expects(:to_hash).returns({}) lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) end it "should set the resource's title as a variable if not otherwise provided" do @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("title").should == "bar" end it "should set the resource's name as a variable if not otherwise provided" do @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("name").should == "bar" end it "should set its module name in the scope if available" do @type.instance_eval { @module_name = "mymod" } @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("module_name").should == "mymod" end it "should set its caller module name in the scope if available" do @scope.expects(:parent_module_name).returns "mycaller" @type.set_resource_parameters(@resource, @scope) @scope.lookupvar("caller_module_name").should == "mycaller" end end describe "when describing and managing parent classes" do before do @code = Puppet::Resource::TypeCollection.new("env") @parent = Puppet::Resource::Type.new(:hostclass, "bar") @code.add @parent @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") @code.add @child @env = stub "environment", :known_resource_types => @code @scope = stub "scope", :environment => @env, :namespaces => [""] end it "should be able to define a parent" do Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") end it "should use the code collection to find the parent resource type" do @child.parent_type(@scope).should equal(@parent) end it "should be able to find parent nodes" do parent = Puppet::Resource::Type.new(:node, "bar") @code.add parent child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar") @code.add child child.parent_type(@scope).should equal(parent) end it "should cache a reference to the parent type" do @code.stubs(:hostclass).with("foo::bar").returns nil @code.expects(:hostclass).with("bar").once.returns @parent @child.parent_type(@scope) @child.parent_type end it "should correctly state when it is another type's child" do @child.parent_type(@scope) @child.should be_child_of(@parent) end it "should be considered the child of a parent's parent" do @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo") @code.add @grandchild @child.parent_type(@scope) @grandchild.parent_type(@scope) @grandchild.should be_child_of(@parent) end it "should correctly state when it is not another type's child" do @notchild = Puppet::Resource::Type.new(:hostclass, "baz") @code.add @notchild @notchild.should_not be_child_of(@parent) end end describe "when evaluating its code" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new :compiler => @compiler @resource = Puppet::Parser::Resource.new(:foo, "yay", :scope => @scope) # This is so the internal resource lookup works, yo. @compiler.catalog.add_resource @resource @known_resource_types = stub 'known_resource_types' @resource.stubs(:known_resource_types).returns @known_resource_types @type = Puppet::Resource::Type.new(:hostclass, "foo") end it "should add hostclass names to the classes list" do @type.evaluate_code(@resource) @compiler.catalog.classes.should be_include("foo") end it "should add node names to the classes list" do @type = Puppet::Resource::Type.new(:node, "foo") @type.evaluate_code(@resource) @compiler.catalog.classes.should be_include("foo") end it "should not add defined resource names to the classes list" do @type = Puppet::Resource::Type.new(:definition, "foo") @type.evaluate_code(@resource) @compiler.catalog.classes.should_not be_include("foo") end it "should set all of its parameters in a subscope" do subscope = stub 'subscope', :compiler => @compiler - @type.expects(:subscope).with(@scope, @resource).returns subscope + @scope.expects(:newscope).with(:source => @type, :dynamic => true, :namespace => 'foo', :resource => @resource).returns subscope @type.expects(:set_resource_parameters).with(@resource, subscope) @type.evaluate_code(@resource) end it "should not create a subscope for the :main class" do @resource.stubs(:title).returns(:main) @type.expects(:subscope).never @type.expects(:set_resource_parameters).with(@resource, @scope) @type.evaluate_code(@resource) end it "should store the class scope" do @type.evaluate_code(@resource) @scope.class_scope(@type).should be_instance_of(@scope.class) end it "should still create a scope but not store it if the type is a definition" do @type = Puppet::Resource::Type.new(:definition, "foo") @type.evaluate_code(@resource) @scope.class_scope(@type).should be_nil end it "should evaluate the AST code if any is provided" do code = stub 'code' @type.stubs(:code).returns code - @type.stubs(:subscope).returns stub_everything("subscope", :compiler => @compiler) - code.expects(:safeevaluate).with @type.subscope + subscope = stub_everything("subscope", :compiler => @compiler) + @scope.stubs(:newscope).returns subscope + code.expects(:safeevaluate).with subscope @type.evaluate_code(@resource) end describe "and ruby code is provided" do it "should create a DSL Resource API and evaluate it" do @type.stubs(:ruby_code).returns(proc { "foo" }) @api = stub 'api' Puppet::DSL::ResourceAPI.expects(:new).with { |res, scope, code| code == @type.ruby_code }.returns @api @api.expects(:evaluate) @type.evaluate_code(@resource) end end it "should noop if there is no code" do @type.expects(:code).returns nil @type.evaluate_code(@resource) end describe "and it has a parent class" do before do @parent_type = Puppet::Resource::Type.new(:hostclass, "parent") @type.parent = "parent" @parent_resource = Puppet::Parser::Resource.new(:class, "parent", :scope => @scope) @compiler.add_resource @scope, @parent_resource @type.resource_type_collection = @scope.known_resource_types @type.resource_type_collection.add @parent_type end it "should evaluate the parent's resource" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil end it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @type.evaluate_code(@resource) end it "should use the parent's scope as its base scope" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id end end describe "and it has a parent node" do before do @type = Puppet::Resource::Type.new(:node, "foo") @parent_type = Puppet::Resource::Type.new(:node, "parent") @type.parent = "parent" @parent_resource = Puppet::Parser::Resource.new(:node, "parent", :scope => @scope) @compiler.add_resource @scope, @parent_resource @type.resource_type_collection = @scope.known_resource_types @type.resource_type_collection.add(@parent_type) end it "should evaluate the parent's resource" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil end it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @type.evaluate_code(@resource) end it "should use the parent's scope as its base scope" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id end end end describe "when creating a resource" do before do @node = Puppet::Node.new("foo", :environment => 'env') @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @top = Puppet::Resource::Type.new :hostclass, "top" @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top" @code = Puppet::Resource::TypeCollection.new("env") @code.add @top @code.add @middle @node.environment.stubs(:known_resource_types).returns(@code) end it "should create a resource instance" do @top.ensure_in_catalog(@scope).should be_instance_of(Puppet::Parser::Resource) end it "should set its resource type to 'class' when it is a hostclass" do Puppet::Resource::Type.new(:hostclass, "top").ensure_in_catalog(@scope).type.should == "Class" end it "should set its resource type to 'node' when it is a node" do Puppet::Resource::Type.new(:node, "top").ensure_in_catalog(@scope).type.should == "Node" end it "should fail when it is a definition" do lambda { Puppet::Resource::Type.new(:definition, "top").ensure_in_catalog(@scope) }.should raise_error(ArgumentError) end it "should add the created resource to the scope's catalog" do @top.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should add specified parameters to the resource" do @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'}) @compiler.catalog.resource(:class, "top")['one'].should == '1' @compiler.catalog.resource(:class, "top")['two'].should == '2' end it "should not require params for a param class" do @top.ensure_in_catalog(@scope, {}) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should evaluate the parent class if one exists" do @middle.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should evaluate the parent class if one exists" do @middle.ensure_in_catalog(@scope, {}) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should fail if you try to create duplicate class resources" do othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope ) # add the same class resource to the catalog @compiler.catalog.add_resource(othertop) lambda { @top.ensure_in_catalog(@scope, {}) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should fail to evaluate if a parent class is defined but cannot be found" do othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" @code.add othertop lambda { othertop.ensure_in_catalog(@scope) }.should raise_error(Puppet::ParseError) end it "should not create a new resource if one already exists" do @compiler.catalog.expects(:resource).with(:class, "top").returns("something") @compiler.catalog.expects(:add_resource).never @top.ensure_in_catalog(@scope) end it "should return the existing resource when not creating a new one" do @compiler.catalog.expects(:resource).with(:class, "top").returns("something") @compiler.catalog.expects(:add_resource).never @top.ensure_in_catalog(@scope).should == "something" end it "should not create a new parent resource if one already exists and it has a parent class" do @top.ensure_in_catalog(@scope) top_resource = @compiler.catalog.resource(:class, "top") @middle.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should equal(top_resource) end # #795 - tag before evaluation. it "should tag the catalog with the resource tags when it is evaluated" do @middle.ensure_in_catalog(@scope) @compiler.catalog.should be_tagged("middle") end it "should tag the catalog with the parent class tags when it is evaluated" do @middle.ensure_in_catalog(@scope) @compiler.catalog.should be_tagged("top") end end describe "when merging code from another instance" do def code(str) Puppet::Parser::AST::Leaf.new :value => str end it "should fail unless it is a class" do lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(Puppet::Error) end it "should fail unless the source instance is a class" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:node, "foo") lambda { dest.merge(source) }.should raise_error(Puppet::Error) end it "should fail if both classes have different parent classes" do code = Puppet::Resource::TypeCollection.new("env") {"a" => "b", "c" => "d"}.each do |parent, child| code.add Puppet::Resource::Type.new(:hostclass, parent) code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent) end lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(Puppet::Error) end it "should fail if it's named 'main' and 'freeze_main' is enabled" do Puppet.settings[:freeze_main] = true code = Puppet::Resource::TypeCollection.new("env") code.add Puppet::Resource::Type.new(:hostclass, "") other = Puppet::Resource::Type.new(:hostclass, "") lambda { code.hostclass("").merge(other) }.should raise_error(Puppet::Error) end it "should copy the other class's parent if it has not parent" do dest = Puppet::Resource::Type.new(:hostclass, "bar") parent = Puppet::Resource::Type.new(:hostclass, "parent") source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent") dest.merge(source) dest.parent.should == "parent" end it "should copy the other class's documentation as its docs if it has no docs" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") dest.merge(source) dest.doc.should == "yayness" end it "should append the other class's docs to its docs if it has any" do dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness") source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") dest.merge(source) dest.doc.should == "foonessyayness" end it "should turn its code into an ASTArray if necessary" do dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo")) source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) dest.merge(source) dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray) end it "should set the other class's code as its code if it has none" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) dest.merge(source) dest.code.value.should == "bar" end it "should append the other class's code to its code if it has any" do dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")] dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode) scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")] source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode) dest.merge(source) dest.code.children.collect { |l| l.value }.should == %w{dest source} end end end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index 916741d1a..79ea69e9c 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -1,812 +1,811 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource' describe Puppet::Resource do before do @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" end [:catalog, :file, :line].each do |attr| it "should have an #{attr} attribute" do resource = Puppet::Resource.new("file", "/my/file") resource.should respond_to(attr) resource.should respond_to(attr.to_s + "=") end end it "should have a :title attribute" do Puppet::Resource.new(:user, "foo").title.should == "foo" end it "should require the type and title" do lambda { Puppet::Resource.new }.should raise_error(ArgumentError) end it "should canonize types to capitalized strings" do Puppet::Resource.new(:user, "foo").type.should == "User" end it "should canonize qualified types so all strings are capitalized" do Puppet::Resource.new("foo::bar", "foo").type.should == "Foo::Bar" end it "should tag itself with its type" do Puppet::Resource.new("file", "/f").should be_tagged("file") end it "should tag itself with its title if the title is a valid tag" do Puppet::Resource.new("user", "bar").should be_tagged("bar") end it "should not tag itself with its title if the title is a not valid tag" do Puppet::Resource.new("file", "/bar").should_not be_tagged("/bar") end it "should allow setting of attributes" do Puppet::Resource.new("file", "/bar", :file => "/foo").file.should == "/foo" Puppet::Resource.new("file", "/bar", :exported => true).should be_exported end it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do ref = Puppet::Resource.new(:component, "foo") ref.type.should == "Class" ref.title.should == "Foo" end it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do ref = Puppet::Resource.new(:component, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should set the type to 'Class' if it is nil and the title contains no square brackets" do ref = Puppet::Resource.new(nil, "yay") ref.type.should == "Class" ref.title.should == "Yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[baz[yay]]") ref.type.should == "Foo::Bar" ref.title.should =="baz[yay]" end it "should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets" do ref = Puppet::Resource.new("foo::bar[baz]") ref.type.should == "Foo::Bar" ref.title.should =="baz" end it "should be able to extract its information from a Puppet::Type instance" do ral = Puppet::Type.type(:file).new :path => @basepath+"/foo" ref = Puppet::Resource.new(ral) ref.type.should == "File" ref.title.should == @basepath+"/foo" end it "should fail if the title is nil and the type is not a valid resource reference string" do lambda { Puppet::Resource.new("foo") }.should raise_error(ArgumentError) end it 'should fail if strict is set and type does not exist' do lambda { Puppet::Resource.new('foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type foo') end it 'should fail if strict is set and class does not exist' do lambda { Puppet::Resource.new('Class', 'foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class foo') end it "should fail if the title is a hash and the type is not a valid resource reference string" do lambda { Puppet::Resource.new({:type => "foo", :title => "bar"}) }.should raise_error(ArgumentError, 'Puppet::Resource.new does not take a hash as the first argument. Did you mean ("foo", "bar") ?' ) end it "should be able to produce a backward-compatible reference array" do Puppet::Resource.new("foobar", "/f").to_trans_ref.should == %w{Foobar /f} end it "should be taggable" do Puppet::Resource.ancestors.should be_include(Puppet::Util::Tagging) end it "should have an 'exported' attribute" do resource = Puppet::Resource.new("file", "/f") resource.exported = true resource.exported.should == true resource.should be_exported end it "should support an environment attribute" do Puppet::Resource.new("file", "/my/file", :environment => :foo).environment.name.should == :foo end describe "and munging its type and title" do describe "when modeling a builtin resource" do it "should be able to find the resource type" do Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) end it "should set its type to the capitalized type name" do Puppet::Resource.new("file", "/my/file").type.should == "File" end end describe "when modeling a defined resource" do describe "that exists" do before do @type = Puppet::Resource::Type.new(:definition, "foo::bar") Puppet::Node::Environment.new.known_resource_types.add @type end it "should set its type to the capitalized type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) end it "should set its title to the provided title" do Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" end end describe "that does not exist" do it "should set its resource type to the capitalized resource type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end end end describe "when modeling a node" do # Life's easier with nodes, because they can't be qualified. it "should set its type to 'Node' and its title to the provided title" do node = Puppet::Resource.new("node", "foo") node.type.should == "Node" node.title.should == "foo" end end describe "when modeling a class" do it "should set its type to 'Class'" do Puppet::Resource.new("class", "foo").type.should == "Class" end describe "that exists" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") Puppet::Node::Environment.new.known_resource_types.add @type end it "should set its title to the capitalized, fully qualified resource type" do Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) end end describe "that does not exist" do it "should set its type to 'Class' and its title to the capitalized provided name" do klass = Puppet::Resource.new("class", "foo::bar") klass.type.should == "Class" klass.title.should == "Foo::Bar" end end describe "and its name is set to the empty string" do it "should set its title to :main" do Puppet::Resource.new("class", "").title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") Puppet::Node::Environment.new.known_resource_types.add @type Puppet::Resource.new("class", "").title.should == :main end end end describe "and its name is set to :main" do it "should set its title to :main" do Puppet::Resource.new("class", :main).title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") Puppet::Node::Environment.new.known_resource_types.add @type Puppet::Resource.new("class", :main).title.should == :main end end end end end it "should return nil when looking up resource types that don't exist" do Puppet::Resource.new("foobar", "bar").resource_type.should be_nil end it "should not fail when an invalid parameter is used and strict mode is disabled" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type resource = Puppet::Resource.new("foobar", "/my/file") resource[:yay] = true end it "should be considered equivalent to another resource if their type and title match and no parameters are set" do Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") end it "should be considered equivalent to another resource if their type, title, and parameters are equal" do Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}).should == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to another resource if their type and title match but parameters are different" do Puppet::Resource.new("file", "/f", :parameters => {:fee => "baz"}).should_not == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to a non-resource" do Puppet::Resource.new("file", "/f").should_not == "foo" end it "should not be considered equivalent to another resource if their types do not match" do Puppet::Resource.new("file", "/f").should_not == Puppet::Resource.new("exec", "/f") end it "should not be considered equivalent to another resource if their titles do not match" do Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") end describe "when referring to a resource with name canonicalization" do it "should canonicalize its own name" do res = Puppet::Resource.new("file", "/path/") res.uniqueness_key.should == ["/path"] res.ref.should == "File[/path/]" end end describe "when running in strict mode" do it "should be strict" do Puppet::Resource.new("file", "/path", :strict => true).should be_strict end it "should fail if invalid parameters are used" do lambda { Puppet::Resource.new("file", "/path", :strict => true, :parameters => {:nosuchparam => "bar"}) }.should raise_error end it "should fail if the resource type cannot be resolved" do lambda { Puppet::Resource.new("nosuchtype", "/path", :strict => true) }.should raise_error end end describe "when managing parameters" do before do @resource = Puppet::Resource.new("file", "/my/file") end it "should correctly detect when provided parameters are not valid for builtin types" do Puppet::Resource.new("file", "/my/file").should_not be_valid_parameter("foobar") end it "should correctly detect when provided parameters are valid for builtin types" do Puppet::Resource.new("file", "/my/file").should be_valid_parameter("mode") end it "should correctly detect when provided parameters are not valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file").should_not be_valid_parameter("myparam") end it "should correctly detect when provided parameters are valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) Puppet::Node::Environment.new.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file").should be_valid_parameter("myparam") end it "should allow setting and retrieving of parameters" do @resource[:foo] = "bar" @resource[:foo].should == "bar" end it "should allow setting of parameters at initialization" do Puppet::Resource.new("file", "/my/file", :parameters => {:foo => "bar"})[:foo].should == "bar" end it "should canonicalize retrieved parameter names to treat symbols and strings equivalently" do @resource[:foo] = "bar" @resource["foo"].should == "bar" end it "should canonicalize set parameter names to treat symbols and strings equivalently" do @resource["foo"] = "bar" @resource[:foo].should == "bar" end it "should set the namevar when asked to set the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:name] = "bob" resource[:myvar].should == "bob" end it "should return the namevar when asked to return the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:myvar] = "test" resource[:name].should == "test" end it "should be able to set the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" lambda { resource[:name] = "eh" }.should_not raise_error end it "should be able to return the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" resource[:name].should == "eh" end it "should be able to iterate over parameters" do @resource[:foo] = "bar" @resource[:fee] = "bare" params = {} @resource.each do |key, value| params[key] = value end params.should == {:foo => "bar", :fee => "bare"} end it "should include Enumerable" do @resource.class.ancestors.should be_include(Enumerable) end it "should have a method for testing whether a parameter is included" do @resource[:foo] = "bar" @resource.should be_has_key(:foo) @resource.should_not be_has_key(:eh) end it "should have a method for providing the list of parameters" do @resource[:foo] = "bar" @resource[:bar] = "foo" keys = @resource.keys keys.should be_include(:foo) keys.should be_include(:bar) end it "should have a method for providing the number of parameters" do @resource[:foo] = "bar" @resource.length.should == 1 end it "should have a method for deleting parameters" do @resource[:foo] = "bar" @resource.delete(:foo) @resource[:foo].should be_nil end it "should have a method for testing whether the parameter list is empty" do @resource.should be_empty @resource[:foo] = "bar" @resource.should_not be_empty end it "should be able to produce a hash of all existing parameters" do @resource[:foo] = "bar" @resource[:fee] = "yay" hash = @resource.to_hash hash[:foo].should == "bar" hash[:fee].should == "yay" end it "should not provide direct access to the internal parameters hash when producing a hash" do hash = @resource.to_hash hash[:foo] = "bar" @resource[:foo].should be_nil end it "should use the title as the namevar to the hash if no namevar is present" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource.to_hash[:myvar].should == "bob" end it "should set :name to the title if :name is not present for non-builtin types" do krt = Puppet::Resource::TypeCollection.new("myenv") krt.add Puppet::Resource::Type.new(:definition, :foo) resource = Puppet::Resource.new :foo, "bar" resource.stubs(:known_resource_types).returns krt resource.to_hash[:name].should == "bar" end end describe "when serializing" do before do @resource = Puppet::Resource.new("file", "/my/file") @resource["one"] = "test" @resource["two"] = "other" end it "should be able to be dumped to yaml" do proc { YAML.dump(@resource) }.should_not raise_error end it "should produce an equivalent yaml object" do text = YAML.dump(@resource) newresource = YAML.load(text) newresource.title.should == @resource.title newresource.type.should == @resource.type %w{one two}.each do |param| newresource[param].should == @resource[param] end end end describe "when loading 0.25.x storedconfigs YAML" do before :each do @old_storedconfig_yaml = %q{--- !ruby/object:Puppet::Resource::Reference builtin_type: title: /tmp/bar type: File } end it "should deserialize a Puppet::Resource::Reference without exceptions" do lambda { YAML.load(@old_storedconfig_yaml) }.should_not raise_error end it "should deserialize as a Puppet::Resource::Reference as a Puppet::Resource" do YAML.load(@old_storedconfig_yaml).class.should == Puppet::Resource end it "should to_hash properly" do YAML.load(@old_storedconfig_yaml).to_hash.should == { :path => "/tmp/bar" } end end describe "when converting to a RAL resource" do it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do resource = Puppet::Resource.new("file", @basepath+"/my/file") result = resource.to_ral result.should be_instance_of(Puppet::Type.type(:file)) result[:path].should == @basepath+"/my/file" end it "should convert to a component instance if the resource type is not of a builtin type" do resource = Puppet::Resource.new("foobar", "somename") result = resource.to_ral result.should be_instance_of(Puppet::Type.type(:component)) result.title.should == "Foobar[somename]" end end it "should be able to convert itself to Puppet code" do Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_manifest) end describe "when converting to puppet code" do before do @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => { :noop => true, :foo => %w{one two}, :ensure => 'present', } ) end it "should align, sort and add trailing commas to attributes with ensure first" do @resource.to_manifest.should == <<-HEREDOC.gsub(/^\s{8}/, '').gsub(/\n$/, '') one::two { '/my/file': ensure => 'present', foo => ['one', 'two'], noop => 'true', } HEREDOC end end it "should be able to convert itself to a TransObject instance" do Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_trans) end describe "when converting to a TransObject" do describe "and the resource is not an instance of a builtin type" do before do @resource = Puppet::Resource.new("foo", "bar") end it "should return a simple TransBucket if it is not an instance of a builtin type" do bucket = @resource.to_trans bucket.should be_instance_of(Puppet::TransBucket) bucket.type.should == @resource.type bucket.name.should == @resource.title end it "should return a simple TransBucket if it is a stage" do @resource = Puppet::Resource.new("stage", "bar") bucket = @resource.to_trans bucket.should be_instance_of(Puppet::TransBucket) bucket.type.should == @resource.type bucket.name.should == @resource.title end it "should copy over the resource's file" do @resource.file = "/foo/bar" @resource.to_trans.file.should == "/foo/bar" end it "should copy over the resource's line" do @resource.line = 50 @resource.to_trans.line.should == 50 end end describe "and the resource is an instance of a builtin type" do before do @resource = Puppet::Resource.new("file", "bar") end it "should return a TransObject if it is an instance of a builtin resource type" do trans = @resource.to_trans trans.should be_instance_of(Puppet::TransObject) trans.type.should == "file" trans.name.should == @resource.title end it "should copy over the resource's file" do @resource.file = "/foo/bar" @resource.to_trans.file.should == "/foo/bar" end it "should copy over the resource's line" do @resource.line = 50 @resource.to_trans.line.should == 50 end # Only TransObjects support tags, annoyingly it "should copy over the resource's tags" do @resource.tag "foo" @resource.to_trans.tags.should == @resource.tags end it "should copy the resource's parameters into the transobject and convert the parameter name to a string" do @resource[:foo] = "bar" @resource.to_trans["foo"].should == "bar" end it "should be able to copy arrays of values" do @resource[:foo] = %w{yay fee} @resource.to_trans["foo"].should == %w{yay fee} end it "should reduce single-value arrays to just a value" do @resource[:foo] = %w{yay} @resource.to_trans["foo"].should == "yay" end it "should convert resource references into the backward-compatible form" do @resource[:foo] = Puppet::Resource.new(:file, "/f") @resource.to_trans["foo"].should == %w{File /f} end it "should convert resource references into the backward-compatible form even when within arrays" do @resource[:foo] = ["a", Puppet::Resource.new(:file, "/f")] @resource.to_trans["foo"].should == ["a", %w{File /f}] end end end describe "when converting to pson", :if => Puppet.features.pson? do def pson_output_should @resource.class.expects(:pson_create).with { |hash| yield hash } end it "should include the pson util module" do Puppet::Resource.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its type to the provided type" do Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" end it "should include all tags from the resource" do resource = Puppet::Resource.new("File", "/foo") resource.tag("yay") Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).tags.should == resource.tags end it "should include the file if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.file = "/my/file" Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).file.should == "/my/file" end it "should include the line if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.line = 50 Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).line.should == 50 end it "should include the 'exported' value if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.exported = true Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_true end it "should set 'exported' to false if no value is set" do resource = Puppet::Resource.new("File", "/foo") Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_false end it "should set all of its parameters as the 'parameters' entry" do resource = Puppet::Resource.new("File", "/foo") resource[:foo] = %w{bar eh} resource[:fee] = %w{baz} result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result["foo"].should == %w{bar eh} result["fee"].should == %w{baz} end it "should serialize relationships as reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = Puppet::Resource.new("File", "/bar") result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result[:requires].should == "File[/bar]" end it "should serialize multiple relationships as arrays of reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = [Puppet::Resource.new("File", "/bar"), Puppet::Resource.new("File", "/baz")] result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result[:requires].should == [ "File[/bar]", "File[/baz]" ] end end describe "when converting from pson", :if => Puppet.features.pson? do def pson_result_should Puppet::Resource.expects(:new).with { |hash| yield hash } end before do @data = { 'type' => "file", 'title' => @basepath+"/yay", } end it "should set its type to the provided type" do Puppet::Resource.from_pson(@data).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_pson(@data).title.should == @basepath+"/yay" end it "should tag the resource with any provided tags" do @data['tags'] = %w{foo bar} resource = Puppet::Resource.from_pson(@data) resource.tags.should be_include("foo") resource.tags.should be_include("bar") end it "should set its file to the provided file" do @data['file'] = "/foo/bar" Puppet::Resource.from_pson(@data).file.should == "/foo/bar" end it "should set its line to the provided line" do @data['line'] = 50 Puppet::Resource.from_pson(@data).line.should == 50 end it "should 'exported' to true if set in the pson data" do @data['exported'] = true Puppet::Resource.from_pson(@data).exported.should be_true end it "should 'exported' to false if not set in the pson data" do Puppet::Resource.from_pson(@data).exported.should be_false end it "should fail if no title is provided" do @data.delete('title') lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) end it "should fail if no type is provided" do @data.delete('type') lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) end it "should set each of the provided parameters" do @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} resource = Puppet::Resource.from_pson(@data) resource['foo'].should == %w{one two} resource['fee'].should == %w{three four} end it "should convert single-value array parameters to normal values" do @data['parameters'] = {'foo' => %w{one}} resource = Puppet::Resource.from_pson(@data) resource['foo'].should == %w{one} end end describe "it should implement to_resource" do resource = Puppet::Resource.new("file", "/my/file") resource.to_resource.should == resource end describe "because it is an indirector model" do it "should include Puppet::Indirector" do Puppet::Resource.should be_is_a(Puppet::Indirector) end it "should have a default terminus" do Puppet::Resource.indirection.terminus_class.should == :ral end it "should have a name" do Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" end end describe "when resolving resources with a catalog" do it "should resolve all resources using the catalog" do catalog = mock 'catalog' resource = Puppet::Resource.new("foo::bar", "yay") resource.catalog = catalog catalog.expects(:resource).with("Foo::Bar[yay]").returns(:myresource) resource.resolve.should == :myresource end end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:myvar, :owner, :path] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) res = Puppet::Resource.new("file", "/my/file", :parameters => {:owner => 'root', :content => 'hello'}) res.uniqueness_key.should == [ nil, 'root', '/my/file'] end end end diff --git a/spec/unit/run_spec.rb b/spec/unit/run_spec.rb index 6c5ecde65..ea6ec74b1 100755 --- a/spec/unit/run_spec.rb +++ b/spec/unit/run_spec.rb @@ -1,137 +1,136 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/agent' require 'puppet/run' describe Puppet::Run do before do @runner = Puppet::Run.new end it "should indirect :run" do Puppet::Run.indirection.name.should == :run end it "should use a configurer agent as its agent" do agent = mock 'agent' Puppet::Agent.expects(:new).with(Puppet::Configurer).returns agent @runner.agent.should equal(agent) end it "should accept options at initialization" do lambda { Puppet::Run.new :background => true }.should_not raise_error end it "should default to running in the foreground" do Puppet::Run.new.should_not be_background end it "should default to its options being an empty hash" do Puppet::Run.new.options.should == {} end it "should accept :tags for the agent" do Puppet::Run.new(:tags => "foo").options[:tags].should == "foo" end it "should accept :ignoreschedules for the agent" do Puppet::Run.new(:ignoreschedules => true).options[:ignoreschedules].should be_true end it "should accept an option to configure it to run in the background" do Puppet::Run.new(:background => true).should be_background end it "should retain the background option" do Puppet::Run.new(:background => true).options[:background].should be_nil end it "should not accept arbitrary options" do lambda { Puppet::Run.new(:foo => true) }.should raise_error(ArgumentError) end describe "when asked to run" do before do @agent = stub 'agent', :run => nil, :running? => false @runner.stubs(:agent).returns @agent end it "should run its agent" do agent = stub 'agent2', :running? => false @runner.stubs(:agent).returns agent agent.expects(:run) @runner.run end it "should pass any of its options on to the agent" do @runner.stubs(:options).returns(:foo => :bar) @agent.expects(:run).with(:foo => :bar) @runner.run end it "should log its run using the provided options" do @runner.expects(:log_run) @runner.run end it "should set its status to 'already_running' if the agent is already running" do @agent.expects(:running?).returns true @runner.run @runner.status.should == "running" end it "should set its status to 'success' if the agent is run" do @agent.expects(:running?).returns false @runner.run @runner.status.should == "success" end it "should run the agent in a thread if asked to run it in the background" do Thread.expects(:new) @runner.expects(:background?).returns true @agent.expects(:run).never # because our thread didn't yield @runner.run end it "should run the agent directly if asked to run it in the foreground" do Thread.expects(:new).never @runner.expects(:background?).returns false @agent.expects(:run) @runner.run end end describe ".from_pson" do it "should accept a hash of options, and pass them with symbolified keys to new" do options = { "tags" => "whatever", "background" => true, } Puppet::Run.expects(:new).with( { :tags => "whatever", :background => true, }) Puppet::Run.from_pson(options) end end end diff --git a/spec/unit/simple_graph_spec.rb b/spec/unit/simple_graph_spec.rb index 99db2a55c..c8fea3b58 100755 --- a/spec/unit/simple_graph_spec.rb +++ b/spec/unit/simple_graph_spec.rb @@ -1,904 +1,904 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-1. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' require 'puppet/simple_graph' describe Puppet::SimpleGraph do it "should return the number of its vertices as its length" do @graph = Puppet::SimpleGraph.new @graph.add_vertex("one") @graph.add_vertex("two") @graph.size.should == 2 end it "should consider itself a directed graph" do Puppet::SimpleGraph.new.directed?.should be_true end it "should provide a method for reversing the graph" do @graph = Puppet::SimpleGraph.new @graph.add_edge(:one, :two) @graph.reversal.edge?(:two, :one).should be_true end it "should be able to produce a dot graph" do @graph = Puppet::SimpleGraph.new @graph.add_edge(:one, :two) proc { @graph.to_dot_graph }.should_not raise_error end describe "when managing vertices" do before do @graph = Puppet::SimpleGraph.new end it "should provide a method to add a vertex" do @graph.add_vertex(:test) @graph.vertex?(:test).should be_true end it "should reset its reversed graph when vertices are added" do rev = @graph.reversal @graph.add_vertex(:test) @graph.reversal.should_not equal(rev) end it "should ignore already-present vertices when asked to add a vertex" do @graph.add_vertex(:test) proc { @graph.add_vertex(:test) }.should_not raise_error end it "should return true when asked if a vertex is present" do @graph.add_vertex(:test) @graph.vertex?(:test).should be_true end it "should return false when asked if a non-vertex is present" do @graph.vertex?(:test).should be_false end it "should return all set vertices when asked" do @graph.add_vertex(:one) @graph.add_vertex(:two) @graph.vertices.length.should == 2 @graph.vertices.should include(:one) @graph.vertices.should include(:two) end it "should remove a given vertex when asked" do @graph.add_vertex(:one) @graph.remove_vertex!(:one) @graph.vertex?(:one).should be_false end it "should do nothing when a non-vertex is asked to be removed" do proc { @graph.remove_vertex!(:one) }.should_not raise_error end end describe "when managing edges" do before do @graph = Puppet::SimpleGraph.new end it "should provide a method to test whether a given vertex pair is an edge" do @graph.should respond_to(:edge?) end it "should reset its reversed graph when edges are added" do rev = @graph.reversal @graph.add_edge(:one, :two) @graph.reversal.should_not equal(rev) end it "should provide a method to add an edge as an instance of the edge class" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.edge?(:one, :two).should be_true end it "should provide a method to add an edge by specifying the two vertices" do @graph.add_edge(:one, :two) @graph.edge?(:one, :two).should be_true end it "should provide a method to add an edge by specifying the two vertices and a label" do @graph.add_edge(:one, :two, :callback => :awesome) @graph.edge?(:one, :two).should be_true end describe "when retrieving edges between two nodes" do it "should handle the case of nodes not in the graph" do @graph.edges_between(:one, :two).should == [] end it "should handle the case of nodes with no edges between them" do @graph.add_vertex(:one) @graph.add_vertex(:two) @graph.edges_between(:one, :two).should == [] end it "should handle the case of nodes connected by a single edge" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.edges_between(:one, :two).length.should == 1 @graph.edges_between(:one, :two)[0].should equal(edge) end it "should handle the case of nodes connected by multiple edges" do edge1 = Puppet::Relationship.new(:one, :two, :callback => :foo) edge2 = Puppet::Relationship.new(:one, :two, :callback => :bar) @graph.add_edge(edge1) @graph.add_edge(edge2) Set.new(@graph.edges_between(:one, :two)).should == Set.new([edge1, edge2]) end end it "should add the edge source as a vertex if it is not already" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.vertex?(:one).should be_true end it "should add the edge target as a vertex if it is not already" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.vertex?(:two).should be_true end it "should return all edges as edge instances when asked" do one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) @graph.add_edge(one) @graph.add_edge(two) edges = @graph.edges edges.should be_instance_of(Array) edges.length.should == 2 edges.should include(one) edges.should include(two) end it "should remove an edge when asked" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.remove_edge!(edge) @graph.edge?(edge.source, edge.target).should be_false end it "should remove all related edges when a vertex is removed" do one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) @graph.add_edge(one) @graph.add_edge(two) @graph.remove_vertex!(:two) @graph.edge?(:one, :two).should be_false @graph.edge?(:two, :three).should be_false @graph.edges.length.should == 0 end end describe "when finding adjacent vertices" do before do @graph = Puppet::SimpleGraph.new @one_two = Puppet::Relationship.new(:one, :two) @two_three = Puppet::Relationship.new(:two, :three) @one_three = Puppet::Relationship.new(:one, :three) @graph.add_edge(@one_two) @graph.add_edge(@one_three) @graph.add_edge(@two_three) end it "should return adjacent vertices" do adj = @graph.adjacent(:one) adj.should be_include(:three) adj.should be_include(:two) end it "should default to finding :out vertices" do @graph.adjacent(:two).should == [:three] end it "should support selecting :in vertices" do @graph.adjacent(:two, :direction => :in).should == [:one] end it "should default to returning the matching vertices as an array of vertices" do @graph.adjacent(:two).should == [:three] end it "should support returning an array of matching edges" do @graph.adjacent(:two, :type => :edges).should == [@two_three] end # Bug #2111 it "should not consider a vertex adjacent just because it was asked about previously" do @graph = Puppet::SimpleGraph.new @graph.add_vertex("a") @graph.add_vertex("b") @graph.edge?("a", "b") @graph.adjacent("a").should == [] end end describe "when clearing" do before do @graph = Puppet::SimpleGraph.new one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) @graph.add_edge(one) @graph.add_edge(two) @graph.clear end it "should remove all vertices" do @graph.vertices.should be_empty end it "should remove all edges" do @graph.edges.should be_empty end end describe "when reversing graphs" do before do @graph = Puppet::SimpleGraph.new end it "should provide a method for reversing the graph" do @graph.add_edge(:one, :two) @graph.reversal.edge?(:two, :one).should be_true end it "should add all vertices to the reversed graph" do @graph.add_edge(:one, :two) @graph.vertex?(:one).should be_true @graph.vertex?(:two).should be_true end it "should retain labels on edges" do @graph.add_edge(:one, :two, :callback => :awesome) edge = @graph.reversal.edges_between(:two, :one)[0] edge.label.should == {:callback => :awesome} end end describe "when reporting cycles in the graph" do before do @graph = Puppet::SimpleGraph.new end def add_edges(hash) hash.each do |a,b| @graph.add_edge(a, b) end end it "should fail on two-vertex loops" do add_edges :a => :b, :b => :a proc { @graph.report_cycles_in_graph }.should raise_error(Puppet::Error) end it "should fail on multi-vertex loops" do add_edges :a => :b, :b => :c, :c => :a proc { @graph.report_cycles_in_graph }.should raise_error(Puppet::Error) end it "should fail when a larger tree contains a small cycle" do add_edges :a => :b, :b => :a, :c => :a, :d => :c proc { @graph.report_cycles_in_graph }.should raise_error(Puppet::Error) end it "should succeed on trees with no cycles" do add_edges :a => :b, :b => :e, :c => :a, :d => :c proc { @graph.report_cycles_in_graph }.should_not raise_error end it "should produce the correct relationship text" do add_edges :a => :b, :b => :a # cycle detection starts from a or b randomly # so we need to check for either ordering in the error message want = %r{Found 1 dependency cycle:\n\((a => b => a|b => a => b)\)\nTry} expect { @graph.report_cycles_in_graph }.to raise_error(Puppet::Error, want) end it "cycle discovery should be the minimum cycle for a simple graph" do add_edges "a" => "b" add_edges "b" => "a" add_edges "b" => "c" cycles = nil expect { cycles = @graph.find_cycles_in_graph.sort }.should_not raise_error cycles.should be == [["a", "b"]] end it "cycle discovery should handle two distinct cycles" do add_edges "a" => "a1", "a1" => "a" add_edges "b" => "b1", "b1" => "b" cycles = nil expect { cycles = @graph.find_cycles_in_graph.sort }.should_not raise_error cycles.should be == [["a", "a1"], ["b", "b1"]] end it "cycle discovery should handle two cycles in a connected graph" do add_edges "a" => "b", "b" => "c", "c" => "d" add_edges "a" => "a1", "a1" => "a" add_edges "c" => "c1", "c1" => "c2", "c2" => "c3", "c3" => "c" cycles = nil expect { cycles = @graph.find_cycles_in_graph.sort }.should_not raise_error cycles.should be == [%w{a a1}, %w{c c1 c2 c3}] end it "cycle discovery should handle a complicated cycle" do add_edges "a" => "b", "b" => "c" add_edges "a" => "c" add_edges "c" => "c1", "c1" => "a" add_edges "c" => "c2", "c2" => "b" cycles = nil expect { cycles = @graph.find_cycles_in_graph.sort }.should_not raise_error cycles.should be == [%w{a b c c1 c2}] end it "cycle discovery should not fail with large data sets" do limit = 3000 (1..(limit - 1)).each do |n| add_edges n.to_s => (n+1).to_s end cycles = nil expect { cycles = @graph.find_cycles_in_graph.sort }.should_not raise_error cycles.should be == [] end it "path finding should work with a simple cycle" do add_edges "a" => "b", "b" => "c", "c" => "a" cycles = @graph.find_cycles_in_graph.sort paths = @graph.paths_in_cycle(cycles.first, 100) paths.should be == [%w{a b c a}] end it "path finding should work with two independent cycles" do add_edges "a" => "b1" add_edges "a" => "b2" add_edges "b1" => "a", "b2" => "a" cycles = @graph.find_cycles_in_graph.sort cycles.length.should be == 1 paths = @graph.paths_in_cycle(cycles.first, 100) paths.sort.should be == [%w{a b1 a}, %w{a b2 a}] end it "path finding should prefer shorter paths in cycles" do add_edges "a" => "b", "b" => "c", "c" => "a" add_edges "b" => "a" cycles = @graph.find_cycles_in_graph.sort cycles.length.should be == 1 paths = @graph.paths_in_cycle(cycles.first, 100) paths.should be == [%w{a b a}, %w{a b c a}] end it "path finding should respect the max_path value" do (1..20).each do |n| add_edges "a" => "b#{n}", "b#{n}" => "a" end cycles = @graph.find_cycles_in_graph.sort cycles.length.should be == 1 (1..20).each do |n| paths = @graph.paths_in_cycle(cycles.first, n) paths.length.should be == n end paths = @graph.paths_in_cycle(cycles.first, 21) paths.length.should be == 20 end end describe "when writing dot files" do before do @graph = Puppet::SimpleGraph.new @name = :test @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") end it "should only write when graphing is enabled" do File.expects(:open).with(@file).never Puppet[:graph] = false @graph.write_graph(@name) end it "should write a dot file based on the passed name" do File.expects(:open).with(@file, "w").yields(stub("file", :puts => nil)) @graph.expects(:to_dot).with("name" => @name.to_s.capitalize) Puppet[:graph] = true @graph.write_graph(@name) end after do Puppet.settings.clear end end describe Puppet::SimpleGraph do before do @graph = Puppet::SimpleGraph.new end it "should correctly clear vertices and edges when asked" do @graph.add_edge("a", "b") @graph.add_vertex "c" @graph.clear @graph.vertices.should be_empty @graph.edges.should be_empty end end describe "when matching edges" do before do @graph = Puppet::SimpleGraph.new @event = Puppet::Transaction::Event.new(:name => :yay, :resource => "a") @none = Puppet::Transaction::Event.new(:name => :NONE, :resource => "a") @edges = {} @edges["a/b"] = Puppet::Relationship.new("a", "b", {:event => :yay, :callback => :refresh}) @edges["a/c"] = Puppet::Relationship.new("a", "c", {:event => :yay, :callback => :refresh}) @graph.add_edge(@edges["a/b"]) end it "should match edges whose source matches the source of the event" do @graph.matching_edges(@event).should == [@edges["a/b"]] end it "should match always match nothing when the event is :NONE" do @graph.matching_edges(@none).should be_empty end it "should match multiple edges" do @graph.add_edge(@edges["a/c"]) edges = @graph.matching_edges(@event) edges.should be_include(@edges["a/b"]) edges.should be_include(@edges["a/c"]) end end describe "when determining dependencies" do before do @graph = Puppet::SimpleGraph.new @graph.add_edge("a", "b") @graph.add_edge("a", "c") @graph.add_edge("b", "d") end it "should find all dependents when they are on multiple levels" do @graph.dependents("a").sort.should == %w{b c d}.sort end it "should find single dependents" do @graph.dependents("b").sort.should == %w{d}.sort end it "should return an empty array when there are no dependents" do @graph.dependents("c").sort.should == [].sort end it "should find all dependencies when they are on multiple levels" do @graph.dependencies("d").sort.should == %w{a b} end it "should find single dependencies" do @graph.dependencies("c").sort.should == %w{a} end it "should return an empty array when there are no dependencies" do @graph.dependencies("a").sort.should == [] end end require 'puppet/util/graph' class Container < Puppet::Type::Component include Puppet::Util::Graph include Enumerable attr_accessor :name def each @children.each do |c| yield c end end def initialize(name, ary) @name = name @children = ary end def push(*ary) ary.each { |c| @children.push(c)} end def to_s @name end end require "puppet/resource/catalog" describe "when splicing the graph" do def container_graph @one = Container.new("one", %w{a b}) @two = Container.new("two", ["c", "d"]) @three = Container.new("three", ["i", "j"]) @middle = Container.new("middle", ["e", "f", @two]) @top = Container.new("top", ["g", "h", @middle, @one, @three]) @empty = Container.new("empty", []) @whit = Puppet::Type.type(:whit) @stage = Puppet::Type.type(:stage).new(:name => "foo") @contgraph = @top.to_graph(Puppet::Resource::Catalog.new) # We have to add the container to the main graph, else it won't # be spliced in the dependency graph. @contgraph.add_vertex(@empty) end def containers @contgraph.vertices.select { |x| !x.is_a? String } end def contents_of(x) @contgraph.direct_dependents_of(x) end def dependency_graph @depgraph = Puppet::SimpleGraph.new @contgraph.vertices.each do |v| @depgraph.add_vertex(v) end # We have to specify a relationship to our empty container, else it # never makes it into the dep graph in the first place. @explicit_dependencies = {@one => @two, "f" => "c", "h" => @middle, "c" => @empty} @explicit_dependencies.each do |source, target| @depgraph.add_edge(source, target, :callback => :refresh) end end def splice @contgraph.splice!(@depgraph) end def whit_called(name) x = @depgraph.vertices.find { |v| v.is_a?(@whit) && v.name =~ /#{name}/ } x.should_not be_nil def x.to_s "Whit[#{name}]" end def x.inspect to_s end x end def admissible_sentinal_of(x) @depgraph.vertex?(x) ? x : whit_called("admissible_#{x.name}") end def completed_sentinal_of(x) @depgraph.vertex?(x) ? x : whit_called("completed_#{x.name}") end before do container_graph dependency_graph splice end # This is the real heart of splicing -- replacing all containers X in our # relationship graph with a pair of whits { admissible_X and completed_X } # such that that # # 0) completed_X depends on admissible_X # 1) contents of X each depend on admissible_X # 2) completed_X depends on each on the contents of X # 3) everything which depended on X depends on completed_X # 4) admissible_X depends on everything X depended on # 5) the containers and their edges must be removed # # Note that this requires attention to the possible case of containers # which contain or depend on other containers. # # Point by point: # 0) completed_X depends on admissible_X # it "every container's completed sentinal should depend on its admissible sentinal" do containers.each { |container| @depgraph.path_between(admissible_sentinal_of(container),completed_sentinal_of(container)).should be } end # 1) contents of X each depend on admissible_X # it "all contained objects should depend on their container's admissible sentinal" do containers.each { |container| contents_of(container).each { |leaf| @depgraph.should be_edge(admissible_sentinal_of(container),admissible_sentinal_of(leaf)) } } end # 2) completed_X depends on each on the contents of X # it "completed sentinals should depend on their container's contents" do containers.each { |container| contents_of(container).each { |leaf| @depgraph.should be_edge(completed_sentinal_of(leaf),completed_sentinal_of(container)) } } end # # 3) everything which depended on X depends on completed_X # # 4) admissible_X depends on everything X depended on # 5) the containers and their edges must be removed # it "should remove all Container objects from the dependency graph" do @depgraph.vertices.find_all { |v| v.is_a?(Container) }.should be_empty end it "should remove all Stage resources from the dependency graph" do @depgraph.vertices.find_all { |v| v.is_a?(Puppet::Type.type(:stage)) }.should be_empty end it "should no longer contain anything but the non-container objects" do @depgraph.vertices.find_all { |v| ! v.is_a?(String) and ! v.is_a?(@whit)}.should be_empty end it "should retain labels on non-containment edges" do @explicit_dependencies.each { |f,t| @depgraph.edges_between(completed_sentinal_of(f),admissible_sentinal_of(t))[0].label.should == {:callback => :refresh} } end it "should not add labels to edges that have none" do @depgraph.add_edge(@two, @three) splice @depgraph.path_between("c", "i").any? {|segment| segment.all? {|e| e.label == {} }}.should be end it "should copy labels over edges that have none" do @depgraph.add_edge("c", @three, {:callback => :refresh}) splice # And make sure the label got copied. @depgraph.path_between("c", "i").flatten.select {|e| e.label == {:callback => :refresh} }.should_not be_empty end it "should not replace a label with a nil label" do # Lastly, add some new label-less edges and make sure the label stays. @depgraph.add_edge(@middle, @three) @depgraph.add_edge("c", @three, {:callback => :refresh}) splice @depgraph.path_between("c","i").flatten.select {|e| e.label == {:callback => :refresh} }.should_not be_empty end it "should copy labels to all created edges" do @depgraph.add_edge(@middle, @three) @depgraph.add_edge("c", @three, {:callback => :refresh}) splice @three.each do |child| edge = Puppet::Relationship.new("c", child) (path = @depgraph.path_between(edge.source, edge.target)).should be path.should_not be_empty path.flatten.select {|e| e.label == {:callback => :refresh} }.should_not be_empty end end end it "should serialize to YAML using the old format by default" do Puppet::SimpleGraph.use_new_yaml_format.should == false end describe "(yaml tests)" do def empty_graph(graph) end def one_vertex_graph(graph) graph.add_vertex(:a) end def graph_without_edges(graph) [:a, :b, :c].each { |x| graph.add_vertex(x) } end def one_edge_graph(graph) graph.add_edge(:a, :b) end def many_edge_graph(graph) graph.add_edge(:a, :b) graph.add_edge(:a, :c) graph.add_edge(:b, :d) graph.add_edge(:c, :d) end def labeled_edge_graph(graph) graph.add_edge(:a, :b, :callback => :foo, :event => :bar) end def overlapping_edge_graph(graph) graph.add_edge(:a, :b, :callback => :foo, :event => :bar) graph.add_edge(:a, :b, :callback => :biz, :event => :baz) end def self.all_test_graphs [:empty_graph, :one_vertex_graph, :graph_without_edges, :one_edge_graph, :many_edge_graph, :labeled_edge_graph, :overlapping_edge_graph] end def object_ids(enumerable) # Return a sorted list of the object id's of the elements of an # enumerable. enumerable.collect { |x| x.object_id }.sort end def graph_to_yaml(graph, which_format) previous_use_new_yaml_format = Puppet::SimpleGraph.use_new_yaml_format Puppet::SimpleGraph.use_new_yaml_format = (which_format == :new) ZAML.dump(graph) ensure Puppet::SimpleGraph.use_new_yaml_format = previous_use_new_yaml_format end # Test serialization of graph to YAML. [:old, :new].each do |which_format| all_test_graphs.each do |graph_to_test| it "should be able to serialize #{graph_to_test} to YAML (#{which_format} format)" do graph = Puppet::SimpleGraph.new send(graph_to_test, graph) yaml_form = graph_to_yaml(graph, which_format) # Hack the YAML so that objects in the Puppet namespace get # changed to YAML::DomainType objects. This lets us inspect # the serialized objects easily without invoking any # yaml_initialize hooks. yaml_form.gsub!('!ruby/object:Puppet::', '!hack/object:Puppet::') serialized_object = YAML.load(yaml_form) # Check that the object contains instance variables @edges and # @vertices only. @reversal is also permitted, but we don't # check it, because it is going to be phased out. serialized_object.type_id.should == 'object:Puppet::SimpleGraph' serialized_object.value.keys.reject { |x| x == 'reversal' }.sort.should == ['edges', 'vertices'] # Check edges by forming a set of tuples (source, target, # callback, event) based on the graph and the YAML and make sure # they match. edges = serialized_object.value['edges'] edges.should be_a(Array) expected_edge_tuples = graph.edges.collect { |edge| [edge.source, edge.target, edge.callback, edge.event] } actual_edge_tuples = edges.collect do |edge| edge.type_id.should == 'object:Puppet::Relationship' %w{source target}.each { |x| edge.value.keys.should include(x) } edge.value.keys.each { |x| ['source', 'target', 'callback', 'event'].should include(x) } %w{source target callback event}.collect { |x| edge.value[x] } end Set.new(actual_edge_tuples).should == Set.new(expected_edge_tuples) actual_edge_tuples.length.should == expected_edge_tuples.length # Check vertices one by one. vertices = serialized_object.value['vertices'] if which_format == :old vertices.should be_a(Hash) Set.new(vertices.keys).should == Set.new(graph.vertices) vertices.each do |key, value| value.type_id.should == 'object:Puppet::SimpleGraph::VertexWrapper' value.value.keys.sort.should == %w{adjacencies vertex} value.value['vertex'].should equal(key) adjacencies = value.value['adjacencies'] adjacencies.should be_a(Hash) Set.new(adjacencies.keys).should == Set.new([:in, :out]) [:in, :out].each do |direction| adjacencies[direction].should be_a(Hash) expected_adjacent_vertices = Set.new(graph.adjacent(key, :direction => direction, :type => :vertices)) Set.new(adjacencies[direction].keys).should == expected_adjacent_vertices adjacencies[direction].each do |adj_key, adj_value| # Since we already checked edges, just check consistency # with edges. desired_source = direction == :in ? adj_key : key desired_target = direction == :in ? key : adj_key expected_edges = edges.select do |edge| edge.value['source'] == desired_source && edge.value['target'] == desired_target end adj_value.should be_a(Set) if object_ids(adj_value) != object_ids(expected_edges) raise "For vertex #{key.inspect}, direction #{direction.inspect}: expected adjacencies #{expected_edges.inspect} but got #{adj_value.inspect}" end end end end else vertices.should be_a(Array) Set.new(vertices).should == Set.new(graph.vertices) vertices.length.should == graph.vertices.length end end end # Test deserialization of graph from YAML. This presumes the # correctness of serialization to YAML, which has already been # tested. all_test_graphs.each do |graph_to_test| it "should be able to deserialize #{graph_to_test} from YAML (#{which_format} format)" do reference_graph = Puppet::SimpleGraph.new send(graph_to_test, reference_graph) yaml_form = graph_to_yaml(reference_graph, which_format) recovered_graph = YAML.load(yaml_form) # Test that the recovered vertices match the vertices in the # reference graph. expected_vertices = reference_graph.vertices.to_a recovered_vertices = recovered_graph.vertices.to_a Set.new(recovered_vertices).should == Set.new(expected_vertices) recovered_vertices.length.should == expected_vertices.length # Test that the recovered edges match the edges in the # reference graph. expected_edge_tuples = reference_graph.edges.collect do |edge| [edge.source, edge.target, edge.callback, edge.event] end recovered_edge_tuples = recovered_graph.edges.collect do |edge| [edge.source, edge.target, edge.callback, edge.event] end Set.new(recovered_edge_tuples).should == Set.new(expected_edge_tuples) recovered_edge_tuples.length.should == expected_edge_tuples.length # We ought to test that the recovered graph is self-consistent # too. But we're not going to bother with that yet because # the internal representation of the graph is about to change. end end it "should be able to serialize a graph where the vertices contain backreferences to the graph (#{which_format} format)" do reference_graph = Puppet::SimpleGraph.new vertex = Object.new vertex.instance_eval { @graph = reference_graph } reference_graph.add_edge(vertex, :other_vertex) yaml_form = graph_to_yaml(reference_graph, which_format) recovered_graph = YAML.load(yaml_form) recovered_graph.vertices.length.should == 2 recovered_vertex = recovered_graph.vertices.reject { |x| x.is_a?(Symbol) }[0] recovered_vertex.instance_eval { @graph }.should equal(recovered_graph) recovered_graph.edges.length.should == 1 recovered_edge = recovered_graph.edges[0] recovered_edge.source.should equal(recovered_vertex) recovered_edge.target.should == :other_vertex end end it "should serialize properly when used as a base class" do class Puppet::TestDerivedClass < Puppet::SimpleGraph attr_accessor :foo end derived = Puppet::TestDerivedClass.new derived.add_edge(:a, :b) derived.foo = 1234 recovered_derived = YAML.load(YAML.dump(derived)) recovered_derived.class.should equal(Puppet::TestDerivedClass) recovered_derived.edges.length.should == 1 recovered_derived.edges[0].source.should == :a recovered_derived.edges[0].target.should == :b recovered_derived.vertices.length.should == 2 recovered_derived.foo.should == 1234 end end end diff --git a/spec/unit/ssl/base_spec.rb b/spec/unit/ssl/base_spec.rb index 99038391f..125623b70 100755 --- a/spec/unit/ssl/base_spec.rb +++ b/spec/unit/ssl/base_spec.rb @@ -1,43 +1,42 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate' class TestCertificate < Puppet::SSL::Base; end describe Puppet::SSL::Certificate do before :each do @base = TestCertificate.new("name") end describe "when fingerprinting content" do before :each do @cert = stub 'cert', :to_der => "DER" @base.stubs(:content).returns(@cert) OpenSSL::Digest.stubs(:constants).returns ["MD5", "DIGEST"] @digest = stub_everything OpenSSL::Digest.stubs(:const_get).returns @digest end it "should digest the certificate DER value and return a ':' seperated nibblet string" do @cert.expects(:to_der).returns("DER") @digest.expects(:hexdigest).with("DER").returns "digest" @base.fingerprint.should == "DI:GE:ST" end it "should raise an error if the digest algorithm is not defined" do OpenSSL::Digest.expects(:constants).returns [] lambda { @base.fingerprint }.should raise_error end it "should use the given digest algorithm" do OpenSSL::Digest.stubs(:const_get).with("DIGEST").returns @digest @digest.expects(:hexdigest).with("DER").returns "digest" @base.fingerprint(:digest).should == "DI:GE:ST" end end -end \ No newline at end of file +end diff --git a/spec/unit/ssl/certificate_authority/interface_spec.rb b/spec/unit/ssl/certificate_authority/interface_spec.rb index 2e4a3fc77..9e858dd54 100755 --- a/spec/unit/ssl/certificate_authority/interface_spec.rb +++ b/spec/unit/ssl/certificate_authority/interface_spec.rb @@ -1,333 +1,332 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate_authority' shared_examples_for "a normal interface method" do it "should call the method on the CA for each host specified if an array was provided" do @ca.expects(@method).with("host1") @ca.expects(@method).with("host2") @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => %w{host1 host2}) @applier.apply(@ca) end it "should call the method on the CA for all existing certificates if :all was provided" do @ca.expects(:list).returns %w{host1 host2} @ca.expects(@method).with("host1") @ca.expects(@method).with("host2") @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => :all) @applier.apply(@ca) end end describe Puppet::SSL::CertificateAuthority::Interface do before do @class = Puppet::SSL::CertificateAuthority::Interface end describe "when initializing" do it "should set its method using its settor" do @class.any_instance.expects(:method=).with(:generate) @class.new(:generate, :to => :all) end it "should set its subjects using the settor" do @class.any_instance.expects(:subjects=).with(:all) @class.new(:generate, :to => :all) end it "should set the digest if given" do interface = @class.new(:generate, :to => :all, :digest => :digest) interface.digest.should == :digest end it "should set the digest to md5 if none given" do interface = @class.new(:generate, :to => :all) interface.digest.should == :MD5 end end describe "when setting the method" do it "should set the method" do @class.new(:generate, :to => :all).method.should == :generate end it "should fail if the method isn't a member of the INTERFACE_METHODS array" do Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.expects(:include?).with(:thing).returns false lambda { @class.new(:thing, :to => :all) }.should raise_error(ArgumentError) end end describe "when setting the subjects" do it "should set the subjects" do @class.new(:generate, :to => :all).subjects.should == :all end it "should fail if the subjects setting isn't :all or an array" do lambda { @class.new(:generate, "other") }.should raise_error(ArgumentError) end end it "should have a method for triggering the application" do @class.new(:generate, :to => :all).should respond_to(:apply) end describe "when applying" do before do # We use a real object here, because :verify can't be stubbed, apparently. @ca = Object.new end it "should raise InterfaceErrors" do @applier = @class.new(:revoke, :to => :all) @ca.expects(:list).raises Puppet::SSL::CertificateAuthority::Interface::InterfaceError lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) end it "should log non-Interface failures rather than failing" do @applier = @class.new(:revoke, :to => :all) @ca.expects(:list).raises ArgumentError Puppet.expects(:err) lambda { @applier.apply(@ca) }.should_not raise_error end describe "with an empty array specified and the method is not list" do it "should fail" do @applier = @class.new(:sign, :to => []) lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) end end describe ":generate" do it "should fail if :all was specified" do @applier = @class.new(:generate, :to => :all) lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) end it "should call :generate on the CA for each host specified" do @applier = @class.new(:generate, :to => %w{host1 host2}) @ca.expects(:generate).with("host1") @ca.expects(:generate).with("host2") @applier.apply(@ca) end end describe ":verify" do before { @method = :verify } #it_should_behave_like "a normal interface method" it "should call the method on the CA for each host specified if an array was provided" do # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. end it "should call the method on the CA for all existing certificates if :all was provided" do # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. end end describe ":destroy" do before { @method = :destroy } it_should_behave_like "a normal interface method" end describe ":revoke" do before { @method = :revoke } it_should_behave_like "a normal interface method" end describe ":sign" do describe "and an array of names was provided" do before do @applier = @class.new(:sign, :to => %w{host1 host2}) end it "should sign the specified waiting certificate requests" do @ca.expects(:sign).with("host1") @ca.expects(:sign).with("host2") @applier.apply(@ca) end end describe "and :all was provided" do it "should sign all waiting certificate requests" do @ca.stubs(:waiting?).returns(%w{cert1 cert2}) @ca.expects(:sign).with("cert1") @ca.expects(:sign).with("cert2") @applier = @class.new(:sign, :to => :all) @applier.apply(@ca) end it "should fail if there are no waiting certificate requests" do @ca.stubs(:waiting?).returns([]) @applier = @class.new(:sign, :to => :all) lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) end end end describe ":list" do describe "and an empty array was provided" do it "should print a string containing all certificate requests" do @ca.expects(:waiting?).returns %w{host1 host2} @ca.stubs(:verify) @applier = @class.new(:list, :to => []) @applier.expects(:puts).with "host1\nhost2" @applier.apply(@ca) end end describe "and :all was provided" do it "should print a string containing all certificate requests and certificates" do @ca.expects(:waiting?).returns %w{host1 host2} @ca.expects(:list).returns %w{host3 host4} @ca.stubs(:verify) @ca.stubs(:fingerprint).returns "fingerprint" @ca.expects(:verify).with("host3").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23), "certificate revoked") @applier = @class.new(:list, :to => :all) @applier.expects(:puts).with "host1 (fingerprint)" @applier.expects(:puts).with "host2 (fingerprint)" @applier.expects(:puts).with "- host3 (fingerprint) (certificate revoked)" @applier.expects(:puts).with "+ host4 (fingerprint)" @applier.apply(@ca) end end describe "and :signed was provided" do it "should print a string containing all signed certificate requests and certificates" do @ca.expects(:list).returns %w{host1 host2} @applier = @class.new(:list, :to => :signed) @applier.apply(@ca) end end describe "and an array of names was provided" do it "should print a string of all named hosts that have a waiting request" do @ca.expects(:waiting?).returns %w{host1 host2} @ca.expects(:list).returns %w{host3 host4} @ca.stubs(:fingerprint).returns "fingerprint" @ca.stubs(:verify) @applier = @class.new(:list, :to => %w{host1 host2 host3 host4}) @applier.expects(:puts).with "host1 (fingerprint)" @applier.expects(:puts).with "host2 (fingerprint)" @applier.expects(:puts).with "+ host3 (fingerprint)" @applier.expects(:puts).with "+ host4 (fingerprint)" @applier.apply(@ca) end end end describe ":print" do describe "and :all was provided" do it "should print all certificates" do @ca.expects(:list).returns %w{host1 host2} @applier = @class.new(:print, :to => :all) @ca.expects(:print).with("host1").returns "h1" @applier.expects(:puts).with "h1" @ca.expects(:print).with("host2").returns "h2" @applier.expects(:puts).with "h2" @applier.apply(@ca) end end describe "and an array of names was provided" do it "should print each named certificate if found" do @applier = @class.new(:print, :to => %w{host1 host2}) @ca.expects(:print).with("host1").returns "h1" @applier.expects(:puts).with "h1" @ca.expects(:print).with("host2").returns "h2" @applier.expects(:puts).with "h2" @applier.apply(@ca) end it "should log any named but not found certificates" do @applier = @class.new(:print, :to => %w{host1 host2}) @ca.expects(:print).with("host1").returns "h1" @applier.expects(:puts).with "h1" @ca.expects(:print).with("host2").returns nil Puppet.expects(:err).with { |msg| msg.include?("host2") } @applier.apply(@ca) end end end describe ":fingerprint" do it "should fingerprint with the set digest algorithm" do @applier = @class.new(:fingerprint, :to => %w{host1}, :digest => :digest) @ca.expects(:fingerprint).with("host1", :digest).returns "fingerprint1" @applier.expects(:puts).with "host1 fingerprint1" @applier.apply(@ca) end describe "and :all was provided" do it "should fingerprint all certificates (including waiting ones)" do @ca.expects(:list).returns %w{host1} @ca.expects(:waiting?).returns %w{host2} @applier = @class.new(:fingerprint, :to => :all) @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1" @applier.expects(:puts).with "host1 fingerprint1" @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2" @applier.expects(:puts).with "host2 fingerprint2" @applier.apply(@ca) end end describe "and an array of names was provided" do it "should print each named certificate if found" do @applier = @class.new(:fingerprint, :to => %w{host1 host2}) @ca.expects(:fingerprint).with("host1", :MD5).returns "fingerprint1" @applier.expects(:puts).with "host1 fingerprint1" @ca.expects(:fingerprint).with("host2", :MD5).returns "fingerprint2" @applier.expects(:puts).with "host2 fingerprint2" @applier.apply(@ca) end end end end end diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb index 76633b48f..3aedfdc25 100755 --- a/spec/unit/ssl/certificate_authority_spec.rb +++ b/spec/unit/ssl/certificate_authority_spec.rb @@ -1,773 +1,772 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate_authority' describe Puppet::SSL::CertificateAuthority do after do Puppet::Util::Cacher.expire Puppet.settings.clearused end def stub_ca_host @key = mock 'key' @key.stubs(:content).returns "cakey" @cacert = mock 'certificate' @cacert.stubs(:content).returns "cacertificate" @host = stub 'ssl_host', :key => @key, :certificate => @cacert, :name => Puppet::SSL::Host.ca_name end it "should have a class method for returning a singleton instance" do Puppet::SSL::CertificateAuthority.should respond_to(:instance) end describe "when finding an existing instance" do describe "and the host is a CA host and the run_mode is master" do before do Puppet.settings.stubs(:value).with(:ca).returns true Puppet.run_mode.stubs(:master?).returns true @ca = mock('ca') Puppet::SSL::CertificateAuthority.stubs(:new).returns @ca end it "should return an instance" do Puppet::SSL::CertificateAuthority.instance.should equal(@ca) end it "should always return the same instance" do Puppet::SSL::CertificateAuthority.instance.should equal(Puppet::SSL::CertificateAuthority.instance) end end describe "and the host is not a CA host" do it "should return nil" do Puppet.settings.stubs(:value).with(:ca).returns false Puppet.run_mode.stubs(:master?).returns true ca = mock('ca') Puppet::SSL::CertificateAuthority.expects(:new).never Puppet::SSL::CertificateAuthority.instance.should be_nil end end describe "and the run_mode is not master" do it "should return nil" do Puppet.settings.stubs(:value).with(:ca).returns true Puppet.run_mode.stubs(:master?).returns false ca = mock('ca') Puppet::SSL::CertificateAuthority.expects(:new).never Puppet::SSL::CertificateAuthority.instance.should be_nil end end end describe "when initializing" do before do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "ca_testing" Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) end it "should always set its name to the value of :certname" do Puppet.settings.expects(:value).with(:certname).returns "ca_testing" Puppet::SSL::CertificateAuthority.new.name.should == "ca_testing" end it "should create an SSL::Host instance whose name is the 'ca_name'" do Puppet::SSL::Host.expects(:ca_name).returns "caname" host = stub 'host' Puppet::SSL::Host.expects(:new).with("caname").returns host Puppet::SSL::CertificateAuthority.new end it "should use the :main, :ca, and :ssl settings sections" do Puppet.settings.expects(:use).with(:main, :ssl, :ca) Puppet::SSL::CertificateAuthority.new end it "should create an inventory instance" do Puppet::SSL::Inventory.expects(:new).returns "inventory" Puppet::SSL::CertificateAuthority.new.inventory.should == "inventory" end it "should make sure the CA is set up" do Puppet::SSL::CertificateAuthority.any_instance.expects(:setup) Puppet::SSL::CertificateAuthority.new end end describe "when setting itself up" do it "should generate the CA certificate if it does not have one" do Puppet.settings.stubs :use host = stub 'host' Puppet::SSL::Host.stubs(:new).returns host host.expects(:certificate).returns nil Puppet::SSL::CertificateAuthority.any_instance.expects(:generate_ca_certificate) Puppet::SSL::CertificateAuthority.new end end describe "when retrieving the certificate revocation list" do before do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "ca_testing" Puppet.settings.stubs(:value).with(:cacrl).returns "/my/crl" cert = stub("certificate", :content => "real_cert") key = stub("key", :content => "real_key") @host = stub 'host', :certificate => cert, :name => "hostname", :key => key Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) @ca = Puppet::SSL::CertificateAuthority.new @ca.stubs(:host).returns @host end it "should return any found CRL instance" do crl = mock 'crl' Puppet::SSL::CertificateRevocationList.indirection.expects(:find).returns crl @ca.crl.should equal(crl) end it "should create, generate, and save a new CRL instance of no CRL can be found" do crl = Puppet::SSL::CertificateRevocationList.new("fakename") Puppet::SSL::CertificateRevocationList.indirection.expects(:find).returns nil Puppet::SSL::CertificateRevocationList.expects(:new).returns crl crl.expects(:generate).with(@ca.host.certificate.content, @ca.host.key.content) Puppet::SSL::CertificateRevocationList.indirection.expects(:save).with(crl) @ca.crl.should equal(crl) end end describe "when generating a self-signed CA certificate" do before do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "ca_testing" Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) Puppet::SSL::CertificateAuthority.any_instance.stubs(:crl) @ca = Puppet::SSL::CertificateAuthority.new @host = stub 'host', :key => mock("key"), :name => "hostname", :certificate => mock('certificate') Puppet::SSL::CertificateRequest.any_instance.stubs(:generate) @ca.stubs(:host).returns @host end it "should create and store a password at :capass" do Puppet.settings.expects(:value).with(:capass).returns "/path/to/pass" FileTest.expects(:exist?).with("/path/to/pass").returns false fh = mock 'filehandle' Puppet.settings.expects(:write).with(:capass).yields fh fh.expects(:print).with { |s| s.length > 18 } @ca.stubs(:sign) @ca.generate_ca_certificate end it "should generate a key if one does not exist" do @ca.stubs :generate_password @ca.stubs :sign @ca.host.expects(:key).returns nil @ca.host.expects(:generate_key) @ca.generate_ca_certificate end it "should create and sign a self-signed cert using the CA name" do request = mock 'request' Puppet::SSL::CertificateRequest.expects(:new).with(@ca.host.name).returns request request.expects(:generate).with(@ca.host.key) @ca.expects(:sign).with(@host.name, :ca, request) @ca.stubs :generate_password @ca.generate_ca_certificate end it "should generate its CRL" do @ca.stubs :generate_password @ca.stubs :sign @ca.host.expects(:key).returns nil @ca.host.expects(:generate_key) @ca.expects(:crl) @ca.generate_ca_certificate end end describe "when signing" do before do Puppet.settings.stubs(:use) Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true stub_ca_host Puppet::SSL::Host.expects(:new).with(Puppet::SSL::Host.ca_name).returns @host @ca = Puppet::SSL::CertificateAuthority.new @name = "myhost" @real_cert = stub 'realcert', :sign => nil @cert = Puppet::SSL::Certificate.new(@name) @cert.content = @real_cert Puppet::SSL::Certificate.stubs(:new).returns @cert @cert.stubs(:content=) Puppet::SSL::Certificate.indirection.stubs(:save) # Stub out the factory @factory = stub 'factory', :result => "my real cert" Puppet::SSL::CertificateFactory.stubs(:new).returns @factory @request = stub 'request', :content => "myrequest", :name => @name # And the inventory @inventory = stub 'inventory', :add => nil @ca.stubs(:inventory).returns @inventory Puppet::SSL::CertificateRequest.indirection.stubs(:destroy) end describe "and calculating the next certificate serial number" do before do @path = "/path/to/serial" Puppet.settings.stubs(:value).with(:serial).returns @path @filehandle = stub 'filehandle', :<< => @filehandle Puppet.settings.stubs(:readwritelock).with(:serial).yields @filehandle end it "should default to 0x1 for the first serial number" do @ca.next_serial.should == 0x1 end it "should return the current content of the serial file" do FileTest.stubs(:exist?).with(@path).returns true File.expects(:read).with(@path).returns "0002" @ca.next_serial.should == 2 end it "should write the next serial number to the serial file as hex" do @filehandle.expects(:<<).with("0002") @ca.next_serial end it "should lock the serial file while writing" do Puppet.settings.expects(:readwritelock).with(:serial) @ca.next_serial end end describe "its own certificate" do before do @serial = 10 @ca.stubs(:next_serial).returns @serial end it "should not look up a certificate request for the host" do Puppet::SSL::CertificateRequest.indirection.expects(:find).never @ca.sign(@name, :ca, @request) end it "should use a certificate type of :ca" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[0] == :ca end.returns @factory @ca.sign(@name, :ca, @request) end it "should pass the provided CSR as the CSR" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[1] == "myrequest" end.returns @factory @ca.sign(@name, :ca, @request) end it "should use the provided CSR's content as the issuer" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[2] == "myrequest" end.returns @factory @ca.sign(@name, :ca, @request) end it "should pass the next serial as the serial number" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[3] == @serial end.returns @factory @ca.sign(@name, :ca, @request) end it "should save the resulting certificate" do Puppet::SSL::Certificate.indirection.expects(:save).with(@cert) @ca.sign(@name, :ca, @request) end end describe "another host's certificate" do before do @serial = 10 @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::CertificateRequest.indirection.stubs :save end it "should use a certificate type of :server" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[0] == :server end.returns @factory @ca.sign(@name) end it "should use look up a CSR for the host in the :ca_file terminus" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with(@name).returns @request @ca.sign(@name) end it "should fail if no CSR can be found for the host" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with(@name).returns nil lambda { @ca.sign(@name) }.should raise_error(ArgumentError) end it "should use the CA certificate as the issuer" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[2] == @cacert.content end.returns @factory @ca.sign(@name) end it "should pass the next serial as the serial number" do Puppet::SSL::CertificateFactory.expects(:new).with do |*args| args[3] == @serial end.returns @factory @ca.sign(@name) end it "should sign the resulting certificate using its real key and a digest" do digest = mock 'digest' OpenSSL::Digest::SHA1.expects(:new).returns digest key = stub 'key', :content => "real_key" @ca.host.stubs(:key).returns key @cert.content.expects(:sign).with("real_key", digest) @ca.sign(@name) end it "should save the resulting certificate" do Puppet::SSL::Certificate.indirection.stubs(:save).with(@cert) @ca.sign(@name) end it "should remove the host's certificate request" do Puppet::SSL::CertificateRequest.indirection.expects(:destroy).with(@name) @ca.sign(@name) end end it "should create a certificate instance with the content set to the newly signed x509 certificate" do @serial = 10 @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::Certificate.indirection.stubs :save Puppet::SSL::Certificate.expects(:new).with(@name).returns @cert @ca.sign(@name) end it "should return the certificate instance" do @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::Certificate.indirection.stubs :save @ca.sign(@name).should equal(@cert) end it "should add the certificate to its inventory" do @ca.stubs(:next_serial).returns @serial @inventory.expects(:add).with(@cert) Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::Certificate.indirection.stubs :save @ca.sign(@name) end it "should have a method for triggering autosigning of available CSRs" do @ca.should respond_to(:autosign) end describe "when autosigning certificates" do it "should do nothing if autosign is disabled" do Puppet.settings.expects(:value).with(:autosign).returns 'false' Puppet::SSL::CertificateRequest.indirection.expects(:search).never @ca.autosign end it "should do nothing if no autosign.conf exists" do Puppet.settings.expects(:value).with(:autosign).returns '/auto/sign' FileTest.expects(:exist?).with("/auto/sign").returns false Puppet::SSL::CertificateRequest.indirection.expects(:search).never @ca.autosign end describe "and autosign is enabled and the autosign.conf file exists" do before do Puppet.settings.stubs(:value).with(:autosign).returns '/auto/sign' FileTest.stubs(:exist?).with("/auto/sign").returns true File.stubs(:readlines).with("/auto/sign").returns ["one\n", "two\n"] Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [] @store = stub 'store', :allow => nil Puppet::Network::AuthStore.stubs(:new).returns @store end describe "when creating the AuthStore instance to verify autosigning" do it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do Puppet::Network::AuthStore.expects(:new).returns @store @store.expects(:allow).with("one") @store.expects(:allow).with("two") @ca.autosign end it "should reparse the autosign configuration on each call" do Puppet::Network::AuthStore.expects(:new).times(2).returns @store @ca.autosign @ca.autosign end it "should ignore comments" do File.stubs(:readlines).with("/auto/sign").returns ["one\n", "#two\n"] @store.expects(:allow).with("one") @ca.autosign end it "should ignore blank lines" do File.stubs(:readlines).with("/auto/sign").returns ["one\n", "\n"] @store.expects(:allow).with("one") @ca.autosign end end it "should sign all CSRs whose hostname matches the autosign configuration" do csr1 = mock 'csr1' csr2 = mock 'csr2' Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [csr1, csr2] end it "should not sign CSRs whose hostname does not match the autosign configuration" do csr1 = mock 'csr1' csr2 = mock 'csr2' Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [csr1, csr2] end end end end describe "when managing certificate clients" do before do Puppet.settings.stubs(:use) Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true stub_ca_host Puppet::SSL::Host.expects(:new).returns @host Puppet::SSL::CertificateAuthority.any_instance.stubs(:host).returns @host @cacert = mock 'certificate' @cacert.stubs(:content).returns "cacertificate" @ca = Puppet::SSL::CertificateAuthority.new end it "should have a method for acting on the SSL files" do @ca.should respond_to(:apply) end describe "when applying a method to a set of hosts" do it "should fail if no subjects have been specified" do lambda { @ca.apply(:generate) }.should raise_error(ArgumentError) end it "should create an Interface instance with the specified method and the options" do Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :to => :host).returns(stub('applier', :apply => nil)) @ca.apply(:generate, :to => :host) end it "should apply the Interface with itself as the argument" do applier = stub('applier') applier.expects(:apply).with(@ca) Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns applier @ca.apply(:generate, :to => :ca_testing) end end it "should be able to list waiting certificate requests" do req1 = stub 'req1', :name => "one" req2 = stub 'req2', :name => "two" Puppet::SSL::CertificateRequest.indirection.expects(:search).with("*").returns [req1, req2] @ca.waiting?.should == %w{one two} end it "should delegate removing hosts to the Host class" do Puppet::SSL::Host.expects(:destroy).with("myhost") @ca.destroy("myhost") end it "should be able to verify certificates" do @ca.should respond_to(:verify) end it "should list certificates as the sorted list of all existing signed certificates" do cert1 = stub 'cert1', :name => "cert1" cert2 = stub 'cert2', :name => "cert2" Puppet::SSL::Certificate.indirection.expects(:search).with("*").returns [cert1, cert2] @ca.list.should == %w{cert1 cert2} end describe "and printing certificates" do it "should return nil if the certificate cannot be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns nil @ca.print("myhost").should be_nil end it "should print certificates by calling :to_text on the host's certificate" do cert1 = stub 'cert1', :name => "cert1", :to_text => "mytext" Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns cert1 @ca.print("myhost").should == "mytext" end end describe "and fingerprinting certificates" do before :each do @cert = stub 'cert', :name => "cert", :fingerprint => "DIGEST" Puppet::SSL::Certificate.indirection.stubs(:find).with("myhost").returns @cert Puppet::SSL::CertificateRequest.indirection.stubs(:find).with("myhost") end it "should raise an error if the certificate or CSR cannot be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns nil Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myhost").returns nil lambda { @ca.fingerprint("myhost") }.should raise_error end it "should try to find a CSR if no certificate can be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns nil Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myhost").returns @cert @cert.expects(:fingerprint) @ca.fingerprint("myhost") end it "should delegate to the certificate fingerprinting" do @cert.expects(:fingerprint) @ca.fingerprint("myhost") end it "should propagate the digest algorithm to the certificate fingerprinting system" do @cert.expects(:fingerprint).with(:digest) @ca.fingerprint("myhost", :digest) end end describe "and verifying certificates" do before do @store = stub 'store', :verify => true, :add_file => nil, :purpose= => nil, :add_crl => true, :flags= => nil OpenSSL::X509::Store.stubs(:new).returns @store Puppet.settings.stubs(:value).returns "crtstuff" @cert = stub 'cert', :content => "mycert" Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert @crl = stub('crl', :content => "mycrl") @ca.stubs(:crl).returns @crl end it "should fail if the host's certificate cannot be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("me").returns(nil) lambda { @ca.verify("me") }.should raise_error(ArgumentError) end it "should create an SSL Store to verify" do OpenSSL::X509::Store.expects(:new).returns @store @ca.verify("me") end it "should add the CA Certificate to the store" do Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" @store.expects(:add_file).with "/ca/cert" @ca.verify("me") end it "should add the CRL to the store if the crl is enabled" do @store.expects(:add_crl).with "mycrl" @ca.verify("me") end it "should set the store purpose to OpenSSL::X509::PURPOSE_SSL_CLIENT" do Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" @store.expects(:add_file).with "/ca/cert" @ca.verify("me") end it "should set the store flags to check the crl" do @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK @ca.verify("me") end it "should use the store to verify the certificate" do @cert.expects(:content).returns "mycert" @store.expects(:verify).with("mycert").returns true @ca.verify("me") end it "should fail if the verification returns false" do @cert.expects(:content).returns "mycert" @store.expects(:verify).with("mycert").returns false lambda { @ca.verify("me") }.should raise_error end end describe "and revoking certificates" do before do @crl = mock 'crl' @ca.stubs(:crl).returns @crl @ca.stubs(:next_serial).returns 10 @real_cert = stub 'real_cert', :serial => 15 @cert = stub 'cert', :content => @real_cert Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert end it "should fail if the certificate revocation list is disabled" do @ca.stubs(:crl).returns false lambda { @ca.revoke('ca_testing') }.should raise_error(ArgumentError) end it "should delegate the revocation to its CRL" do @ca.crl.expects(:revoke) @ca.revoke('host') end it "should get the serial number from the local certificate if it exists" do @ca.crl.expects(:revoke).with { |serial, key| serial == 15 } Puppet::SSL::Certificate.indirection.expects(:find).with("host").returns @cert @ca.revoke('host') end it "should get the serial number from inventory if no local certificate exists" do real_cert = stub 'real_cert', :serial => 15 cert = stub 'cert', :content => real_cert Puppet::SSL::Certificate.indirection.expects(:find).with("host").returns nil @ca.inventory.expects(:serial).with("host").returns 16 @ca.crl.expects(:revoke).with { |serial, key| serial == 16 } @ca.revoke('host') end end it "should be able to generate a complete new SSL host" do @ca.should respond_to(:generate) end describe "and generating certificates" do before do @host = stub 'host', :generate_certificate_request => nil Puppet::SSL::Host.stubs(:new).returns @host Puppet::SSL::Certificate.indirection.stubs(:find).returns nil @ca.stubs(:sign) end it "should fail if a certificate already exists for the host" do Puppet::SSL::Certificate.indirection.expects(:find).with("him").returns "something" lambda { @ca.generate("him") }.should raise_error(ArgumentError) end it "should create a new Host instance with the correct name" do Puppet::SSL::Host.expects(:new).with("him").returns @host @ca.generate("him") end it "should use the Host to generate the certificate request" do @host.expects :generate_certificate_request @ca.generate("him") end it "should sign the generated request" do @ca.expects(:sign).with("him") @ca.generate("him") end end end end diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb index c2bcb19ce..f34dafe43 100755 --- a/spec/unit/ssl/certificate_factory_spec.rb +++ b/spec/unit/ssl/certificate_factory_spec.rb @@ -1,107 +1,106 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate_factory' describe Puppet::SSL::CertificateFactory do before do @cert_type = mock 'cert_type' @name = mock 'name' @csr = stub 'csr', :subject => @name @issuer = mock 'issuer' @serial = mock 'serial' @factory = Puppet::SSL::CertificateFactory.new(@cert_type, @csr, @issuer, @serial) end describe "when initializing" do it "should set its :cert_type to its first argument" do @factory.cert_type.should equal(@cert_type) end it "should set its :csr to its second argument" do @factory.csr.should equal(@csr) end it "should set its :issuer to its third argument" do @factory.issuer.should equal(@issuer) end it "should set its :serial to its fourth argument" do @factory.serial.should equal(@serial) end it "should set its name to the subject of the csr" do @factory.name.should equal(@name) end end describe "when generating the certificate" do before do @cert = mock 'cert' @cert.stub_everything @factory.stubs :build_extensions @factory.stubs :set_ttl @issuer_name = mock 'issuer_name' @issuer.stubs(:subject).returns @issuer_name @public_key = mock 'public_key' @csr.stubs(:public_key).returns @public_key OpenSSL::X509::Certificate.stubs(:new).returns @cert end it "should return a new X509 certificate" do OpenSSL::X509::Certificate.expects(:new).returns @cert @factory.result.should equal(@cert) end it "should set the certificate's version to 2" do @cert.expects(:version=).with 2 @factory.result end it "should set the certificate's subject to the CSR's subject" do @cert.expects(:subject=).with @name @factory.result end it "should set the certificate's issuer to the Issuer's subject" do @cert.expects(:issuer=).with @issuer_name @factory.result end it "should set the certificate's public key to the CSR's public key" do @cert.expects(:public_key=).with @public_key @factory.result end it "should set the certificate's serial number to the provided serial number" do @cert.expects(:serial=).with @serial @factory.result end it "should build extensions for the certificate" do @factory.expects(:build_extensions) @factory.result end it "should set the ttl of the certificate" do @factory.expects(:set_ttl) @factory.result end end describe "when building extensions" do it "should have tests" end describe "when setting the ttl" do it "should have tests" end end diff --git a/spec/unit/ssl/certificate_request_spec.rb b/spec/unit/ssl/certificate_request_spec.rb index 32c9cb51d..e45f0130b 100755 --- a/spec/unit/ssl/certificate_request_spec.rb +++ b/spec/unit/ssl/certificate_request_spec.rb @@ -1,217 +1,216 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate_request' require 'puppet/ssl/key' describe Puppet::SSL::CertificateRequest do before do @class = Puppet::SSL::CertificateRequest end it "should be extended with the Indirector module" do @class.singleton_class.should be_include(Puppet::Indirector) end it "should indirect certificate_request" do @class.indirection.name.should == :certificate_request end it "should use any provided name as its name" do @class.new("myname").name.should == "myname" end it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do it "should create a CSR instance with its name set to the CSR subject and its content set to the extracted CSR" do csr = stub 'csr', :subject => "/CN=Foo.madstop.com" OpenSSL::X509::Request.expects(:new).with("my csr").returns(csr) mycsr = stub 'sslcsr' mycsr.expects(:content=).with(csr) @class.expects(:new).with("foo.madstop.com").returns mycsr @class.from_s("my csr") end end describe "when managing instances" do before do @request = @class.new("myname") end it "should have a name attribute" do @request.name.should == "myname" end it "should downcase its name" do @class.new("MyName").name.should == "myname" end it "should have a content attribute" do @request.should respond_to(:content) end it "should be able to read requests from disk" do path = "/my/path" File.expects(:read).with(path).returns("my request") request = mock 'request' OpenSSL::X509::Request.expects(:new).with("my request").returns(request) @request.read(path).should equal(request) @request.content.should equal(request) end it "should return an empty string when converted to a string with no request" do @request.to_s.should == "" end it "should convert the request to pem format when converted to a string" do request = mock 'request', :to_pem => "pem" @request.content = request @request.to_s.should == "pem" end it "should have a :to_text method that it delegates to the actual key" do real_request = mock 'request' real_request.expects(:to_text).returns "requesttext" @request.content = real_request @request.to_text.should == "requesttext" end end describe "when generating" do before do @instance = @class.new("myname") key = Puppet::SSL::Key.new("myname") @key = key.generate @request = OpenSSL::X509::Request.new OpenSSL::X509::Request.expects(:new).returns(@request) @request.stubs(:verify).returns(true) end it "should use the content of the provided key if the key is a Puppet::SSL::Key instance" do key = Puppet::SSL::Key.new("test") key.expects(:content).returns @key @request.expects(:sign).with{ |key, digest| key == @key } @instance.generate(key) end it "should log that it is creating a new certificate request" do Puppet.expects(:info).twice @instance.generate(@key) end it "should set the subject to [CN, name]" do subject = mock 'subject' OpenSSL::X509::Name.expects(:new).with([["CN", @instance.name]]).returns(subject) @request.expects(:subject=).with(subject) @instance.generate(@key) end it "should set the CN to the CSR name when the CSR is not for a CA" do subject = mock 'subject' OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == @instance.name }.returns(subject) @request.expects(:subject=).with(subject) @instance.generate(@key) end it "should set the CN to the :ca_name setting when the CSR is for a CA" do subject = mock 'subject' Puppet.settings.expects(:value).with(:ca_name).returns "mycertname" OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == "mycertname" }.returns(subject) @request.expects(:subject=).with(subject) Puppet::SSL::CertificateRequest.new(Puppet::SSL::CA_NAME).generate(@key) end it "should set the version to 0" do @request.expects(:version=).with(0) @instance.generate(@key) end it "should set the public key to the provided key's public key" do # Yay, the private key extracts a new key each time. pubkey = @key.public_key @key.stubs(:public_key).returns pubkey @request.expects(:public_key=).with(@key.public_key) @instance.generate(@key) end it "should sign the csr with the provided key and a digest" do digest = mock 'digest' OpenSSL::Digest::MD5.expects(:new).returns(digest) @request.expects(:sign).with(@key, digest) @instance.generate(@key) end it "should verify the generated request using the public key" do # Stupid keys don't have a competent == method. @request.expects(:verify).with { |public_key| public_key.to_s == @key.public_key.to_s }.returns true @instance.generate(@key) end it "should fail if verification fails" do @request.expects(:verify).returns false lambda { @instance.generate(@key) }.should raise_error(Puppet::Error) end it "should fingerprint the request" do @instance.expects(:fingerprint) @instance.generate(@key) end it "should display the fingerprint" do Puppet.stubs(:info) @instance.stubs(:fingerprint).returns("FINGERPRINT") Puppet.expects(:info).with { |s| s =~ /FINGERPRINT/ } @instance.generate(@key) end it "should return the generated request" do @instance.generate(@key).should equal(@request) end it "should set its content to the generated request" do @instance.generate(@key) @instance.content.should equal(@request) end end describe "when a CSR is saved" do describe "and a CA is available" do it "should save the CSR and trigger autosigning" do ca = mock 'ca', :autosign Puppet::SSL::CertificateAuthority.expects(:instance).returns ca csr = Puppet::SSL::CertificateRequest.new("me") terminus = mock 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |request| request.instance == csr && request.key == "me" } Puppet::SSL::CertificateRequest.indirection.save(csr) end end describe "and a CA is not available" do it "should save the CSR" do Puppet::SSL::CertificateAuthority.expects(:instance).returns nil csr = Puppet::SSL::CertificateRequest.new("me") terminus = mock 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |request| request.instance == csr && request.key == "me" } Puppet::SSL::CertificateRequest.indirection.save(csr) end end end end diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index e83ad9cc5..99058b353 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -1,168 +1,167 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate_revocation_list' describe Puppet::SSL::CertificateRevocationList do before do @cert = stub 'cert', :subject => "mysubject" @key = stub 'key', :private? => true @class = Puppet::SSL::CertificateRevocationList end it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do it "should create a CRL instance with its name set to 'foo' and its content set to the extracted CRL" do crl = stub 'crl' OpenSSL::X509::CRL.expects(:new).returns(crl) mycrl = stub 'sslcrl' mycrl.expects(:content=).with(crl) @class.expects(:new).with("foo").returns mycrl @class.from_s("my crl").should == mycrl end end describe "when an instance" do before do @class.any_instance.stubs(:read_or_generate) @crl = @class.new("whatever") end it "should always use 'crl' for its name" do @crl.name.should == "crl" end it "should have a content attribute" do @crl.should respond_to(:content) end end describe "when generating the crl" do before do @real_crl = mock 'crl' @real_crl.stub_everything OpenSSL::X509::CRL.stubs(:new).returns(@real_crl) @class.any_instance.stubs(:read_or_generate) @crl = @class.new("crl") end it "should set its issuer to the subject of the passed certificate" do @real_crl.expects(:issuer=).with(@cert.subject) @crl.generate(@cert, @key) end it "should set its version to 1" do @real_crl.expects(:version=).with(1) @crl.generate(@cert, @key) end it "should create an instance of OpenSSL::X509::CRL" do OpenSSL::X509::CRL.expects(:new).returns(@real_crl) @crl.generate(@cert, @key) end # The next three tests aren't good, but at least they # specify the behaviour. it "should add an extension for the CRL number" do @real_crl.expects(:extensions=) @crl.generate(@cert, @key) end it "should set the last update time" do @real_crl.expects(:last_update=) @crl.generate(@cert, @key) end it "should set the next update time" do @real_crl.expects(:next_update=) @crl.generate(@cert, @key) end it "should sign the CRL" do @real_crl.expects(:sign).with { |key, digest| key == @key } @crl.generate(@cert, @key) end it "should set the content to the generated crl" do @crl.generate(@cert, @key) @crl.content.should equal(@real_crl) end it "should return the generated crl" do @crl.generate(@cert, @key).should equal(@real_crl) end end # This test suite isn't exactly complete, because the # SSL stuff is very complicated. It just hits the high points. describe "when revoking a certificate" do before do @class.wrapped_class.any_instance.stubs(:issuer=) @class.wrapped_class.any_instance.stubs(:sign) @crl = @class.new("crl") @crl.generate(@cert, @key) @crl.content.stubs(:sign) Puppet::SSL::CertificateRevocationList.indirection.stubs :save @key = mock 'key' end it "should require a serial number and the CA's private key" do lambda { @crl.revoke }.should raise_error(ArgumentError) end it "should default to OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE as the revocation reason" do # This makes it a bit more of an integration test than we'd normally like, but that's life # with openssl. reason = OpenSSL::ASN1::Enumerated(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE) OpenSSL::ASN1.expects(:Enumerated).with(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE).returns reason @crl.revoke(1, @key) end it "should mark the CRL as updated" do time = Time.now Time.stubs(:now).returns time @crl.content.expects(:last_update=).with(time) @crl.revoke(1, @key) end it "should mark the CRL valid for five years" do time = Time.now Time.stubs(:now).returns time @crl.content.expects(:next_update=).with(time + (5 * 365*24*60*60)) @crl.revoke(1, @key) end it "should sign the CRL with the CA's private key and a digest instance" do @crl.content.expects(:sign).with { |key, digest| key == @key and digest.is_a?(OpenSSL::Digest::SHA1) } @crl.revoke(1, @key) end it "should save the CRL" do Puppet::SSL::CertificateRevocationList.indirection.expects(:save).with(@crl, nil) @crl.revoke(1, @key) end end end diff --git a/spec/unit/ssl/certificate_spec.rb b/spec/unit/ssl/certificate_spec.rb index b3af9f236..0b635f2bc 100755 --- a/spec/unit/ssl/certificate_spec.rb +++ b/spec/unit/ssl/certificate_spec.rb @@ -1,124 +1,123 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/certificate' describe Puppet::SSL::Certificate do before do @class = Puppet::SSL::Certificate end after do @class.instance_variable_set("@ca_location", nil) end it "should be extended with the Indirector module" do @class.singleton_class.should be_include(Puppet::Indirector) end it "should indirect certificate" do @class.indirection.name.should == :certificate end it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do it "should create a certificate instance with its name set to the certificate subject and its content set to the extracted certificate" do cert = stub 'certificate', :subject => "/CN=Foo.madstop.com" OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(cert) mycert = stub 'sslcert' mycert.expects(:content=).with(cert) @class.expects(:new).with("foo.madstop.com").returns mycert @class.from_s("my certificate") end it "should create multiple certificate instances when asked" do cert1 = stub 'cert1' @class.expects(:from_s).with("cert1").returns cert1 cert2 = stub 'cert2' @class.expects(:from_s).with("cert2").returns cert2 @class.from_multiple_s("cert1\n---\ncert2").should == [cert1, cert2] end end describe "when converting to a string" do before do @certificate = @class.new("myname") end it "should return an empty string when it has no certificate" do @certificate.to_s.should == "" end it "should convert the certificate to pem format" do certificate = mock 'certificate', :to_pem => "pem" @certificate.content = certificate @certificate.to_s.should == "pem" end it "should be able to convert multiple instances to a string" do cert2 = @class.new("foo") @certificate.expects(:to_s).returns "cert1" cert2.expects(:to_s).returns "cert2" @class.to_multiple_s([@certificate, cert2]).should == "cert1\n---\ncert2" end end describe "when managing instances" do before do @certificate = @class.new("myname") end it "should have a name attribute" do @certificate.name.should == "myname" end it "should convert its name to a string and downcase it" do @class.new(:MyName).name.should == "myname" end it "should have a content attribute" do @certificate.should respond_to(:content) end it "should return a nil expiration if there is no actual certificate" do @certificate.stubs(:content).returns nil @certificate.expiration.should be_nil end it "should use the expiration of the certificate as its expiration date" do cert = stub 'cert' @certificate.stubs(:content).returns cert cert.expects(:not_after).returns "sometime" @certificate.expiration.should == "sometime" end it "should be able to read certificates from disk" do path = "/my/path" File.expects(:read).with(path).returns("my certificate") certificate = mock 'certificate' OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(certificate) @certificate.read(path).should equal(certificate) @certificate.content.should equal(certificate) end it "should have a :to_text method that it delegates to the actual key" do real_certificate = mock 'certificate' real_certificate.expects(:to_text).returns "certificatetext" @certificate.content = real_certificate @certificate.to_text.should == "certificatetext" end end end diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb index 885bd45e2..2624c2234 100755 --- a/spec/unit/ssl/host_spec.rb +++ b/spec/unit/ssl/host_spec.rb @@ -1,796 +1,795 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/host' require 'puppet/sslcertificates' require 'puppet/sslcertificates/ca' describe Puppet::SSL::Host do before do Puppet::SSL::Host.indirection.terminus_class = :file @host = Puppet::SSL::Host.new("myname") end after do # Cleaned out any cached localhost instance. Puppet::Util::Cacher.expire Puppet::SSL::Host.ca_location = :none end it "should use any provided name as its name" do @host.name.should == "myname" end it "should retrieve its public key from its private key" do realkey = mock 'realkey' key = stub 'key', :content => realkey Puppet::SSL::Key.indirection.stubs(:find).returns(key) pubkey = mock 'public_key' realkey.expects(:public_key).returns pubkey @host.public_key.should equal(pubkey) end it "should default to being a non-ca host" do @host.ca?.should be_false end it "should be a ca host if its name matches the CA_NAME" do Puppet::SSL::Host.stubs(:ca_name).returns "yayca" Puppet::SSL::Host.new("yayca").should be_ca end it "should have a method for determining the CA location" do Puppet::SSL::Host.should respond_to(:ca_location) end it "should have a method for specifying the CA location" do Puppet::SSL::Host.should respond_to(:ca_location=) end it "should have a method for retrieving the default ssl host" do Puppet::SSL::Host.should respond_to(:ca_location=) end it "should have a method for producing an instance to manage the local host's keys" do Puppet::SSL::Host.should respond_to(:localhost) end it "should generate the certificate for the localhost instance if no certificate is available" do host = stub 'host', :key => nil Puppet::SSL::Host.expects(:new).returns host host.expects(:certificate).returns nil host.expects(:generate) Puppet::SSL::Host.localhost.should equal(host) end it "should always read the key for the localhost instance in from disk" do host = stub 'host', :certificate => "eh" Puppet::SSL::Host.expects(:new).returns host host.expects(:key) Puppet::SSL::Host.localhost end it "should cache the localhost instance" do host = stub 'host', :certificate => "eh", :key => 'foo' Puppet::SSL::Host.expects(:new).once.returns host Puppet::SSL::Host.localhost.should == Puppet::SSL::Host.localhost end it "should be able to expire the cached instance" do one = stub 'host1', :certificate => "eh", :key => 'foo' two = stub 'host2', :certificate => "eh", :key => 'foo' Puppet::SSL::Host.expects(:new).times(2).returns(one).then.returns(two) Puppet::SSL::Host.localhost.should equal(one) Puppet::Util::Cacher.expire Puppet::SSL::Host.localhost.should equal(two) end it "should be able to verify its certificate matches its key" do Puppet::SSL::Host.new("foo").should respond_to(:certificate_matches_key?) end it "should consider the certificate invalid if it cannot find a key" do host = Puppet::SSL::Host.new("foo") host.expects(:key).returns nil host.should_not be_certificate_matches_key end it "should consider the certificate invalid if it cannot find a certificate" do host = Puppet::SSL::Host.new("foo") host.expects(:key).returns mock("key") host.expects(:certificate).returns nil host.should_not be_certificate_matches_key end it "should consider the certificate invalid if the SSL certificate's key verification fails" do host = Puppet::SSL::Host.new("foo") key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', :content => sslcert host.stubs(:key).returns key host.stubs(:certificate).returns certificate sslcert.expects(:check_private_key).with("private_key").returns false host.should_not be_certificate_matches_key end it "should consider the certificate valid if the SSL certificate's key verification succeeds" do host = Puppet::SSL::Host.new("foo") key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', :content => sslcert host.stubs(:key).returns key host.stubs(:certificate).returns certificate sslcert.expects(:check_private_key).with("private_key").returns true host.should be_certificate_matches_key end describe "when specifying the CA location" do it "should support the location ':local'" do lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error end it "should support the location ':remote'" do lambda { Puppet::SSL::Host.ca_location = :remote }.should_not raise_error end it "should support the location ':none'" do lambda { Puppet::SSL::Host.ca_location = :none }.should_not raise_error end it "should support the location ':only'" do lambda { Puppet::SSL::Host.ca_location = :only }.should_not raise_error end it "should not support other modes" do lambda { Puppet::SSL::Host.ca_location = :whatever }.should raise_error(ArgumentError) end describe "as 'local'" do before do Puppet::SSL::Host.ca_location = :local end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Certificate.indirection.cache_class.should == :file Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file end it "should set the terminus class for Key and Host as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file Puppet::SSL::Host.indirection.terminus_class.should == :file end it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do Puppet::SSL::Certificate.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca end end describe "as 'remote'" do before do Puppet::SSL::Host.ca_location = :remote end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Certificate.indirection.cache_class.should == :file Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file end it "should set the terminus class for Key as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file end it "should set the terminus class for Host, Certificate, CertificateRevocationList, and CertificateRequest as :rest" do Puppet::SSL::Host.indirection.terminus_class.should == :rest Puppet::SSL::Certificate.indirection.terminus_class.should == :rest Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :rest Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :rest end end describe "as 'only'" do before do Puppet::SSL::Host.ca_location = :only end it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do Puppet::SSL::Key.indirection.terminus_class.should == :ca Puppet::SSL::Certificate.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do Puppet::SSL::Certificate.indirection.cache_class.should be_nil Puppet::SSL::CertificateRequest.indirection.cache_class.should be_nil Puppet::SSL::CertificateRevocationList.indirection.cache_class.should be_nil end it "should set the terminus class for Host to :file" do Puppet::SSL::Host.indirection.terminus_class.should == :file end end describe "as 'none'" do before do Puppet::SSL::Host.ca_location = :none end it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file Puppet::SSL::Certificate.indirection.terminus_class.should == :file Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :file end it "should set the terminus class for Host to 'none'" do lambda { Puppet::SSL::Host.indirection.terminus_class }.should raise_error(Puppet::DevError) end end end it "should have a class method for destroying all files related to a given host" do Puppet::SSL::Host.should respond_to(:destroy) end describe "when destroying a host's SSL files" do before do Puppet::SSL::Key.indirection.stubs(:destroy).returns false Puppet::SSL::Certificate.indirection.stubs(:destroy).returns false Puppet::SSL::CertificateRequest.indirection.stubs(:destroy).returns false end it "should destroy its certificate, certificate request, and key" do Puppet::SSL::Key.indirection.expects(:destroy).with("myhost") Puppet::SSL::Certificate.indirection.expects(:destroy).with("myhost") Puppet::SSL::CertificateRequest.indirection.expects(:destroy).with("myhost") Puppet::SSL::Host.destroy("myhost") end it "should return true if any of the classes returned true" do Puppet::SSL::Certificate.indirection.expects(:destroy).with("myhost").returns true Puppet::SSL::Host.destroy("myhost").should be_true end it "should report that nothing was deleted if none of the classes returned true" do Puppet::SSL::Host.destroy("myhost").should == "Nothing was deleted" end end describe "when initializing" do it "should default its name to the :certname setting" do Puppet.settings.expects(:value).with(:certname).returns "myname" Puppet::SSL::Host.new.name.should == "myname" end it "should downcase a passed in name" do Puppet::SSL::Host.new("Host.Domain.Com").name.should == "host.domain.com" end it "should downcase the certname if it's used" do Puppet.settings.expects(:value).with(:certname).returns "Host.Domain.Com" Puppet::SSL::Host.new.name.should == "host.domain.com" end it "should indicate that it is a CA host if its name matches the ca_name constant" do Puppet::SSL::Host.stubs(:ca_name).returns "myca" Puppet::SSL::Host.new("myca").should be_ca end end describe "when managing its private key" do before do @realkey = "mykey" @key = Puppet::SSL::Key.new("mykey") @key.content = @realkey end it "should return nil if the key is not set and cannot be found" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(nil) @host.key.should be_nil end it "should find the key in the Key class and return the Puppet instance" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(@key) @host.key.should equal(@key) end it "should be able to generate and save a new key" do Puppet::SSL::Key.expects(:new).with("myname").returns(@key) @key.expects(:generate) Puppet::SSL::Key.indirection.expects(:save) @host.generate_key.should be_true @host.key.should equal(@key) end it "should not retain keys that could not be saved" do Puppet::SSL::Key.expects(:new).with("myname").returns(@key) @key.stubs(:generate) Puppet::SSL::Key.indirection.expects(:save).raises "eh" lambda { @host.generate_key }.should raise_error @host.key.should be_nil end it "should return any previously found key without requerying" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(@key).once @host.key.should equal(@key) @host.key.should equal(@key) end end describe "when managing its certificate request" do before do @realrequest = "real request" @request = Puppet::SSL::CertificateRequest.new("myname") @request.content = @realrequest end it "should return nil if the key is not set and cannot be found" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns(nil) @host.certificate_request.should be_nil end it "should find the request in the Key class and return it and return the Puppet SSL request" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns @request @host.certificate_request.should equal(@request) end it "should generate a new key when generating the cert request if no key exists" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.expects(:key).times(2).returns(nil).then.returns(key) @host.expects(:generate_key).returns(key) @request.stubs(:generate) Puppet::SSL::CertificateRequest.indirection.stubs(:save) @host.generate_certificate_request end it "should be able to generate and save a new request using the private key" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.stubs(:key).returns(key) @request.expects(:generate).with("mycontent") Puppet::SSL::CertificateRequest.indirection.expects(:save).with(@request) @host.generate_certificate_request.should be_true @host.certificate_request.should equal(@request) end it "should return any previously found request without requerying" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns(@request).once @host.certificate_request.should equal(@request) @host.certificate_request.should equal(@request) end it "should not keep its certificate request in memory if the request cannot be saved" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.stubs(:key).returns(key) @request.stubs(:generate) @request.stubs(:name).returns("myname") terminus = stub 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |req| req.instance == @request && req.key == "myname" }.raises "eh" lambda { @host.generate_certificate_request }.should raise_error @host.instance_eval { @certificate_request }.should be_nil end end describe "when managing its certificate" do before do @realcert = mock 'certificate' @cert = stub 'cert', :content => @realcert @host.stubs(:key).returns mock("key") @host.stubs(:certificate_matches_key?).returns true end it "should find the CA certificate if it does not have a certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.stubs(:find).with("myname").returns @cert @host.certificate end it "should not find the CA certificate if it is the CA host" do @host.expects(:ca?).returns true Puppet::SSL::Certificate.indirection.stubs(:find) Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).never @host.certificate end it "should return nil if it cannot find a CA certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns nil Puppet::SSL::Certificate.indirection.expects(:find).with("myname").never @host.certificate.should be_nil end it "should find the key if it does not have one" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns mock("key") @host.certificate end it "should generate the key if one cannot be found" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns nil @host.expects(:generate_key) @host.certificate end it "should find the certificate in the Certificate class and return the Puppet certificate instance" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns @cert @host.certificate.should equal(@cert) end it "should fail if the found certificate does not match the private key" do @host.expects(:certificate_matches_key?).returns false Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert lambda { @host.certificate }.should raise_error(Puppet::Error) end it "should return any previously found certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns(@cert).once @host.certificate.should equal(@cert) @host.certificate.should equal(@cert) end end it "should have a method for listing certificate hosts" do Puppet::SSL::Host.should respond_to(:search) end describe "when listing certificate hosts" do it "should default to listing all clients with any file types" do Puppet::SSL::Key.indirection.expects(:search).returns [] Puppet::SSL::Certificate.indirection.expects(:search).returns [] Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [] Puppet::SSL::Host.search end it "should be able to list only clients with a key" do Puppet::SSL::Key.indirection.expects(:search).returns [] Puppet::SSL::Certificate.indirection.expects(:search).never Puppet::SSL::CertificateRequest.indirection.expects(:search).never Puppet::SSL::Host.search :for => Puppet::SSL::Key end it "should be able to list only clients with a certificate" do Puppet::SSL::Key.indirection.expects(:search).never Puppet::SSL::Certificate.indirection.expects(:search).returns [] Puppet::SSL::CertificateRequest.indirection.expects(:search).never Puppet::SSL::Host.search :for => Puppet::SSL::Certificate end it "should be able to list only clients with a certificate request" do Puppet::SSL::Key.indirection.expects(:search).never Puppet::SSL::Certificate.indirection.expects(:search).never Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [] Puppet::SSL::Host.search :for => Puppet::SSL::CertificateRequest end it "should return a Host instance created with the name of each found instance" do key = stub 'key', :name => "key" cert = stub 'cert', :name => "cert" csr = stub 'csr', :name => "csr" Puppet::SSL::Key.indirection.expects(:search).returns [key] Puppet::SSL::Certificate.indirection.expects(:search).returns [cert] Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [csr] returned = [] %w{key cert csr}.each do |name| result = mock(name) returned << result Puppet::SSL::Host.expects(:new).with(name).returns result end result = Puppet::SSL::Host.search returned.each do |r| result.should be_include(r) end end end it "should have a method for generating all necessary files" do Puppet::SSL::Host.new("me").should respond_to(:generate) end describe "when generating files" do before do @host = Puppet::SSL::Host.new("me") @host.stubs(:generate_key) @host.stubs(:generate_certificate_request) end it "should generate a key if one is not present" do @host.stubs(:key).returns nil @host.expects(:generate_key) @host.generate end it "should generate a certificate request if one is not present" do @host.expects(:certificate_request).returns nil @host.expects(:generate_certificate_request) @host.generate end describe "and it can create a certificate authority" do before do @ca = mock 'ca' Puppet::SSL::CertificateAuthority.stubs(:instance).returns @ca end it "should use the CA to sign its certificate request if it does not have a certificate" do @host.expects(:certificate).returns nil @ca.expects(:sign).with(@host.name) @host.generate end end describe "and it cannot create a certificate authority" do before do Puppet::SSL::CertificateAuthority.stubs(:instance).returns nil end it "should seek its certificate" do @host.expects(:certificate) @host.generate end end end it "should have a method for creating an SSL store" do Puppet::SSL::Host.new("me").should respond_to(:ssl_store) end it "should always return the same store" do host = Puppet::SSL::Host.new("foo") store = mock 'store' store.stub_everything OpenSSL::X509::Store.expects(:new).returns store host.ssl_store.should equal(host.ssl_store) end describe "when creating an SSL store" do before do @host = Puppet::SSL::Host.new("me") @store = mock 'store' @store.stub_everything OpenSSL::X509::Store.stubs(:new).returns @store Puppet.settings.stubs(:value).with(:localcacert).returns "ssl_host_testing" Puppet::SSL::CertificateRevocationList.indirection.stubs(:find).returns(nil) end it "should accept a purpose" do @store.expects(:purpose=).with "my special purpose" @host.ssl_store("my special purpose") end it "should default to OpenSSL::X509::PURPOSE_ANY as the purpose" do @store.expects(:purpose=).with OpenSSL::X509::PURPOSE_ANY @host.ssl_store end it "should add the local CA cert file" do Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" @store.expects(:add_file).with "/ca/cert/file" @host.ssl_store end describe "and a CRL is available" do before do @crl = stub 'crl', :content => "real_crl" Puppet::SSL::CertificateRevocationList.indirection.stubs(:find).returns @crl Puppet.settings.stubs(:value).with(:certificate_revocation).returns true end it "should add the CRL" do @store.expects(:add_crl).with "real_crl" @host.ssl_store end it "should set the flags to OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK" do @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK @host.ssl_store end end end describe "when waiting for a cert" do before do @host = Puppet::SSL::Host.new("me") end it "should generate its certificate request and attempt to read the certificate again if no certificate is found" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate) @host.wait_for_cert(1) end it "should catch and log errors during CSR saving" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate).raises(RuntimeError).then.returns nil @host.stubs(:sleep) @host.wait_for_cert(1) end it "should sleep and retry after failures saving the CSR if waitforcert is enabled" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate).raises(RuntimeError).then.returns nil @host.expects(:sleep).with(1) @host.wait_for_cert(1) end it "should exit after failures saving the CSR of waitforcert is disabled" do @host.expects(:certificate).returns(nil) @host.expects(:generate).raises(RuntimeError) @host.expects(:puts) @host.expects(:exit).with(1).raises(SystemExit) lambda { @host.wait_for_cert(0) }.should raise_error(SystemExit) end it "should exit if the wait time is 0 and it can neither find nor retrieve a certificate" do @host.stubs(:certificate).returns nil @host.expects(:generate) @host.expects(:puts) @host.expects(:exit).with(1).raises(SystemExit) lambda { @host.wait_for_cert(0) }.should raise_error(SystemExit) end it "should sleep for the specified amount of time if no certificate is found after generating its certificate request" do @host.expects(:certificate).times(3).returns(nil).then.returns(nil).then.returns "foo" @host.expects(:generate) @host.expects(:sleep).with(1) @host.wait_for_cert(1) end it "should catch and log exceptions during certificate retrieval" do @host.expects(:certificate).times(3).returns(nil).then.raises(RuntimeError).then.returns("foo") @host.stubs(:generate) @host.stubs(:sleep) Puppet.expects(:err) @host.wait_for_cert(1) end end describe "when handling PSON" do include PuppetSpec::Files before do Puppet[:vardir] = tmpdir("ssl_test_vardir") Puppet[:ssldir] = tmpdir("ssl_test_ssldir") Puppet::SSLCertificates::CA.new.mkrootcert # localcacert is where each client stores the CA certificate # cacert is where the master stores the CA certificate # Since we need to play the role of both for testing we need them to be the same and exist Puppet[:cacert] = Puppet[:localcacert] @ca=Puppet::SSL::CertificateAuthority.new end describe "when converting to PSON" do it "should be able to identify a host with an unsigned certificate request" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request pson_hash = { "fingerprint" => host.certificate_request.fingerprint, "desired_state" => 'requested', "name" => host.name } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end it "should be able to identify a host with a signed certificate" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request @ca.sign(host.name) pson_hash = { "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint, "desired_state" => 'signed', "name" => host.name, } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end it "should be able to identify a host with a revoked certificate" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request @ca.sign(host.name) @ca.revoke(host.name) pson_hash = { "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint, "desired_state" => 'revoked', "name" => host.name, } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end end describe "when converting from PSON" do it "should return a Puppet::SSL::Host object with the specified desired state" do host = Puppet::SSL::Host.new("bazinga") host.desired_state="signed" pson_hash = { "name" => host.name, "desired_state" => host.desired_state, } generated_host = Puppet::SSL::Host.from_pson(pson_hash) generated_host.desired_state.should == host.desired_state generated_host.name.should == host.name end end end end diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb index 4d47a37c6..d8606b1b4 100755 --- a/spec/unit/ssl/inventory_spec.rb +++ b/spec/unit/ssl/inventory_spec.rb @@ -1,180 +1,179 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/inventory' describe Puppet::SSL::Inventory do before do @class = Puppet::SSL::Inventory end it "should use the :certinventory setting for the path to the inventory file" do Puppet.settings.expects(:value).with(:cert_inventory).returns "/inven/tory" @class.any_instance.stubs(:rebuild) @class.new.path.should == "/inven/tory" end describe "when initializing" do it "should set its path to the inventory file" do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" @class.new.path.should == "/inven/tory" end end describe "when managing an inventory" do before do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" FileTest.stubs(:exist?).with("/inven/tory").returns true @inventory = @class.new @cert = mock 'cert' end describe "and creating the inventory file" do before do Puppet.settings.stubs(:write) FileTest.stubs(:exist?).with("/inven/tory").returns false Puppet::SSL::Certificate.indirection.stubs(:search).returns [] end it "should log that it is building a new inventory file" do Puppet.expects(:notice) @inventory.rebuild end it "should use the Settings to write to the file" do Puppet.settings.expects(:write).with(:cert_inventory) @inventory.rebuild end it "should add a header to the file" do fh = mock 'filehandle' Puppet.settings.stubs(:write).yields fh fh.expects(:print).with { |str| str =~ /^#/ } @inventory.rebuild end it "should add formatted information on all existing certificates" do cert1 = mock 'cert1' cert2 = mock 'cert2' Puppet::SSL::Certificate.indirection.expects(:search).with("*").returns [cert1, cert2] @class.any_instance.expects(:add).with(cert1) @class.any_instance.expects(:add).with(cert2) @inventory.rebuild end end describe "and adding a certificate" do it "should build the inventory file if one does not exist" do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" Puppet.settings.stubs(:write) FileTest.expects(:exist?).with("/inven/tory").returns false @inventory.expects(:rebuild) @inventory.add(@cert) end it "should use the Settings to write to the file" do Puppet.settings.expects(:write).with(:cert_inventory, "a") @inventory.add(@cert) end it "should use the actual certificate if it was passed a Puppet certificate" do cert = Puppet::SSL::Certificate.new("mycert") cert.content = @cert fh = stub 'filehandle', :print => nil Puppet.settings.stubs(:write).yields fh @inventory.expects(:format).with(@cert) @inventory.add(@cert) end it "should add formatted certificate information to the end of the file" do fh = mock 'filehandle' Puppet.settings.stubs(:write).yields fh @inventory.expects(:format).with(@cert).returns "myformat" fh.expects(:print).with("myformat") @inventory.add(@cert) end end describe "and formatting a certificate" do before do @cert = stub 'cert', :not_before => Time.now, :not_after => Time.now, :subject => "mycert", :serial => 15 end it "should print the serial number as a 4 digit hex number in the first field" do @inventory.format(@cert).split[0].should == "0x000f" # 15 in hex end it "should print the not_before date in '%Y-%m-%dT%H:%M:%S%Z' format in the second field" do @cert.not_before.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "before_time" @inventory.format(@cert).split[1].should == "before_time" end it "should print the not_after date in '%Y-%m-%dT%H:%M:%S%Z' format in the third field" do @cert.not_after.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "after_time" @inventory.format(@cert).split[2].should == "after_time" end it "should print the subject in the fourth field" do @inventory.format(@cert).split[3].should == "mycert" end it "should add a carriage return" do @inventory.format(@cert).should =~ /\n$/ end it "should produce a line consisting of the serial number, start date, expiration date, and subject" do # Just make sure our serial and subject bracket the lines. @inventory.format(@cert).should =~ /^0x.+mycert$/ end end it "should be able to find a given host's serial number" do @inventory.should respond_to(:serial) end describe "and finding a serial number" do it "should return nil if the inventory file is missing" do FileTest.expects(:exist?).with("/inven/tory").returns false @inventory.serial(:whatever).should be_nil end it "should return the serial number from the line matching the provided name" do File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] @inventory.serial("me").should == 15 end it "should return the number as an integer" do File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] @inventory.serial("me").should == 15 end end end end diff --git a/spec/unit/ssl/key_spec.rb b/spec/unit/ssl/key_spec.rb index e0667a5cc..cddf9f8ea 100755 --- a/spec/unit/ssl/key_spec.rb +++ b/spec/unit/ssl/key_spec.rb @@ -1,198 +1,197 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/ssl/key' describe Puppet::SSL::Key do before do @class = Puppet::SSL::Key end it "should be extended with the Indirector module" do @class.singleton_class.should be_include(Puppet::Indirector) end it "should indirect key" do @class.indirection.name.should == :key end it "should default to the :file terminus" do @class.indirection.terminus_class.should == :file end it "should only support the text format" do @class.supported_formats.should == [:s] end it "should have a method for determining whether it's a CA key" do @class.new("test").should respond_to(:ca?) end it "should consider itself a ca key if its name matches the CA_NAME" do @class.new(Puppet::SSL::Host.ca_name).should be_ca end describe "when initializing" do it "should set its password file to the :capass if it's a CA key" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:capass).returns "/ca/pass" key = Puppet::SSL::Key.new(Puppet::SSL::Host.ca_name) key.password_file.should == "/ca/pass" end it "should downcase its name" do @class.new("MyName").name.should == "myname" end it "should set its password file to the default password file if it is not the CA key" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:passfile).returns "/normal/pass" key = Puppet::SSL::Key.new("notca") key.password_file.should == "/normal/pass" end end describe "when managing instances" do before do @key = @class.new("myname") end it "should have a name attribute" do @key.name.should == "myname" end it "should have a content attribute" do @key.should respond_to(:content) end it "should be able to read keys from disk" do path = "/my/path" File.expects(:read).with(path).returns("my key") key = mock 'key' OpenSSL::PKey::RSA.expects(:new).returns(key) @key.read(path).should equal(key) @key.content.should equal(key) end it "should not try to use the provided password file if the file does not exist" do FileTest.stubs(:exist?).returns false @key.password_file = "/path/to/password" path = "/my/path" File.stubs(:read).with(path).returns("my key") OpenSSL::PKey::RSA.expects(:new).with("my key", nil).returns(mock('key')) File.expects(:read).with("/path/to/password").never @key.read(path) end it "should read the key with the password retrieved from the password file if one is provided" do FileTest.stubs(:exist?).returns true @key.password_file = "/path/to/password" path = "/my/path" File.expects(:read).with(path).returns("my key") File.expects(:read).with("/path/to/password").returns("my password") key = mock 'key' OpenSSL::PKey::RSA.expects(:new).with("my key", "my password").returns(key) @key.read(path).should equal(key) @key.content.should equal(key) end it "should return an empty string when converted to a string with no key" do @key.to_s.should == "" end it "should convert the key to pem format when converted to a string" do key = mock 'key', :to_pem => "pem" @key.content = key @key.to_s.should == "pem" end it "should have a :to_text method that it delegates to the actual key" do real_key = mock 'key' real_key.expects(:to_text).returns "keytext" @key.content = real_key @key.to_text.should == "keytext" end end describe "when generating the private key" do before do @instance = @class.new("test") @key = mock 'key' end it "should create an instance of OpenSSL::PKey::RSA" do OpenSSL::PKey::RSA.expects(:new).returns(@key) @instance.generate end it "should create the private key with the keylength specified in the settings" do Puppet.settings.expects(:value).with(:keylength).returns("50") OpenSSL::PKey::RSA.expects(:new).with(50).returns(@key) @instance.generate end it "should set the content to the generated key" do OpenSSL::PKey::RSA.stubs(:new).returns(@key) @instance.generate @instance.content.should equal(@key) end it "should return the generated key" do OpenSSL::PKey::RSA.stubs(:new).returns(@key) @instance.generate.should equal(@key) end it "should return the key in pem format" do @instance.generate @instance.content.expects(:to_pem).returns "my normal key" @instance.to_s.should == "my normal key" end describe "with a password file set" do it "should return a nil password if the password file does not exist" do FileTest.expects(:exist?).with("/path/to/pass").returns false File.expects(:read).with("/path/to/pass").never @instance.password_file = "/path/to/pass" @instance.password.should be_nil end it "should return the contents of the password file as its password" do FileTest.expects(:exist?).with("/path/to/pass").returns true File.expects(:read).with("/path/to/pass").returns "my password" @instance.password_file = "/path/to/pass" @instance.password.should == "my password" end it "should export the private key to text using the password" do Puppet.settings.stubs(:value).with(:keylength).returns("50") @instance.password_file = "/path/to/pass" @instance.stubs(:password).returns "my password" OpenSSL::PKey::RSA.expects(:new).returns(@key) @instance.generate cipher = mock 'cipher' OpenSSL::Cipher::DES.expects(:new).with(:EDE3, :CBC).returns cipher @key.expects(:export).with(cipher, "my password").returns "my encrypted key" @instance.to_s.should == "my encrypted key" end end end end diff --git a/spec/unit/sslcertificates/ca_spec.rb b/spec/unit/sslcertificates/ca_spec.rb old mode 100644 new mode 100755 index 4e44f2d90..eea246ba1 --- a/spec/unit/sslcertificates/ca_spec.rb +++ b/spec/unit/sslcertificates/ca_spec.rb @@ -1,110 +1,110 @@ -#!/usr/bin/env ruby -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet' require 'puppet/sslcertificates' require 'puppet/sslcertificates/ca' describe Puppet::SSLCertificates::CA do before :all do @hosts = %w{host.domain.com Other.Testing.Com} end before :each do Puppet::Util::SUIDManager.stubs(:asuser).yields file = Tempfile.new("ca_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir @ca = Puppet::SSLCertificates::CA.new end after :each do system("rm -rf #{@dir}") end describe 'when cleaning' do it 'should remove associated files' do dirs = [:csrdir, :signeddir, :publickeydir, :privatekeydir, :certdir] @hosts.each do |host| files = [] dirs.each do |dir| dir = Puppet[dir] # Case insensitivity is handled through downcasing file = File.join(dir, host.downcase + '.pem') File.open(file, "w") do |f| f.puts "testing" end files << file end lambda { @ca.clean(host) }.should_not raise_error files.reject {|f| ! File.exists?(f)}.should be_empty end end end describe 'when mapping hosts to files' do it 'should correctly return the certfile' do @hosts.each do |host| value = nil lambda { value = @ca.host2certfile host }.should_not raise_error File.join(Puppet[:signeddir], host.downcase + '.pem').should == value end end it 'should correctly return the csrfile' do @hosts.each do |host| value = nil lambda { value = @ca.host2csrfile host }.should_not raise_error File.join(Puppet[:csrdir], host.downcase + '.pem').should == value end end end describe 'when listing' do it 'should find all csr' do list = [] # Make some fake CSRs @hosts.each do |host| file = File.join(Puppet[:csrdir], host.downcase + '.pem') File.open(file, 'w') { |f| f.puts "yay" } list << host.downcase end @ca.list.sort.should == list.sort end end describe 'when creating a root certificate' do before :each do lambda { @ca.mkrootcert }.should_not raise_exception end it 'should store the public key' do File.exists?(Puppet[:capub]).should be_true end it 'should prepend "Puppet CA: " to the fqdn as the ca_name by default' do host_mock_fact = mock() host_mock_fact.expects(:value).returns('myhost') domain_mock_fact = mock() domain_mock_fact.expects(:value).returns('puppetlabs.lan') Facter.stubs(:[]).with('hostname').returns(host_mock_fact) Facter.stubs(:[]).with('domain').returns(domain_mock_fact) @ca.mkrootcert.name.should == 'Puppet CA: myhost.puppetlabs.lan' end end end diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb old mode 100644 new mode 100755 index 3a9c805b3..820807638 --- a/spec/unit/status_spec.rb +++ b/spec/unit/status_spec.rb @@ -1,31 +1,30 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Status do it "should implement find" do Puppet::Status.indirection.find( :default ).should be_is_a(Puppet::Status) Puppet::Status.indirection.find( :default ).status["is_alive"].should == true end it "should default to is_alive is true" do Puppet::Status.new.status["is_alive"].should == true end it "should return a pson hash" do Puppet::Status.new.status.to_pson.should == '{"is_alive":true}' end it "should accept a hash from pson" do status = Puppet::Status.new( { "is_alive" => false } ) status.status.should == { "is_alive" => false } end it "should have a name" do Puppet::Status.new.name end it "should allow a name to be set" do Puppet::Status.new.name = "status" end end diff --git a/spec/unit/transaction/event_manager_spec.rb b/spec/unit/transaction/event_manager_spec.rb index e45fbdf78..d127d0391 100755 --- a/spec/unit/transaction/event_manager_spec.rb +++ b/spec/unit/transaction/event_manager_spec.rb @@ -1,260 +1,259 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transaction/event_manager' describe Puppet::Transaction::EventManager do describe "at initialization" do it "should require a transaction" do Puppet::Transaction::EventManager.new("trans").transaction.should == "trans" end end it "should delegate its relationship graph to the transaction" do transaction = stub 'transaction' manager = Puppet::Transaction::EventManager.new(transaction) transaction.expects(:relationship_graph).returns "mygraph" manager.relationship_graph.should == "mygraph" end describe "when queueing events" do before do @manager = Puppet::Transaction::EventManager.new(@transaction) @resource = Puppet::Type.type(:file).new :path => "/my/file" @graph = stub 'graph', :matching_edges => [], :resource => @resource @manager.stubs(:relationship_graph).returns @graph @event = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource) end it "should store all of the events in its event list" do @event2 = Puppet::Transaction::Event.new(:name => :bar, :resource => @resource) @manager.queue_events(@resource, [@event, @event2]) @manager.events.should include(@event) @manager.events.should include(@event2) end it "should queue events for the target and callback of any matching edges" do edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1", :c1 => nil)) edge2 = stub("edge2", :callback => :c2, :source => stub("s2"), :target => stub("t2", :c2 => nil)) @graph.expects(:matching_edges).with { |event, resource| event == @event }.returns [edge1, edge2] @manager.expects(:queue_events_for_resource).with(@resource, edge1.target, edge1.callback, [@event]) @manager.expects(:queue_events_for_resource).with(@resource, edge2.target, edge2.callback, [@event]) @manager.queue_events(@resource, [@event]) end it "should queue events for the changed resource if the resource is self-refreshing and not being deleted" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns true @resource.expects(:deleting?).returns false @manager.expects(:queue_events_for_resource).with(@resource, @resource, :refresh, [@event]) @manager.queue_events(@resource, [@event]) end it "should not queue events for the changed resource if the resource is not self-refreshing" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns false @resource.stubs(:deleting?).returns false @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should not queue events for the changed resource if the resource is being deleted" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns true @resource.expects(:deleting?).returns true @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should ignore edges that don't have a callback" do edge1 = stub("edge1", :callback => :nil, :source => stub("s1"), :target => stub("t1", :c1 => nil)) @graph.expects(:matching_edges).returns [edge1] @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should ignore targets that don't respond to the callback" do edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1")) @graph.expects(:matching_edges).returns [edge1] @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end end describe "when queueing events for a resource" do before do @transaction = stub 'transaction' @manager = Puppet::Transaction::EventManager.new(@transaction) end it "should do nothing if no events are queued" do @manager.queued_events(stub("target")) { |callback, events| raise "should never reach this" } end it "should yield the callback and events for each callback" do target = stub("target") 2.times do |i| @manager.queue_events_for_resource(stub("source", :info => nil), target, "callback#{i}", ["event#{i}"]) end @manager.queued_events(target) { |callback, events| } end it "should use the source to log that it's scheduling a refresh of the target" do target = stub("target") source = stub 'source' source.expects(:info) @manager.queue_events_for_resource(source, target, "callback", ["event"]) @manager.queued_events(target) { |callback, events| } end end describe "when processing events for a given resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @manager = Puppet::Transaction::EventManager.new(@transaction) @manager.stubs(:queue_events) @resource = Puppet::Type.type(:file).new :path => "/my/file" @event = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) end it "should call the required callback once for each set of associated events" do @manager.expects(:queued_events).with(@resource).multiple_yields([:callback1, [@event]], [:callback2, [@event]]) @resource.expects(:callback1) @resource.expects(:callback2) @manager.process_events(@resource) end it "should set the 'restarted' state on the resource status" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @manager.process_events(@resource) @transaction.resource_status(@resource).should be_restarted end it "should queue a 'restarted' event generated by the resource" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @resource.expects(:event).with(:name => :restarted, :status => "success").returns "myevent" @manager.expects(:queue_events).with(@resource, ["myevent"]) @manager.process_events(@resource) end it "should log that it restarted" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @resource.expects(:notice).with { |msg| msg.include?("Triggered 'callback1'") } @manager.process_events(@resource) end describe "and the events include a noop event and at least one non-noop event" do before do @event.stubs(:status).returns "noop" @event2 = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) @event2.status = "success" @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event, @event2]) end it "should call the callback" do @resource.expects(:callback1) @manager.process_events(@resource) end end describe "and the events are all noop events" do before do @event.stubs(:status).returns "noop" @resource.stubs(:event).returns(Puppet::Transaction::Event.new) @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) end it "should log" do @resource.expects(:notice).with { |msg| msg.include?("Would have triggered 'callback1'") } @manager.process_events(@resource) end it "should not call the callback" do @resource.expects(:callback1).never @manager.process_events(@resource) end it "should queue a new noop event generated from the resource" do event = Puppet::Transaction::Event.new @resource.expects(:event).with(:status => "noop", :name => :noop_restart).returns event @manager.expects(:queue_events).with(@resource, [event]) @manager.process_events(@resource) end end describe "and the callback fails" do before do @resource.expects(:callback1).raises "a failure" @resource.stubs(:err) @manager.expects(:queued_events).yields(:callback1, [@event]) end it "should log but not fail" do @resource.expects(:err) lambda { @manager.process_events(@resource) }.should_not raise_error end it "should set the 'failed_restarts' state on the resource status" do @manager.process_events(@resource) @transaction.resource_status(@resource).should be_failed_to_restart end it "should not queue a 'restarted' event" do @manager.expects(:queue_events).never @manager.process_events(@resource) end it "should set the 'restarted' state on the resource status" do @manager.process_events(@resource) @transaction.resource_status(@resource).should_not be_restarted end end end end diff --git a/spec/unit/transaction/event_spec.rb b/spec/unit/transaction/event_spec.rb index c77716cd6..1227802a7 100755 --- a/spec/unit/transaction/event_spec.rb +++ b/spec/unit/transaction/event_spec.rb @@ -1,127 +1,126 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transaction/event' describe Puppet::Transaction::Event do [:previous_value, :desired_value, :property, :resource, :name, :message, :file, :line, :tags, :audited].each do |attr| it "should support #{attr}" do event = Puppet::Transaction::Event.new event.send(attr.to_s + "=", "foo") event.send(attr).should == "foo" end end it "should always convert the property to a string" do Puppet::Transaction::Event.new(:property => :foo).property.should == "foo" end it "should always convert the resource to a string" do Puppet::Transaction::Event.new(:resource => :foo).resource.should == "foo" end it "should produce the message when converted to a string" do event = Puppet::Transaction::Event.new event.expects(:message).returns "my message" event.to_s.should == "my message" end it "should support 'status'" do event = Puppet::Transaction::Event.new event.status = "success" event.status.should == "success" end it "should fail if the status is not to 'audit', 'noop', 'success', or 'failure" do event = Puppet::Transaction::Event.new lambda { event.status = "foo" }.should raise_error(ArgumentError) end it "should support tags" do Puppet::Transaction::Event.ancestors.should include(Puppet::Util::Tagging) end it "should create a timestamp at its creation time" do Puppet::Transaction::Event.new.time.should be_instance_of(Time) end describe "audit property" do it "should default to false" do Puppet::Transaction::Event.new.audited.should == false end end describe "when sending logs" do before do Puppet::Util::Log.stubs(:new) end it "should set the level to the resources's log level if the event status is 'success' and a resource is available" do resource = stub 'resource' resource.expects(:[]).with(:loglevel).returns :myloglevel Puppet::Util::Log.expects(:create).with { |args| args[:level] == :myloglevel } Puppet::Transaction::Event.new(:status => "success", :resource => resource).send_log end it "should set the level to 'notice' if the event status is 'success' and no resource is available" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } Puppet::Transaction::Event.new(:status => "success").send_log end it "should set the level to 'notice' if the event status is 'noop'" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } Puppet::Transaction::Event.new(:status => "noop").send_log end it "should set the level to 'err' if the event status is 'failure'" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :err } Puppet::Transaction::Event.new(:status => "failure").send_log end it "should set the 'message' to the event log" do Puppet::Util::Log.expects(:new).with { |args| args[:message] == "my message" } Puppet::Transaction::Event.new(:message => "my message").send_log end it "should set the tags to the event tags" do Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } Puppet::Transaction::Event.new(:tags => %w{one two}).send_log end [:file, :line].each do |attr| it "should pass the #{attr}" do Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } Puppet::Transaction::Event.new(attr => "my val").send_log end end it "should use the source description as the source if one is set" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "/my/param" } Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => "Foo[bar]", :property => "foo").send_log end it "should use the property as the source if one is available and no source description is set" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "foo" } Puppet::Transaction::Event.new(:resource => "Foo[bar]", :property => "foo").send_log end it "should use the property as the source if one is available and no property or source description is set" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "Foo[bar]" } Puppet::Transaction::Event.new(:resource => "Foo[bar]").send_log end end describe "When converting to YAML" do it "should include only documented attributes" do resource = Puppet::Type.type(:file).new(:title => "/tmp/foo") event = Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => resource, :file => "/foo.rb", :line => 27, :tags => %w{one two}, :desired_value => 7, :historical_value => 'Brazil', :message => "Help I'm trapped in a spec test", :name => :mode_changed, :previous_value => 6, :property => :mode, :status => 'success') event.to_yaml_properties.should == Puppet::Transaction::Event::YAML_ATTRIBUTES.sort end end end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index a3bfe1e82..191a30eb7 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -1,294 +1,312 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transaction/report' describe Puppet::Transaction::Report do + include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end it "should set its host name to the certname" do Puppet.settings.expects(:value).with(:certname).returns "myhost" Puppet::Transaction::Report.new("apply").host.should == "myhost" end it "should return its host name as its name" do r = Puppet::Transaction::Report.new("apply") r.name.should == r.host end it "should create an initialization timestamp" do Time.expects(:now).returns "mytime" Puppet::Transaction::Report.new("apply").time.should == "mytime" end it "should take a 'kind' as an argument" do Puppet::Transaction::Report.new("inspect").kind.should == "inspect" end it "should take a 'configuration_version' as an argument" do Puppet::Transaction::Report.new("inspect", "some configuration version").configuration_version.should == "some configuration version" end it "should be able to set configuration_version" do report = Puppet::Transaction::Report.new("inspect") report.configuration_version = "some version" report.configuration_version.should == "some version" end + it "should not include whits" do + Puppet::FileBucket::File.indirection.stubs(:save) + + filename = tmpfile('whit_test') + file = Puppet::Type.type(:file).new(:path => filename) + + catalog = Puppet::Resource::Catalog.new + catalog.add_resource(file) + + report = Puppet::Transaction::Report.new("apply") + + catalog.apply(:report => report) + report.finalize_report + + report.resource_statuses.values.any? {|res| res.resource_type =~ /whit/i}.should be_false + report.metrics['time'].values.any? {|metric| metric.first =~ /whit/i}.should be_false + end + describe "when accepting logs" do before do @report = Puppet::Transaction::Report.new("apply") end it "should add new logs to the log list" do @report << "log" @report.logs[-1].should == "log" end it "should return self" do r = @report << "log" r.should equal(@report) end end describe "when accepting resource statuses" do before do @report = Puppet::Transaction::Report.new("apply") end it "should add each status to its status list" do status = stub 'status', :resource => "foo" @report.add_resource_status status @report.resource_statuses["foo"].should equal(status) end end describe "when using the indirector" do it "should redirect :save to the indirection" do Facter.stubs(:value).returns("eh") @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) report = Puppet::Transaction::Report.new("apply") @indirection.expects(:save) Puppet::Transaction::Report.indirection.save(report) end it "should default to the 'processor' terminus" do Puppet::Transaction::Report.indirection.terminus_class.should == :processor end it "should delegate its name attribute to its host method" do report = Puppet::Transaction::Report.new("apply") report.expects(:host).returns "me" report.name.should == "me" end after do Puppet::Util::Cacher.expire end end describe "when computing exit status" do it "should produce 2 if changes are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 1}) report.add_metric("resources", {"failed" => 0}) report.exit_status.should == 2 end it "should produce 4 if failures are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 0}) report.add_metric("resources", {"failed" => 1}) report.exit_status.should == 4 end it "should produce 6 if both changes and failures are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 1}) report.add_metric("resources", {"failed" => 1}) report.exit_status.should == 6 end end describe "before finalizing the report" do it "should have a status of 'failed'" do report = Puppet::Transaction::Report.new("apply") report.status.should == 'failed' end end describe "when finalizing the report" do before do @report = Puppet::Transaction::Report.new("apply") end def metric(name, value) if metric = @report.metrics[name.to_s] metric[value] else nil end end def add_statuses(count, type = :file) count.times do |i| status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => "/my/path#{i}")) yield status if block_given? @report.add_resource_status status end end [:time, :resources, :changes, :events].each do |type| it "should add #{type} metrics" do @report.finalize_report @report.metrics[type.to_s].should be_instance_of(Puppet::Transaction::Metric) end end describe "for resources" do it "should provide the total number of resources" do add_statuses(3) @report.finalize_report metric(:resources, "total").should == 3 end Puppet::Resource::Status::STATES.each do |state| it "should provide the number of #{state} resources as determined by the status objects" do add_statuses(3) { |status| status.send(state.to_s + "=", true) } @report.finalize_report metric(:resources, state.to_s).should == 3 end end it "should mark the report as 'failed' if there are failing resources" do add_statuses(1) { |status| status.failed = true } @report.finalize_report @report.status.should == 'failed' end end describe "for changes" do it "should provide the number of changes from the resource statuses and mark the report as 'changed'" do add_statuses(3) { |status| 3.times { status << Puppet::Transaction::Event.new(:status => 'success') } } @report.finalize_report metric(:changes, "total").should == 9 @report.status.should == 'changed' end it "should provide a total even if there are no changes, and mark the report as 'unchanged'" do @report.finalize_report metric(:changes, "total").should == 0 @report.status.should == 'unchanged' end end describe "for times" do it "should provide the total amount of time for each resource type" do add_statuses(3, :file) do |status| status.evaluation_time = 1 end add_statuses(3, :exec) do |status| status.evaluation_time = 2 end add_statuses(3, :mount) do |status| status.evaluation_time = 3 end @report.finalize_report metric(:time, "file").should == 3 metric(:time, "exec").should == 6 metric(:time, "mount").should == 9 end it "should add any provided times from external sources" do @report.add_times :foobar, 50 @report.finalize_report metric(:time, "foobar").should == 50 end it "should have a total time" do add_statuses(3, :file) do |status| status.evaluation_time = 1.25 end @report.add_times :config_retrieval, 0.5 @report.finalize_report metric(:time, "total").should == 4.25 end end describe "for events" do it "should provide the total number of events" do add_statuses(3) do |status| 3.times { |i| status.add_event(Puppet::Transaction::Event.new :status => 'success') } end @report.finalize_report metric(:events, "total").should == 9 end it "should provide the total even if there are no events" do @report.finalize_report metric(:events, "total").should == 0 end Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name| it "should provide the number of #{status_name} events" do add_statuses(3) do |status| 3.times do |i| event = Puppet::Transaction::Event.new event.status = status_name status.add_event(event) end end @report.finalize_report metric(:events, status_name).should == 9 end end end end describe "when producing a summary" do before do resource = Puppet::Type.type(:notify).new(:name => "testing") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource trans = catalog.apply @report = trans.report @report.finalize_report end %w{changes time resources events}.each do |main| it "should include the key #{main} in the raw summary hash" do @report.raw_summary.should be_key main end end it "should include the last run time in the raw summary hash" do Time.stubs(:now).returns(Time.utc(2010,11,10,12,0,24)) @report.raw_summary["time"]["last_run"].should == 1289390424 end %w{Changes Total Resources Time Events}.each do |main| it "should include information on #{main} in the textual summary" do @report.summary.should be_include(main) end end end describe "when outputting yaml" do it "should not include @external_times" do report = Puppet::Transaction::Report.new('apply') report.add_times('config_retrieval', 1.0) report.to_yaml_properties.should_not include('@external_times') end end end diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb index 168b50287..20a42d27b 100755 --- a/spec/unit/transaction/resource_harness_spec.rb +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -1,481 +1,480 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transaction/resource_harness' describe Puppet::Transaction::ResourceHarness do include PuppetSpec::Files before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @resource = Puppet::Type.type(:file).new :path => "/my/file" @harness = Puppet::Transaction::ResourceHarness.new(@transaction) @current_state = Puppet::Resource.new(:file, "/my/file") @resource.stubs(:retrieve).returns @current_state @status = Puppet::Resource::Status.new(@resource) Puppet::Resource::Status.stubs(:new).returns @status end it "should accept a transaction at initialization" do harness = Puppet::Transaction::ResourceHarness.new(@transaction) harness.transaction.should equal(@transaction) end it "should delegate to the transaction for its relationship graph" do @transaction.expects(:relationship_graph).returns "relgraph" Puppet::Transaction::ResourceHarness.new(@transaction).relationship_graph.should == "relgraph" end describe "when evaluating a resource" do it "should create and return a resource status instance for the resource" do @harness.evaluate(@resource).should be_instance_of(Puppet::Resource::Status) end it "should fail if no status can be created" do Puppet::Resource::Status.expects(:new).raises ArgumentError lambda { @harness.evaluate(@resource) }.should raise_error end it "should retrieve the current state of the resource" do @resource.expects(:retrieve).returns @current_state @harness.evaluate(@resource) end it "should mark the resource as failed and return if the current state cannot be retrieved" do @resource.expects(:retrieve).raises ArgumentError @harness.evaluate(@resource).should be_failed end it "should store the resource's evaluation time in the resource status" do @harness.evaluate(@resource).evaluation_time.should be_instance_of(Float) end end def events_to_hash(events) events.map do |event| hash = {} event.instance_variables.each do |varname| hash[varname] = event.instance_variable_get(varname.to_sym) end hash end end def make_stub_provider stubProvider = Class.new(Puppet::Type) stubProvider.instance_eval do initvars newparam(:name) do desc "The name var" isnamevar end newproperty(:foo) do desc "A property that can be changed successfully" def sync end def retrieve :absent end def insync?(reference_value) false end end newproperty(:bar) do desc "A property that raises an exception when you try to change it" def sync raise ZeroDivisionError.new('bar') end def retrieve :absent end def insync?(reference_value) false end end end stubProvider end describe "when an error occurs" do before :each do stub_provider = make_stub_provider resource = stub_provider.new :name => 'name', :foo => 1, :bar => 2 resource.expects(:err).never @status = @harness.evaluate(resource) end it "should record previous successful events" do @status.events[0].property.should == 'foo' @status.events[0].status.should == 'success' end it "should record a failure event" do @status.events[1].property.should == 'bar' @status.events[1].status.should == 'failure' end end describe "when auditing" do it "should not call insync? on parameters that are merely audited" do stub_provider = make_stub_provider resource = stub_provider.new :name => 'name', :audit => ['foo'] resource.property(:foo).expects(:insync?).never status = @harness.evaluate(resource) status.events.each do |event| event.status.should != 'failure' end end it "should be able to audit a file's group" do # see bug #5710 test_file = tmpfile('foo') File.open(test_file, 'w').close resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['group'], :backup => false resource.expects(:err).never # make sure no exceptions get swallowed status = @harness.evaluate(resource) status.events.each do |event| event.status.should != 'failure' end end end describe "when applying changes" do [false, true].each do |noop_mode|; describe (noop_mode ? "in noop mode" : "in normal mode") do [nil, '750'].each do |machine_state|; describe (machine_state ? "with a file initially present" : "with no file initially present") do [nil, '750', '755'].each do |yaml_mode| [nil, :file, :absent].each do |yaml_ensure|; describe "with mode=#{yaml_mode.inspect} and ensure=#{yaml_ensure.inspect} stored in state.yml" do [false, true].each do |auditing_ensure| [false, true].each do |auditing_mode| auditing = [] auditing.push(:mode) if auditing_mode auditing.push(:ensure) if auditing_ensure [nil, :file, :absent].each do |ensure_property| # what we set "ensure" to in the manifest [nil, '750', '755'].each do |mode_property| # what we set "mode" to in the manifest manifest_settings = {} manifest_settings[:audit] = auditing if !auditing.empty? manifest_settings[:ensure] = ensure_property if ensure_property manifest_settings[:mode] = mode_property if mode_property describe "with manifest settings #{manifest_settings.inspect}" do; it "should behave properly" do # Set up preconditions test_file = tmpfile('foo') if machine_state File.open(test_file, 'w', machine_state.to_i(8)).close end Puppet[:noop] = noop_mode params = { :path => test_file, :backup => false } params.merge!(manifest_settings) resource = Puppet::Type.type(:file).new params @harness.cache(resource, :mode, yaml_mode) if yaml_mode @harness.cache(resource, :ensure, yaml_ensure) if yaml_ensure fake_time = Time.utc(2011, 'jan', 3, 12, 24, 0) Time.stubs(:now).returns(fake_time) # So that Puppet::Resource::Status objects will compare properly resource.expects(:err).never # make sure no exceptions get swallowed status = @harness.evaluate(resource) # do the thing # check that the state of the machine has been properly updated expected_logs = [] expected_status_events = [] if auditing_mode @harness.cached(resource, :mode).should == (machine_state || :absent) else @harness.cached(resource, :mode).should == yaml_mode end if auditing_ensure @harness.cached(resource, :ensure).should == (machine_state ? :file : :absent) else @harness.cached(resource, :ensure).should == yaml_ensure end if ensure_property == :file file_would_be_there_if_not_noop = true elsif ensure_property == nil file_would_be_there_if_not_noop = machine_state != nil else # ensure_property == :absent file_would_be_there_if_not_noop = false end file_should_be_there = noop_mode ? machine_state != nil : file_would_be_there_if_not_noop File.exists?(test_file).should == file_should_be_there if file_should_be_there if noop_mode expected_file_mode = machine_state else expected_file_mode = mode_property || machine_state end if !expected_file_mode # we didn't specify a mode and the file was created, so mode comes from umode else file_mode = File.stat(test_file).mode & 0777 file_mode.should == expected_file_mode.to_i(8) end end # Test log output for the "mode" parameter previously_recorded_mode_already_logged = false mode_status_msg = nil if machine_state && file_would_be_there_if_not_noop && mode_property && machine_state != mode_property if noop_mode what_happened = "current_value #{machine_state}, should be #{mode_property} (noop)" expected_status = 'noop' else what_happened = "mode changed '#{machine_state}' to '#{mode_property}'" expected_status = 'success' end if auditing_mode && yaml_mode && yaml_mode != machine_state previously_recorded_mode_already_logged = true mode_status_msg = "#{what_happened} (previously recorded value was #{yaml_mode})" else mode_status_msg = what_happened end expected_logs << "notice: /#{resource}/mode: #{mode_status_msg}" end if @harness.cached(resource, :mode) && @harness.cached(resource, :mode) != yaml_mode if yaml_mode unless previously_recorded_mode_already_logged mode_status_msg = "audit change: previously recorded value #{yaml_mode} has been changed to #{@harness.cached(resource, :mode)}" expected_logs << "notice: /#{resource}/mode: #{mode_status_msg}" expected_status = 'audit' end else expected_logs << "notice: /#{resource}/mode: audit change: newly-recorded value #{@harness.cached(resource, :mode)}" end end if mode_status_msg expected_status_events << Puppet::Transaction::Event.new( :source_description => "/#{resource}/mode", :resource => resource, :file => nil, :line => nil, :tags => %w{file}, :desired_value => mode_property, :historical_value => yaml_mode, :message => mode_status_msg, :name => :mode_changed, :previous_value => machine_state || :absent, :property => :mode, :status => expected_status, :audited => auditing_mode) end # Test log output for the "ensure" parameter previously_recorded_ensure_already_logged = false ensure_status_msg = nil if file_would_be_there_if_not_noop != (machine_state != nil) if noop_mode what_happened = "current_value #{machine_state ? 'file' : 'absent'}, should be #{file_would_be_there_if_not_noop ? 'file' : 'absent'} (noop)" expected_status = 'noop' else what_happened = file_would_be_there_if_not_noop ? 'created' : 'removed' expected_status = 'success' end if auditing_ensure && yaml_ensure && yaml_ensure != (machine_state ? :file : :absent) previously_recorded_ensure_already_logged = true ensure_status_msg = "#{what_happened} (previously recorded value was #{yaml_ensure})" else ensure_status_msg = "#{what_happened}" end expected_logs << "notice: /#{resource}/ensure: #{ensure_status_msg}" end if @harness.cached(resource, :ensure) && @harness.cached(resource, :ensure) != yaml_ensure if yaml_ensure unless previously_recorded_ensure_already_logged ensure_status_msg = "audit change: previously recorded value #{yaml_ensure} has been changed to #{@harness.cached(resource, :ensure)}" expected_logs << "notice: /#{resource}/ensure: #{ensure_status_msg}" expected_status = 'audit' end else expected_logs << "notice: /#{resource}/ensure: audit change: newly-recorded value #{@harness.cached(resource, :ensure)}" end end if ensure_status_msg if ensure_property == :file ensure_event_name = :file_created elsif ensure_property == nil ensure_event_name = :file_changed else # ensure_property == :absent ensure_event_name = :file_removed end expected_status_events << Puppet::Transaction::Event.new( :source_description => "/#{resource}/ensure", :resource => resource, :file => nil, :line => nil, :tags => %w{file}, :desired_value => ensure_property, :historical_value => yaml_ensure, :message => ensure_status_msg, :name => ensure_event_name, :previous_value => machine_state ? :file : :absent, :property => :ensure, :status => expected_status, :audited => auditing_ensure) end # Actually check the logs. @logs.map {|l| "#{l.level}: #{l.source}: #{l.message}"}.should =~ expected_logs # All the log messages should show up as events except the "newly-recorded" ones. expected_event_logs = @logs.reject {|l| l.message =~ /newly-recorded/ } status.events.map {|e| e.message}.should =~ expected_event_logs.map {|l| l.message } events_to_hash(status.events).should =~ events_to_hash(expected_status_events) # Check change count - this is the number of changes that actually occurred. expected_change_count = 0 if (machine_state != nil) != file_should_be_there expected_change_count = 1 elsif machine_state != nil if expected_file_mode != machine_state expected_change_count = 1 end end status.change_count.should == expected_change_count # Check out of sync count - this is the number # of changes that would have occurred in # non-noop mode. expected_out_of_sync_count = 0 if (machine_state != nil) != file_would_be_there_if_not_noop expected_out_of_sync_count = 1 elsif machine_state != nil if mode_property != nil && mode_property != machine_state expected_out_of_sync_count = 1 end end if !noop_mode expected_out_of_sync_count.should == expected_change_count end status.out_of_sync_count.should == expected_out_of_sync_count # Check legacy summary fields status.changed.should == (expected_change_count != 0) status.out_of_sync.should == (expected_out_of_sync_count != 0) # Check the :synced field on state.yml synced_should_be_set = !noop_mode && status.changed (@harness.cached(resource, :synced) != nil).should == synced_should_be_set end; end end end end end end; end end end; end end; end it "should not apply changes if allow_changes?() returns false" do test_file = tmpfile('foo') resource = Puppet::Type.type(:file).new :path => test_file, :backup => false, :ensure => :file resource.expects(:err).never # make sure no exceptions get swallowed @harness.expects(:allow_changes?).with(resource).returns false status = @harness.evaluate(resource) File.exists?(test_file).should == false end end describe "when determining whether the resource can be changed" do before do @resource.stubs(:purging?).returns true @resource.stubs(:deleting?).returns true end it "should be true if the resource is not being purged" do @resource.expects(:purging?).returns false @harness.should be_allow_changes(@resource) end it "should be true if the resource is not being deleted" do @resource.expects(:deleting?).returns false @harness.should be_allow_changes(@resource) end it "should be true if the resource has no dependents" do @harness.relationship_graph.expects(:dependents).with(@resource).returns [] @harness.should be_allow_changes(@resource) end it "should be true if all dependents are being deleted" do dep = stub 'dependent', :deleting? => true @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] @resource.expects(:purging?).returns true @harness.should be_allow_changes(@resource) end it "should be false if the resource's dependents are not being deleted" do dep = stub 'dependent', :deleting? => false, :ref => "myres" @resource.expects(:warning) @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] @harness.should_not be_allow_changes(@resource) end end describe "when finding the schedule" do before do @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog end it "should warn and return nil if the resource has no catalog" do @resource.catalog = nil @resource.expects(:warning) @harness.schedule(@resource).should be_nil end it "should return nil if the resource specifies no schedule" do @harness.schedule(@resource).should be_nil end it "should fail if the named schedule cannot be found" do @resource[:schedule] = "whatever" @resource.expects(:fail) @harness.schedule(@resource) end it "should return the named schedule if it exists" do sched = Puppet::Type.type(:schedule).new(:name => "sched") @catalog.add_resource(sched) @resource[:schedule] = "sched" @harness.schedule(@resource).to_s.should == sched.to_s end end describe "when determining if a resource is scheduled" do before do @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @status = Puppet::Resource::Status.new(@resource) end it "should return true if 'ignoreschedules' is set" do Puppet[:ignoreschedules] = true @resource[:schedule] = "meh" @harness.should be_scheduled(@status, @resource) end it "should return true if the resource has no schedule set" do @harness.should be_scheduled(@status, @resource) end it "should return the result of matching the schedule with the cached 'checked' time if a schedule is set" do t = Time.now @harness.expects(:cached).with(@resource, :checked).returns(t) sched = Puppet::Type.type(:schedule).new(:name => "sched") @catalog.add_resource(sched) @resource[:schedule] = "sched" sched.expects(:match?).with(t.to_i).returns "feh" @harness.scheduled?(@status, @resource).should == "feh" end end it "should be able to cache data in the Storage module" do data = {} Puppet::Util::Storage.expects(:cache).with(@resource).returns data @harness.cache(@resource, :foo, "something") data[:foo].should == "something" end it "should be able to retrieve data from the cache" do data = {:foo => "other"} Puppet::Util::Storage.expects(:cache).with(@resource).returns data @harness.cached(@resource, :foo).should == "other" end end diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index ab76130e3..4157e58ac 100755 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -1,441 +1,440 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/transaction' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Transaction do before do @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) end it "should delegate its event list to the event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.expects(:events).returns %w{my events} @transaction.events.should == %w{my events} end it "should delegate adding times to its report" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.report.expects(:add_times).with(:foo, 10) @transaction.report.expects(:add_times).with(:bar, 20) @transaction.add_times :foo => 10, :bar => 20 end it "should be able to accept resource status instances" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource).should equal(status) end it "should be able to look resource status up by resource reference" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource.to_s).should equal(status) end # This will basically only ever be used during testing. it "should automatically create resource statuses if asked for a non-existent status" do resource = Puppet::Type.type(:notify).new :title => "foobar" @transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status) end it "should add provided resource statuses to its report" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.report.resource_statuses[resource.to_s].should equal(status) end it "should consider a resource to be failed if a status instance exists for that resource and indicates it is failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_failed(resource) end it "should not consider a resource to be failed if a status instance exists for that resource but indicates it is not failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_failed(resource) end it "should consider there to be failed resources if any statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_any_failed end it "should not consider there to be failed resources if no statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_any_failed end it "should be possible to replace the report object" do report = Puppet::Transaction::Report.new("apply") @transaction.report = report @transaction.report.should == report end it "should consider a resource to have failed dependencies if any of its dependencies are failed" describe "when initializing" do it "should create an event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager) @transaction.event_manager.transaction.should equal(@transaction) end it "should create a resource harness" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness) @transaction.resource_harness.transaction.should equal(@transaction) end end describe "when evaluating a resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.stubs(:skip?).returns false @resource = Puppet::Type.type(:file).new :path => @basepath end it "should check whether the resource should be skipped" do @transaction.expects(:skip?).with(@resource).returns false @transaction.eval_resource(@resource) end it "should process events" do @transaction.event_manager.expects(:process_events).with(@resource) @transaction.eval_resource(@resource) end describe "and the resource should be skipped" do before do @transaction.expects(:skip?).with(@resource).returns true end it "should mark the resource's status as skipped" do @transaction.eval_resource(@resource) @transaction.resource_status(@resource).should be_skipped end end end describe "when applying a resource" do before do @resource = Puppet::Type.type(:file).new :path => @basepath @status = Puppet::Resource::Status.new(@resource) @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.stubs(:queue_events) @transaction.resource_harness.stubs(:evaluate).returns(@status) end it "should use its resource harness to apply the resource" do @transaction.resource_harness.expects(:evaluate).with(@resource) @transaction.apply(@resource) end it "should add the resulting resource status to its status list" do @transaction.apply(@resource) @transaction.resource_status(@resource).should be_instance_of(Puppet::Resource::Status) end it "should queue any events added to the resource status" do @status.expects(:events).returns %w{a b} @transaction.event_manager.expects(:queue_events).with(@resource, ["a", "b"]) @transaction.apply(@resource) end it "should log and skip any resources that cannot be applied" do @transaction.resource_harness.expects(:evaluate).raises ArgumentError @resource.expects(:err) @transaction.apply(@resource) @transaction.report.resource_statuses[@resource.to_s].should be_nil end end describe "when generating resources" do it "should call 'generate' on all created resources" do first = Puppet::Type.type(:notify).new(:name => "first") second = Puppet::Type.type(:notify).new(:name => "second") third = Puppet::Type.type(:notify).new(:name => "third") @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) first.expects(:generate).returns [second] second.expects(:generate).returns [third] third.expects(:generate) @transaction.generate_additional_resources(first) end it "should finish all resources" do generator = stub 'generator', :depthfirst? => true, :tags => [] resource = stub 'resource', :tag => nil @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.expects(:generate).returns [resource] @catalog.expects(:add_resource).yields(resource) resource.expects(:finish) @transaction.generate_additional_resources(generator) end it "should skip generated resources that conflict with existing resources" do generator = mock 'generator', :tags => [] resource = stub 'resource', :tag => nil @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.expects(:generate).returns [resource] @catalog.expects(:add_resource).raises(Puppet::Resource::Catalog::DuplicateResourceError.new("foo")) resource.expects(:finish).never resource.expects(:info) # log that it's skipped @transaction.generate_additional_resources(generator) end it "should copy all tags to the newly generated resources" do child = stub 'child' generator = stub 'resource', :tags => ["one", "two"] @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.stubs(:generate).returns [child] @catalog.stubs(:add_resource) child.expects(:tag).with("one", "two") child.expects(:finish) generator.expects(:depthfirst?) @transaction.generate_additional_resources(generator) end end describe "when skipping a resource" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should skip resource with missing tags" do @transaction.stubs(:missing_tags?).returns(true) @transaction.should be_skip(@resource) end it "should skip unscheduled resources" do @transaction.stubs(:scheduled?).returns(false) @transaction.should be_skip(@resource) end it "should skip resources with failed dependencies" do @transaction.stubs(:failed_dependencies?).returns(true) @transaction.should be_skip(@resource) end it "should skip virtual resource" do @resource.stubs(:virtual?).returns true @transaction.should be_skip(@resource) end end describe "when determining if tags are missing" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) @transaction.stubs(:ignore_tags?).returns false end it "should not be missing tags if tags are being ignored" do @transaction.expects(:ignore_tags?).returns true @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should not be missing tags if the transaction tags are empty" do @transaction.tags = [] @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should otherwise let the resource determine if it is missing tags" do tags = ['one', 'two'] @transaction.tags = tags @resource.expects(:tagged?).with(*tags).returns(false) @transaction.should be_missing_tags(@resource) end end describe "when determining if a resource should be scheduled" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should always schedule resources if 'ignoreschedules' is set" do @transaction.ignoreschedules = true @transaction.resource_harness.expects(:scheduled?).never @transaction.should be_scheduled(@resource) end it "should let the resource harness determine whether the resource should be scheduled" do @transaction.resource_harness.expects(:scheduled?).with(@transaction.resource_status(@resource), @resource).returns "feh" @transaction.scheduled?(@resource).should == "feh" end end describe "when prefetching" do it "should match resources by name, not title" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) # Have both a title and name resource = Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh" @catalog.add_resource resource resource.provider.class.expects(:prefetch).with("bar" => resource) @transaction.prefetch end end it "should return all resources for which the resource status indicates the resource has changed when determinig changed resources" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) names = [] 2.times do |i| name = File.join(@basepath, "file#{i}") resource = Puppet::Type.type(:file).new :path => name names << resource.to_s @catalog.add_resource resource @transaction.add_resource_status Puppet::Resource::Status.new(resource) end @transaction.resource_status(names[0]).changed = true @transaction.changed?.should == [@catalog.resource(names[0])] end describe 'when checking application run state' do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should return true for :stop_processing? if Puppet::Application.stop_requested? is true' do Puppet::Application.stubs(:stop_requested?).returns(true) @transaction.stop_processing?.should be_true end it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do Puppet::Application.stubs(:stop_requested?).returns(false) @transaction.stop_processing?.should be_false end describe 'within an evaluate call' do before do @resource = Puppet::Type.type(:notify).new :title => "foobar" @catalog.add_resource @resource @transaction.stubs(:prepare) end it 'should stop processing if :stop_processing? is true' do @transaction.stubs(:stop_processing?).returns(true) @transaction.expects(:eval_resource).never @transaction.evaluate end it 'should continue processing if :stop_processing? is false' do @transaction.stubs(:stop_processing?).returns(false) @transaction.expects(:eval_resource).returns(nil) @transaction.evaluate end end end end describe Puppet::Transaction, " when determining tags" do before do @config = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@config) end it "should default to the tags specified in the :tags setting" do Puppet.expects(:[]).with(:tags).returns("one") @transaction.tags.should == %w{one} end it "should split tags based on ','" do Puppet.expects(:[]).with(:tags).returns("one,two") @transaction.tags.should == %w{one two} end it "should use any tags set after creation" do Puppet.expects(:[]).with(:tags).never @transaction.tags = %w{one two} @transaction.tags.should == %w{one two} end it "should always convert assigned tags to an array" do @transaction.tags = "one::two" @transaction.tags.should == %w{one::two} end it "should accept a comma-delimited string" do @transaction.tags = "one, two" @transaction.tags.should == %w{one two} end it "should accept an empty string" do @transaction.tags = "" @transaction.tags.should == [] end end diff --git a/spec/unit/transportable_spec.rb b/spec/unit/transportable_spec.rb old mode 100644 new mode 100755 diff --git a/spec/unit/type/augeas_spec.rb b/spec/unit/type/augeas_spec.rb old mode 100644 new mode 100755 index dece395e1..c8dc207f9 --- a/spec/unit/type/augeas_spec.rb +++ b/spec/unit/type/augeas_spec.rb @@ -1,120 +1,119 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' augeas = Puppet::Type.type(:augeas) describe augeas do describe "when augeas is present", :if => Puppet.features.augeas? do it "should have a default provider inheriting from Puppet::Provider" do augeas.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should have a valid provider" do augeas.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) end end describe "basic structure" do it "should be able to create a instance" do provider_class = Puppet::Type::Augeas.provider(Puppet::Type::Augeas.providers[0]) Puppet::Type::Augeas.expects(:defaultprovider).returns provider_class augeas.new(:name => "bar").should_not be_nil end it "should have an parse_commands feature" do augeas.provider_feature(:parse_commands).should_not be_nil end it "should have an need_to_run? feature" do augeas.provider_feature(:need_to_run?).should_not be_nil end it "should have an execute_changes feature" do augeas.provider_feature(:execute_changes).should_not be_nil end properties = [:returns] params = [:name, :context, :onlyif, :changes, :root, :load_path, :type_check] properties.each do |property| it "should have a #{property} property" do augeas.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do augeas.attrclass(property).doc.should be_instance_of(String) end end params.each do |param| it "should have a #{param} parameter" do augeas.attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} parameter" do augeas.attrclass(param).doc.should be_instance_of(String) end end end describe "default values" do before do provider_class = augeas.provider(augeas.providers[0]) augeas.expects(:defaultprovider).returns provider_class end it "should be blank for context" do augeas.new(:name => :context)[:context].should == "" end it "should be blank for onlyif" do augeas.new(:name => :onlyif)[:onlyif].should == "" end it "should be blank for load_path" do augeas.new(:name => :load_path)[:load_path].should == "" end it "should be / for root" do augeas.new(:name => :root)[:root].should == "/" end it "should be false for type_check" do augeas.new(:name => :type_check)[:type_check].should == :false end end describe "provider interaction" do it "should return 0 if it does not need to run" do provider = stub("provider", :need_to_run? => false) resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) changes = augeas.attrclass(:returns).new(:resource => resource) changes.retrieve.should == 0 end it "should return :need_to_run if it needs to run" do provider = stub("provider", :need_to_run? => true) resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) changes = augeas.attrclass(:returns).new(:resource => resource) changes.retrieve.should == :need_to_run end end describe "loading specific files" do it "should require lens when incl is used" do lambda { augeas.new(:name => :no_lens, :incl => "/etc/hosts")}.should raise_error(Puppet::Error) end it "should require incl when lens is used" do lambda { augeas.new(:name => :no_incl, :lens => "Hosts.lns") }.should raise_error(Puppet::Error) end it "should set the context when a specific file is used" do fake_provider = stub_everything "fake_provider" augeas.stubs(:defaultprovider).returns fake_provider augeas.new(:name => :no_incl, :lens => "Hosts.lns", :incl => "/etc/hosts")[:context].should == "/files/etc/hosts" end end end diff --git a/spec/unit/type/component_spec.rb b/spec/unit/type/component_spec.rb index 60abfafbf..9d6d71926 100755 --- a/spec/unit/type/component_spec.rb +++ b/spec/unit/type/component_spec.rb @@ -1,63 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' component = Puppet::Type.type(:component) describe component do it "should have a :name attribute" do component.attrclass(:name).should_not be_nil end it "should use Class as its type when a normal string is provided as the title" do component.new(:name => "bar").ref.should == "Class[Bar]" end it "should always produce a resource reference string as its title" do component.new(:name => "bar").title.should == "Class[Bar]" end it "should have a reference string equivalent to its title" do comp = component.new(:name => "Foo[bar]") comp.title.should == comp.ref end it "should alias itself to its reference if it has a catalog and the catalog does not already have a resource with the same reference" do catalog = mock 'catalog' catalog.expects(:resource).with("Foo[bar]").returns nil catalog.expects(:alias).with { |resource, name| resource.is_a?(component) and name == "Foo[bar]" } component.new(:name => "Foo[bar]", :catalog => catalog) end it "should not fail when provided an invalid value" do comp = component.new(:name => "Foo[bar]") lambda { comp[:yayness] = "ey" }.should_not raise_error end it "should return previously provided invalid values" do comp = component.new(:name => "Foo[bar]") comp[:yayness] = "eh" comp[:yayness].should == "eh" end it "should correctly support metaparameters" do comp = component.new(:name => "Foo[bar]", :require => "Foo[bar]") comp.parameter(:require).should be_instance_of(component.attrclass(:require)) end describe "when building up the path" do it "should produce the class name if the component models a class" do component.new(:name => "Class[foo]").pathbuilder.must == ["Foo"] end it "should produce an empty string if the component models the 'main' class" do component.new(:name => "Class[main]").pathbuilder.must == [""] end it "should produce a resource reference if the component does not model a class" do component.new(:name => "Foo[bar]").pathbuilder.must == ["Foo[bar]"] end end end diff --git a/spec/unit/type/computer_spec.rb b/spec/unit/type/computer_spec.rb index 3177884f7..1e15d7a4f 100755 --- a/spec/unit/type/computer_spec.rb +++ b/spec/unit/type/computer_spec.rb @@ -1,81 +1,80 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' computer = Puppet::Type.type(:computer) describe Puppet::Type.type(:computer), " when checking computer objects" do before do provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) Puppet::Type::Computer.expects(:defaultprovider).returns provider_class @resource = Puppet::Type::Computer.new( :name => "puppetcomputertest", :en_address => "aa:bb:cc:dd:ee:ff", :ip_address => "1.2.3.4") @properties = {} @ensure = Puppet::Type::Computer.attrclass(:ensure).new(:resource => @resource) end it "should be able to create a instance" do provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) Puppet::Type::Computer.expects(:defaultprovider).returns provider_class computer.new(:name => "bar").should_not be_nil end properties = [:en_address, :ip_address] params = [:name] properties.each do |property| it "should have a #{property} property" do computer.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do computer.attrclass(property).doc.should be_instance_of(String) end it "should accept :absent as a value" do prop = computer.attrclass(property).new(:resource => @resource) prop.should = :absent prop.should.must == :absent end end params.each do |param| it "should have a #{param} parameter" do computer.attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} parameter" do computer.attrclass(param).doc.should be_instance_of(String) end end describe "default values" do before do provider_class = computer.provider(computer.providers[0]) computer.expects(:defaultprovider).returns provider_class end it "should be nil for en_address" do computer.new(:name => :en_address)[:en_address].should == nil end it "should be nil for ip_address" do computer.new(:name => :ip_address)[:ip_address].should == nil end end describe "when managing the ensure property" do it "should support a :present value" do lambda { @ensure.should = :present }.should_not raise_error end it "should support an :absent value" do lambda { @ensure.should = :absent }.should_not raise_error end end end diff --git a/spec/unit/type/cron_spec.rb b/spec/unit/type/cron_spec.rb index 5466b4d58..7bf92eb02 100755 --- a/spec/unit/type/cron_spec.rb +++ b/spec/unit/type/cron_spec.rb @@ -1,481 +1,490 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:cron) do before do @class = Puppet::Type.type(:cron) # Init a fake provider @provider_class = stub 'provider_class', :ancestors => [], :name => 'fake', :suitable? => true, :supports_parameter? => true @class.stubs(:defaultprovider).returns @provider_class @class.stubs(:provider).returns @provider_class @provider = stub 'provider', :class => @provider_class, :clean => nil @provider.stubs(:is_a?).returns false @provider_class.stubs(:new).returns @provider @cron = @class.new( :name => "foo" ) end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:command, :special, :minute, :hour, :weekday, :month, :monthday, :environment, :user, :target].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end [:command, :minute, :hour, :weekday, :month, :monthday].each do |cronparam| it "should have #{cronparam} of type CronParam" do @class.attrclass(cronparam).ancestors.should include CronParam end end end describe "when validating attribute" do describe "ensure" do it "should support present as a value for ensure" do proc { @class.new(:name => 'foo', :ensure => :present) }.should_not raise_error end it "should support absent as a value for ensure" do proc { @class.new(:name => 'foo', :ensure => :present) }.should_not raise_error end end describe "minute" do it "should support absent" do proc { @class.new(:name => 'foo', :minute => 'absent') }.should_not raise_error end it "should support *" do proc { @class.new(:name => 'foo', :minute => '*') }.should_not raise_error end it "should translate absent to :absent" do @class.new(:name => 'foo', :minute => 'absent')[:minute].should == :absent end it "should translate * to :absent" do @class.new(:name => 'foo', :minute => '*')[:minute].should == :absent end it "should support valid single values" do proc { @class.new(:name => 'foo', :minute => '0') }.should_not raise_error proc { @class.new(:name => 'foo', :minute => '1') }.should_not raise_error proc { @class.new(:name => 'foo', :minute => '59') }.should_not raise_error end it "should not support non numeric characters" do proc { @class.new(:name => 'foo', :minute => 'z59') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => '5z9') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => '59z') }.should raise_error(Puppet::Error) end it "should not support single values out of range" do proc { @class.new(:name => 'foo', :minute => '-1') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => '60') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => '61') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => '120') }.should raise_error(Puppet::Error) end it "should support valid multiple values" do proc { @class.new(:name => 'foo', :minute => ['0','1','59'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :minute => ['40','30','20'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :minute => ['10','30','20'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { @class.new(:name => 'foo', :minute => ['0','1','60'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => ['0','120','59'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => ['-1','1','59'] ) }.should raise_error(Puppet::Error) # two invalid proc { @class.new(:name => 'foo', :minute => ['0','61','62'] ) }.should raise_error(Puppet::Error) # all invalid proc { @class.new(:name => 'foo', :minute => ['-1','61','62'] ) }.should raise_error(Puppet::Error) end it "should support valid step syntax" do proc { @class.new(:name => 'foo', :minute => '*/2' ) }.should_not raise_error proc { @class.new(:name => 'foo', :minute => '10-16/2' ) }.should_not raise_error end it "should not support invalid steps" do proc { @class.new(:name => 'foo', :minute => '*/A' ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :minute => '*/2A' ) }.should raise_error(Puppet::Error) # As it turns out cron does not complaining about steps that exceed the valid range # proc { @class.new(:name => 'foo', :minute => '*/120' ) }.should raise_error(Puppet::Error) end end describe "hour" do it "should support absent" do proc { @class.new(:name => 'foo', :hour => 'absent') }.should_not raise_error end it "should support *" do proc { @class.new(:name => 'foo', :hour => '*') }.should_not raise_error end it "should translate absent to :absent" do @class.new(:name => 'foo', :hour => 'absent')[:hour].should == :absent end it "should translate * to :absent" do @class.new(:name => 'foo', :hour => '*')[:hour].should == :absent end it "should support valid single values" do proc { @class.new(:name => 'foo', :hour => '0') }.should_not raise_error proc { @class.new(:name => 'foo', :hour => '11') }.should_not raise_error proc { @class.new(:name => 'foo', :hour => '12') }.should_not raise_error proc { @class.new(:name => 'foo', :hour => '13') }.should_not raise_error proc { @class.new(:name => 'foo', :hour => '23') }.should_not raise_error end it "should not support non numeric characters" do proc { @class.new(:name => 'foo', :hour => 'z15') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => '1z5') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => '15z') }.should raise_error(Puppet::Error) end it "should not support single values out of range" do proc { @class.new(:name => 'foo', :hour => '-1') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => '24') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => '120') }.should raise_error(Puppet::Error) end it "should support valid multiple values" do proc { @class.new(:name => 'foo', :hour => ['0','1','23'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :hour => ['5','16','14'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :hour => ['16','13','9'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { @class.new(:name => 'foo', :hour => ['0','1','24'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => ['0','-1','5'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => ['-1','1','23'] ) }.should raise_error(Puppet::Error) # two invalid proc { @class.new(:name => 'foo', :hour => ['0','25','26'] ) }.should raise_error(Puppet::Error) # all invalid proc { @class.new(:name => 'foo', :hour => ['-1','24','120'] ) }.should raise_error(Puppet::Error) end it "should support valid step syntax" do proc { @class.new(:name => 'foo', :hour => '*/2' ) }.should_not raise_error proc { @class.new(:name => 'foo', :hour => '10-18/4' ) }.should_not raise_error end it "should not support invalid steps" do proc { @class.new(:name => 'foo', :hour => '*/A' ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :hour => '*/2A' ) }.should raise_error(Puppet::Error) # As it turns out cron does not complaining about steps that exceed the valid range # proc { @class.new(:name => 'foo', :hour => '*/26' ) }.should raise_error(Puppet::Error) end end describe "weekday" do it "should support absent" do proc { @class.new(:name => 'foo', :weekday => 'absent') }.should_not raise_error end it "should support *" do proc { @class.new(:name => 'foo', :weekday => '*') }.should_not raise_error end it "should translate absent to :absent" do @class.new(:name => 'foo', :weekday => 'absent')[:weekday].should == :absent end it "should translate * to :absent" do @class.new(:name => 'foo', :weekday => '*')[:weekday].should == :absent end it "should support valid numeric weekdays" do proc { @class.new(:name => 'foo', :weekday => '0') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => '1') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => '6') }.should_not raise_error # According to http://www.manpagez.com/man/5/crontab 7 is also valid (Sunday) proc { @class.new(:name => 'foo', :weekday => '7') }.should_not raise_error end it "should support valid weekdays as words (3 character version)" do proc { @class.new(:name => 'foo', :weekday => 'Monday') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Tuesday') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Wednesday') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Thursday') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Friday') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Saturday') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Sunday') }.should_not raise_error end it "should support valid weekdays as words (3 character version)" do proc { @class.new(:name => 'foo', :weekday => 'Mon') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Tue') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Wed') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Thu') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Fri') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Sat') }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => 'Sun') }.should_not raise_error end it "should not support numeric values out of range" do proc { @class.new(:name => 'foo', :weekday => '-1') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :weekday => '8') }.should raise_error(Puppet::Error) end it "should not support invalid weekday names" do proc { @class.new(:name => 'foo', :weekday => 'Sar') }.should raise_error(Puppet::Error) end it "should support valid multiple values" do proc { @class.new(:name => 'foo', :weekday => ['0','1','6'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => ['Mon','Wed','Friday'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { @class.new(:name => 'foo', :weekday => ['0','1','8'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :weekday => ['Mon','Fii','Sat'] ) }.should raise_error(Puppet::Error) # two invalid proc { @class.new(:name => 'foo', :weekday => ['Mos','Fii','Sat'] ) }.should raise_error(Puppet::Error) # all invalid proc { @class.new(:name => 'foo', :weekday => ['Mos','Fii','Saa'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :weekday => ['-1','8','11'] ) }.should raise_error(Puppet::Error) end it "should support valid step syntax" do proc { @class.new(:name => 'foo', :weekday => '*/2' ) }.should_not raise_error proc { @class.new(:name => 'foo', :weekday => '0-4/2' ) }.should_not raise_error end it "should not support invalid steps" do proc { @class.new(:name => 'foo', :weekday => '*/A' ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :weekday => '*/2A' ) }.should raise_error(Puppet::Error) # As it turns out cron does not complaining about steps that exceed the valid range # proc { @class.new(:name => 'foo', :weekday => '*/9' ) }.should raise_error(Puppet::Error) end end describe "month" do it "should support absent" do proc { @class.new(:name => 'foo', :month => 'absent') }.should_not raise_error end it "should support *" do proc { @class.new(:name => 'foo', :month => '*') }.should_not raise_error end it "should translate absent to :absent" do @class.new(:name => 'foo', :month => 'absent')[:month].should == :absent end it "should translate * to :absent" do @class.new(:name => 'foo', :month => '*')[:month].should == :absent end it "should support valid numeric values" do proc { @class.new(:name => 'foo', :month => '1') }.should_not raise_error proc { @class.new(:name => 'foo', :month => '12') }.should_not raise_error end it "should support valid months as words" do proc { @class.new(:name => 'foo', :month => 'January') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'February') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'March') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'April') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'May') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'June') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'July') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'August') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'September') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'October') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'November') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'December') }.should_not raise_error end it "should support valid months as words (3 character short version)" do proc { @class.new(:name => 'foo', :month => 'Jan') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Feb') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Mar') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Apr') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'May') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Jun') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Jul') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Aug') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Sep') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Oct') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Nov') }.should_not raise_error proc { @class.new(:name => 'foo', :month => 'Dec') }.should_not raise_error end it "should not support numeric values out of range" do proc { @class.new(:name => 'foo', :month => '-1') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => '0') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => '13') }.should raise_error(Puppet::Error) end it "should not support words that are not valid months" do proc { @class.new(:name => 'foo', :month => 'Jal') }.should raise_error(Puppet::Error) end it "should not support single values out of range" do proc { @class.new(:name => 'foo', :month => '-1') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => '60') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => '61') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => '120') }.should raise_error(Puppet::Error) end it "should support valid multiple values" do proc { @class.new(:name => 'foo', :month => ['1','9','12'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :month => ['Jan','March','Jul'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { @class.new(:name => 'foo', :month => ['0','1','12'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => ['1','13','10'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => ['Jan','Feb','Jxx'] ) }.should raise_error(Puppet::Error) # two invalid proc { @class.new(:name => 'foo', :month => ['Jan','Fex','Jux'] ) }.should raise_error(Puppet::Error) # all invalid proc { @class.new(:name => 'foo', :month => ['-1','0','13'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => ['Jax','Fex','Aux'] ) }.should raise_error(Puppet::Error) end it "should support valid step syntax" do proc { @class.new(:name => 'foo', :month => '*/2' ) }.should_not raise_error proc { @class.new(:name => 'foo', :month => '1-12/3' ) }.should_not raise_error end it "should not support invalid steps" do proc { @class.new(:name => 'foo', :month => '*/A' ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :month => '*/2A' ) }.should raise_error(Puppet::Error) # As it turns out cron does not complaining about steps that exceed the valid range # proc { @class.new(:name => 'foo', :month => '*/13' ) }.should raise_error(Puppet::Error) end end describe "monthday" do it "should support absent" do proc { @class.new(:name => 'foo', :monthday => 'absent') }.should_not raise_error end it "should support *" do proc { @class.new(:name => 'foo', :monthday => '*') }.should_not raise_error end it "should translate absent to :absent" do @class.new(:name => 'foo', :monthday => 'absent')[:monthday].should == :absent end it "should translate * to :absent" do @class.new(:name => 'foo', :monthday => '*')[:monthday].should == :absent end it "should support valid single values" do proc { @class.new(:name => 'foo', :monthday => '1') }.should_not raise_error proc { @class.new(:name => 'foo', :monthday => '30') }.should_not raise_error proc { @class.new(:name => 'foo', :monthday => '31') }.should_not raise_error end it "should not support non numeric characters" do proc { @class.new(:name => 'foo', :monthday => 'z23') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => '2z3') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => '23z') }.should raise_error(Puppet::Error) end it "should not support single values out of range" do proc { @class.new(:name => 'foo', :monthday => '-1') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => '0') }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => '32') }.should raise_error(Puppet::Error) end it "should support valid multiple values" do proc { @class.new(:name => 'foo', :monthday => ['1','23','31'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :monthday => ['31','23','1'] ) }.should_not raise_error proc { @class.new(:name => 'foo', :monthday => ['1','31','23'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { @class.new(:name => 'foo', :monthday => ['1','23','32'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => ['-1','12','23'] ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => ['13','32','30'] ) }.should raise_error(Puppet::Error) # two invalid proc { @class.new(:name => 'foo', :monthday => ['-1','0','23'] ) }.should raise_error(Puppet::Error) # all invalid proc { @class.new(:name => 'foo', :monthday => ['-1','0','32'] ) }.should raise_error(Puppet::Error) end it "should support valid step syntax" do proc { @class.new(:name => 'foo', :monthday => '*/2' ) }.should_not raise_error proc { @class.new(:name => 'foo', :monthday => '10-16/2' ) }.should_not raise_error end it "should not support invalid steps" do proc { @class.new(:name => 'foo', :monthday => '*/A' ) }.should raise_error(Puppet::Error) proc { @class.new(:name => 'foo', :monthday => '*/2A' ) }.should raise_error(Puppet::Error) # As it turns out cron does not complaining about steps that exceed the valid range # proc { @class.new(:name => 'foo', :monthday => '*/32' ) }.should raise_error(Puppet::Error) end end describe "environment" do it "it should accept an :environment that looks like a path" do lambda do @cron[:environment] = 'PATH=/bin:/usr/bin:/usr/sbin' end.should_not raise_error end it "should not accept environment variables that do not contain '='" do lambda do @cron[:environment] = "INVALID" end.should raise_error(Puppet::Error) end it "should accept empty environment variables that do not contain '='" do lambda do @cron[:environment] = "MAILTO=" end.should_not raise_error(Puppet::Error) end it "should accept 'absent'" do lambda do @cron[:environment] = 'absent' end.should_not raise_error(Puppet::Error) end end end + + it "should require a command when adding an entry" do + entry = @class.new(:name => "test_entry", :ensure => :present) + expect { entry.value(:command) }.should raise_error(/No command/) + end + + it "should not require a command when removing an entry" do + entry = @class.new(:name => "test_entry", :ensure => :absent) + entry.value(:command).should == nil + end end diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb index 86b824423..47d1b8523 100755 --- a/spec/unit/type/exec_spec.rb +++ b/spec/unit/type/exec_spec.rb @@ -1,660 +1,659 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:exec) do def exec_tester(command, exitstatus = 0, rest = {}) @user_name = 'some_user_name' @group_name = 'some_group_name' Puppet.features.stubs(:root?).returns(true) output = rest.delete(:output) || '' tries = rest[:tries] || 1 args = { :name => command, :path => @example_path, :user => @user_name, :group => @group_name, :logoutput => false, :loglevel => :err, :returns => 0 }.merge(rest) exec = Puppet::Type.type(:exec).new(args) status = stub "process", :exitstatus => exitstatus Puppet::Util::SUIDManager.expects(:run_and_capture).times(tries). with([command], @user_name, @group_name).returns([output, status]) return exec end before do @command = Puppet.features.posix? ? '/bin/true whatever' : '"C:/Program Files/something.exe" whatever' end describe "when not stubbing the provider" do before do @executable = Puppet.features.posix? ? '/bin/true' : 'C:/Program Files/something.exe' File.stubs(:exists?).returns false File.stubs(:exists?).with(@executable).returns true File.stubs(:exists?).with('/bin/false').returns true @example_path = Puppet.features.posix? ? %w{/usr/bin /bin} : [ "C:/Program Files/something/bin", "C:/Ruby/bin" ] File.stubs(:exists?).with(File.join(@example_path[0],"true")).returns true File.stubs(:exists?).with(File.join(@example_path[0],"false")).returns true end it "should return :executed_command as its event" do resource = Puppet::Type.type(:exec).new :command => @command resource.parameter(:returns).event.name.should == :executed_command end describe "when execing" do it "should use the 'run_and_capture' method to exec" do exec_tester("true").refresh.should == :executed_command end it "should report a failure" do proc { exec_tester('false', 1).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should not report a failure if the exit status is specified in a returns array" do proc { exec_tester("false", 1, :returns => [0, 1]).refresh }.should_not raise_error end it "should report a failure if the exit status is not specified in a returns array" do proc { exec_tester('false', 1, :returns => [0, 100]).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should log the output on success" do output = "output1\noutput2\n" exec_tester('false', 0, :output => output, :logoutput => true).refresh output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :logoutput => true).refresh }. should raise_error(Puppet::Error) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end end describe "when logoutput=>on_failure is set" do it "should log the output on failure" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure when returns is specified as an array" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :returns => [0, 100], :logoutput => :on_failure).refresh }.should raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "shouldn't log the output on success" do exec_tester('true', 0, :output => "a\nb\nc\n", :logoutput => :on_failure).refresh @logs.should == [] end end it "shouldn't log the output on success when non-zero exit status is in a returns array" do exec_tester("true", 100, :output => "a\n", :logoutput => :on_failure, :returns => [1, 100]).refresh @logs.should == [] end describe " when multiple tries are set," do it "should repeat the command attempt 'tries' times on failure and produce an error" do tries = 5 resource = exec_tester("false", 1, :tries => tries, :try_sleep => 0) proc { resource.refresh }.should raise_error(Puppet::Error) end end end it "should be able to autorequire files mentioned in the command" do catalog = Puppet::Resource::Catalog.new tmp = Puppet::Type.type(:file).new(:name => "/bin/foo") catalog.add_resource tmp execer = Puppet::Type.type(:exec).new(:name => "/bin/foo") catalog.add_resource execer catalog.relationship_graph.dependencies(execer).should == [tmp] end describe "when handling the path parameter" do expect = %w{one two three four} { "an array" => expect, "a colon separated list" => "one:two:three:four", "a semi-colon separated list" => "one;two;three;four", "both array and colon lists" => ["one", "two:three", "four"], "both array and semi-colon lists" => ["one", "two;three", "four"], "colon and semi-colon lists" => ["one:two", "three;four"] }.each do |test, input| it "should accept #{test}" do type = Puppet::Type.type(:exec).new(:name => @command, :path => input) type[:path].should == expect end end end describe "when setting user" do it "should fail if we are not root" do Puppet.features.stubs(:root?).returns(false) expect { Puppet::Type.type(:exec).new(:name => @command, :user => 'input') }. should raise_error Puppet::Error, /Parameter user failed/ end ['one', 2, 'root', 4294967295, 4294967296].each do |value| it "should accept '#{value}' as user if we are root" do Puppet.features.stubs(:root?).returns(true) type = Puppet::Type.type(:exec).new(:name => @command, :user => value) type[:user].should == value end end end describe "when setting group" do shared_examples_for "exec[:group]" do ['one', 2, 'wheel', 4294967295, 4294967296].each do |value| it "should accept '#{value}' without error or judgement" do type = Puppet::Type.type(:exec).new(:name => @command, :group => value) type[:group].should == value end end end describe "when running as root" do before :each do Puppet.features.stubs(:root?).returns(true) end it_behaves_like "exec[:group]" end describe "when not running as root" do before :each do Puppet.features.stubs(:root?).returns(false) end it_behaves_like "exec[:group]" end end describe "when setting cwd" do it_should_behave_like "all path parameters", :cwd, :array => false do def instance(path) Puppet::Type.type(:exec).new(:name => '/bin/true', :cwd => path) end end end shared_examples_for "all exec command parameters" do |param| { "relative" => "example", "absolute" => "/bin/example" }.sort.each do |name, command| describe "if command is #{name}" do before :each do @param = param end def test(command, valid) if @param == :name then instance = Puppet::Type.type(:exec).new() else instance = Puppet::Type.type(:exec).new(:name => "/bin/true") end if valid then instance.provider.expects(:validatecmd).returns(true) else instance.provider.expects(:validatecmd).raises(Puppet::Error, "from a stub") end instance[@param] = command end it "should work if the provider calls the command valid" do expect { test(command, true) }.should_not raise_error end it "should fail if the provider calls the command invalid" do expect { test(command, false) }. should raise_error Puppet::Error, /Parameter #{@param} failed: from a stub/ end end end end shared_examples_for "all exec command parameters that take arrays" do |param| describe "when given an array of inputs" do before :each do @test = Puppet::Type.type(:exec).new(:name => "/bin/true") end it "should accept the array when all commands return valid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(true) @test[param] = input @test[param].should == input end it "should reject the array when any commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).with(input.first).returns(false) input[1..-1].each do |cmd| @test.provider.expects(:validatecmd).with(cmd).returns(true) end @test[param] = input @test[param].should == input end it "should reject the array when all commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(false) @test[param] = input @test[param].should == input end end end describe "when setting refresh" do it_should_behave_like "all exec command parameters", :refresh end describe "for simple parameters" do before :each do @exec = Puppet::Type.type(:exec).new(:name => '/bin/true') end describe "when setting environment" do { "single values" => "foo=bar", "multiple values" => ["foo=bar", "baz=quux"], }.each do |name, data| it "should accept #{name}" do @exec[:environment] = data @exec[:environment].should == data end end { "single values" => "foo", "only values" => ["foo", "bar"], "any values" => ["foo=bar", "baz"] }.each do |name, data| it "should reject #{name} without assignment" do expect { @exec[:environment] = data }. should raise_error Puppet::Error, /Invalid environment setting/ end end end describe "when setting timeout" do [0, 0.1, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:timeout] = valid @exec[:timeout].should == valid end it "should accept '#{valid}' in an array as valid" do @exec[:timeout] = [valid] @exec[:timeout].should == valid end end ['1/2', '', 'foo', '5foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:timeout] = invalid }. should raise_error Puppet::Error, /The timeout must be a number/ end it "should reject '#{invalid}' in an array as invalid" do expect { @exec[:timeout] = [invalid] }. should raise_error Puppet::Error, /The timeout must be a number/ end end it "should fail if timeout is exceeded" do File.stubs(:exists?).with('/bin/sleep').returns(true) File.stubs(:exists?).with('sleep').returns(false) sleep_exec = Puppet::Type.type(:exec).new(:name => 'sleep 1', :path => ['/bin'], :timeout => '0.2') lambda { sleep_exec.refresh }.should raise_error Puppet::Error, "Command exceeded timeout" end it "should convert timeout to a float" do resource = Puppet::Type.type(:exec).new :command => "/bin/false", :timeout => "12" resource[:timeout].should be_a(Float) resource[:timeout].should == 12.0 end it "should munge negative timeouts to 0.0" do resource = Puppet::Type.type(:exec).new :command => "/bin/false", :timeout => "-12.0" resource.parameter(:timeout).value.should be_a(Float) resource.parameter(:timeout).value.should == 0.0 end end describe "when setting tries" do [1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:tries] = valid @exec[:tries].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:tries] = [valid] @exec[:tries].should == valid end end end [-3.5, -1, 0, 0.2, '1/2', '1_000_000', '+12', '', 'foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:tries] = invalid }. should raise_error Puppet::Error, /Tries must be an integer/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:tries] = [invalid] }. should raise_error Puppet::Error, /Tries must be an integer/ end end end end describe "when setting try_sleep" do [0, 0.2, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:try_sleep] = valid @exec[:try_sleep].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:try_sleep] = [valid] @exec[:try_sleep].should == valid end end end { -3.5 => "cannot be a negative number", -1 => "cannot be a negative number", '1/2' => 'must be a number', '1_000_000' => 'must be a number', '+12' => 'must be a number', '' => 'must be a number', 'foo' => 'must be a number', }.each do |invalid, error| it "should reject '#{invalid}' as invalid" do expect { @exec[:try_sleep] = invalid }. should raise_error Puppet::Error, /try_sleep #{error}/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:try_sleep] = [invalid] }. should raise_error Puppet::Error, /try_sleep #{error}/ end end end end describe "when setting refreshonly" do [:true, :false].each do |value| it "should accept '#{value}'" do @exec[:refreshonly] = value @exec[:refreshonly].should == value end end [1, 0, "1", "0", "yes", "y", "no", "n"].each do |value| it "should reject '#{value}'" do expect { @exec[:refreshonly] = value }. should raise_error(Puppet::Error, /Invalid value #{value.inspect}\. Valid values are true, false/ ) end end end describe "when setting creates" do it_should_behave_like "all path parameters", :creates, :array => true do def instance(path) Puppet::Type.type(:exec).new(:name => '/bin/true', :creates => path) end end end end describe "when setting unless" do it_should_behave_like "all exec command parameters", :unless it_should_behave_like "all exec command parameters that take arrays", :unless end describe "when setting onlyif" do it_should_behave_like "all exec command parameters", :onlyif it_should_behave_like "all exec command parameters that take arrays", :onlyif end describe "#check" do before :each do @test = Puppet::Type.type(:exec).new(:name => "/bin/true") end describe ":refreshonly" do { :true => false, :false => true }.each do |input, result| it "should return '#{result}' when given '#{input}'" do @test[:refreshonly] = input @test.check_all_attributes.should == result end end end describe ":creates" do before :all do @exist = "/" @unexist = "/this/path/should/never/exist" while FileTest.exist?(@unexist) do @unexist += "/foo" end end context "with a single item" do it "should run when the item does not exist" do @test[:creates] = @unexist @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = @exist @test.check_all_attributes.should == false end end context "with an array with one item" do it "should run when the item does not exist" do @test[:creates] = [@unexist] @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = [@exist] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run when all items do not exist" do @test[:creates] = [@unexist] * 3 @test.check_all_attributes.should == true end it "should not run when one item exists" do @test[:creates] = [@unexist, @exist, @unexist] @test.check_all_attributes.should == false end it "should not run when all items exist" do @test[:creates] = [@exist] * 3 end end end { :onlyif => { :pass => false, :fail => true }, :unless => { :pass => true, :fail => false }, }.each do |param, sense| describe ":#{param}" do before :each do @pass = "/magic/pass" @fail = "/magic/fail" @pass_status = stub('status', :exitstatus => sense[:pass] ? 0 : 1) @fail_status = stub('status', :exitstatus => sense[:fail] ? 0 : 1) @test.provider.stubs(:checkexe).returns(true) [true, false].each do |check| @test.provider.stubs(:run).with(@pass, check). returns(['test output', @pass_status]) @test.provider.stubs(:run).with(@fail, check). returns(['test output', @fail_status]) end end context "with a single item" do it "should run if the command exits non-zero" do @test[param] = @fail @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = @pass @test.check_all_attributes.should == false end end context "with an array with a single item" do it "should run if the command exits non-zero" do @test[param] = [@fail] @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = [@pass] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run if all the commands exits non-zero" do @test[param] = [@fail] * 3 @test.check_all_attributes.should == true end it "should not run if one command exits zero" do @test[param] = [@pass, @fail, @pass] @test.check_all_attributes.should == false end it "should not run if all command exits zero" do @test[param] = [@pass] * 3 @test.check_all_attributes.should == false end end end end end describe "#retrieve" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => "/bogus/cmd") end it "should return :notrun when check_all_attributes returns true" do @exec_resource.stubs(:check_all_attributes).returns true @exec_resource.retrieve[:returns].should == :notrun end it "should return default exit code 0 when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource.retrieve[:returns].should == ['0'] end it "should return the specified exit code when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource[:returns] = 42 @exec_resource.retrieve[:returns].should == ["42"] end end describe "#output" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => "/bogus/cmd") end it "should return the provider's run output" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh @exec_resource.output.should == 'silly output' end end describe "#refresh" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => "/bogus/cmd") end it "should call provider run with the refresh parameter if it is set" do provider = stub 'provider' @exec_resource.stubs(:provider).returns(provider) @exec_resource.stubs(:[]).with(:refresh).returns('/myother/bogus/cmd') provider.expects(:run).with('/myother/bogus/cmd') @exec_resource.refresh end it "should call provider run with the specified command if the refresh parameter is not set" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).with('/bogus/cmd').returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end it "should not run the provider if check_all_attributes is false" do @exec_resource.stubs(:check_all_attributes).returns false provider = stub 'provider' provider.expects(:run).never @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end end end diff --git a/spec/unit/type/file/checksum_spec.rb b/spec/unit/type/file/checksum_spec.rb old mode 100644 new mode 100755 index 16e8e99e1..b47f617cc --- a/spec/unit/type/file/checksum_spec.rb +++ b/spec/unit/type/file/checksum_spec.rb @@ -1,73 +1,72 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' checksum = Puppet::Type.type(:file).attrclass(:checksum) describe checksum do before do @resource = Puppet::Type.type(:file).new :path => "/foo/bar" @checksum = @resource.parameter(:checksum) end it "should be a parameter" do checksum.superclass.must == Puppet::Parameter end it "should use its current value when asked to sum content" do @checksum.value = :md5lite @checksum.expects(:md5lite).with("foobar").returns "yay" @checksum.sum("foobar") end it "should use :md5 to sum when no value is set" do @checksum.expects(:md5).with("foobar").returns "yay" @checksum.sum("foobar") end it "should return the summed contents with a checksum label" do sum = Digest::MD5.hexdigest("foobar") @resource[:checksum] = :md5 @checksum.sum("foobar").should == "{md5}#{sum}" end it "should use :md5 as its default type" do @checksum.default.should == :md5 end it "should use its current value when asked to sum a file's content" do @checksum.value = :md5lite @checksum.expects(:md5lite_file).with("/foo/bar").returns "yay" @checksum.sum_file("/foo/bar") end it "should use :md5 to sum a file when no value is set" do @checksum.expects(:md5_file).with("/foo/bar").returns "yay" @checksum.sum_file("/foo/bar") end it "should convert all sums to strings when summing files" do @checksum.value = :mtime @checksum.expects(:mtime_file).with("/foo/bar").returns Time.now lambda { @checksum.sum_file("/foo/bar") }.should_not raise_error end it "should return the summed contents of a file with a checksum label" do @resource[:checksum] = :md5 @checksum.expects(:md5_file).returns "mysum" @checksum.sum_file("/foo/bar").should == "{md5}mysum" end it "should return the summed contents of a stream with a checksum label" do @resource[:checksum] = :md5 @checksum.expects(:md5_stream).returns "mysum" @checksum.sum_stream.should == "{md5}mysum" end it "should yield the sum_stream block to the underlying checksum" do @resource[:checksum] = :md5 @checksum.expects(:md5_stream).yields("something").returns("mysum") @checksum.sum_stream do |sum| sum.should == "something" end end end diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index 7abc7c433..7af5f9d83 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -1,436 +1,435 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' content = Puppet::Type.type(:file).attrclass(:content) describe content do include PuppetSpec::Files before do @filename = tmpfile('testfile') @resource = Puppet::Type.type(:file).new :path => @filename File.open(@filename, 'w') {|f| f.write "initial file content"} content.stubs(:standalone?).returns(false) end describe "when determining the checksum type" do it "should use the type specified in the source checksum if a source is set" do @resource[:source] = "/foo" @resource.parameter(:source).expects(:checksum).returns "{md5lite}eh" @content = content.new(:resource => @resource) @content.checksum_type.should == :md5lite end it "should use the type specified by the checksum parameter if no source is set" do @resource[:checksum] = :md5lite @content = content.new(:resource => @resource) @content.checksum_type.should == :md5lite end end describe "when determining the actual content to write" do it "should use the set content if available" do @content = content.new(:resource => @resource) @content.should = "ehness" @content.actual_content.should == "ehness" end it "should not use the content from the source if the source is set" do source = mock 'source' @resource.expects(:parameter).never.with(:source).returns source @content = content.new(:resource => @resource) @content.actual_content.should be_nil end end describe "when setting the desired content" do it "should make the actual content available via an attribute" do @content = content.new(:resource => @resource) @content.stubs(:checksum_type).returns "md5" @content.should = "this is some content" @content.actual_content.should == "this is some content" end it "should store the checksum as the desired content" do @content = content.new(:resource => @resource) digest = Digest::MD5.hexdigest("this is some content") @content.stubs(:checksum_type).returns "md5" @content.should = "this is some content" @content.should.must == "{md5}#{digest}" end it "should not checksum 'absent'" do @content = content.new(:resource => @resource) @content.should = :absent @content.should.must == :absent end it "should accept a checksum as the desired content" do @content = content.new(:resource => @resource) digest = Digest::MD5.hexdigest("this is some content") string = "{md5}#{digest}" @content.should = string @content.should.must == string end end describe "when retrieving the current content" do it "should return :absent if the file does not exist" do @content = content.new(:resource => @resource) @resource.expects(:stat).returns nil @content.retrieve.should == :absent end it "should not manage content on directories" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "directory" @resource.expects(:stat).returns stat @content.retrieve.should be_nil end it "should not manage content on links" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "link" @resource.expects(:stat).returns stat @content.retrieve.should be_nil end it "should always return the checksum as a string" do @content = content.new(:resource => @resource) @resource[:checksum] = :mtime stat = mock 'stat', :ftype => "file" @resource.expects(:stat).returns stat time = Time.now @resource.parameter(:checksum).expects(:mtime_file).with(@resource[:path]).returns time @content.retrieve.should == "{mtime}#{time}" end it "should return the checksum of the file if it exists and is a normal file" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "file" @resource.expects(:stat).returns stat @resource.parameter(:checksum).expects(:md5_file).with(@resource[:path]).returns "mysum" @content.retrieve.should == "{md5}mysum" end end describe "when testing whether the content is in sync" do before do @resource[:ensure] = :file @content = content.new(:resource => @resource) end it "should return true if the resource shouldn't be a regular file" do @resource.expects(:should_be_file?).returns false @content.should = "foo" @content.must be_safe_insync("whatever") end it "should return false if the current content is :absent" do @content.should = "foo" @content.should_not be_safe_insync(:absent) end it "should return false if the file should be a file but is not present" do @resource.expects(:should_be_file?).returns true @content.should = "foo" @content.should_not be_safe_insync(:absent) end describe "and the file exists" do before do @resource.stubs(:stat).returns mock("stat") end it "should return false if the current contents are different from the desired content" do @content.should = "some content" @content.should_not be_safe_insync("other content") end it "should return true if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.must be_safe_insync("{md5}" + Digest::MD5.hexdigest("some content")) end describe "and Puppet[:show_diff] is set" do before do Puppet[:show_diff] = true end it "should display a diff if the current contents are different from the desired content" do @content.should = "some content" @content.expects(:diff).returns("my diff").once @content.expects(:print).with("my diff").once @content.safe_insync?("other content") end it "should not display a diff if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.expects(:diff).never @content.safe_insync?("{md5}" + Digest::MD5.hexdigest("some content")) end end end describe "and :replace is false" do before do @resource.stubs(:replace?).returns false end it "should be insync if the file exists and the content is different" do @resource.stubs(:stat).returns mock('stat') @content.must be_safe_insync("whatever") end it "should be insync if the file exists and the content is right" do @resource.stubs(:stat).returns mock('stat') @content.must be_safe_insync("something") end it "should not be insync if the file does not exist" do @content.should = "foo" @content.should_not be_safe_insync(:absent) end end end describe "when changing the content" do before do @content = content.new(:resource => @resource) @content.should = "some content" @resource.stubs(:[]).with(:path).returns "/boo" @resource.stubs(:stat).returns "eh" end it "should use the file's :write method to write the content" do @resource.expects(:write).with(:content) @content.sync end it "should return :file_changed if the file already existed" do @resource.expects(:stat).returns "something" @resource.stubs(:write) @content.sync.should == :file_changed end it "should return :file_created if the file did not exist" do @resource.expects(:stat).returns nil @resource.stubs(:write) @content.sync.should == :file_created end end describe "when writing" do before do @content = content.new(:resource => @resource) end it "should attempt to read from the filebucket if no actual content nor source exists" do @fh = File.open(@filename, 'w') @content.should = "{md5}foo" @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" @content.write(@fh) end describe "from actual content" do before(:each) do @content.stubs(:actual_content).returns("this is content") end it "should write to the given file handle" do @fh.expects(:print).with("this is content") @content.write(@fh) end it "should return the current checksum value" do @resource.parameter(:checksum).expects(:sum_stream).returns "checksum" @content.write(@fh).should == "checksum" end end describe "from a file bucket" do it "should fail if a file bucket cannot be retrieved" do @content.should = "{md5}foo" @content.resource.expects(:bucket).returns nil lambda { @content.write(@fh) }.should raise_error(Puppet::Error) end it "should fail if the file bucket cannot find any content" do @content.should = "{md5}foo" bucket = stub 'bucket' @content.resource.expects(:bucket).returns bucket bucket.expects(:getfile).with("foo").raises "foobar" lambda { @content.write(@fh) }.should raise_error(Puppet::Error) end it "should write the returned content to the file" do @content.should = "{md5}foo" bucket = stub 'bucket' @content.resource.expects(:bucket).returns bucket bucket.expects(:getfile).with("foo").returns "mycontent" @fh.expects(:print).with("mycontent") @content.write(@fh) end end describe "from local source" do before(:each) do @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false @sourcename = tmpfile('source') @source_content = "source file content"*10000 @sourcefile = File.open(@sourcename, 'w') {|f| f.write @source_content} @content = @resource.newattr(:content) @source = @resource.newattr(:source) @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end it "should copy content from the source to the file" do @resource.write(@source) File.read(@filename).should == @source_content end it "should return the checksum computed" do File.open(@filename, 'w') do |file| @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" end end end describe "from remote source" do before(:each) do @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false @response = stub_everything 'response', :code => "200" @source_content = "source file content"*10000 @response.stubs(:read_body).multiple_yields(*(["source file content"]*10000)) @conn = stub_everything 'connection' @conn.stubs(:request_get).yields(@response) Puppet::Network::HttpPool.stubs(:http_instance).returns @conn @content = @resource.newattr(:content) @sourcename = "puppet:///test/foo" @source = @resource.newattr(:source) @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end it "should write the contents to the file" do @resource.write(@source) File.read(@filename).should == @source_content end it "should not write anything if source is not found" do @response.stubs(:code).returns("404") lambda {@resource.write(@source)}.should raise_error(Net::HTTPError) { |e| e.message =~ /404/ } File.read(@filename).should == "initial file content" end it "should raise an HTTP error in case of server error" do @response.stubs(:code).returns("500") lambda { @content.write(@fh) }.should raise_error { |e| e.message.include? @source_content } end it "should return the checksum computed" do File.open(@filename, 'w') do |file| @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" end end end # These are testing the implementation rather than the desired behaviour; while that bites, there are a whole # pile of other methods in the File type that depend on intimate details of this implementation and vice-versa. # If these blow up, you are gonna have to review the callers to make sure they don't explode! --daniel 2011-02-01 describe "each_chunk_from should work" do before do @content = content.new(:resource => @resource) end it "when content is a string" do @content.each_chunk_from('i_am_a_string') { |chunk| chunk.should == 'i_am_a_string' } end # The following manifest is a case where source and content.should are both set # file { "/tmp/mydir" : # source => '/tmp/sourcedir', # recurse => true, # } it "when content checksum comes from source" do source_param = Puppet::Type.type(:file).attrclass(:source) source = source_param.new(:resource => @resource) @content.should = "{md5}123abcd" @content.expects(:chunk_file_from_source).returns('from_source') @content.each_chunk_from(source) { |chunk| chunk.should == 'from_source' } end it "when no content, source, but ensure present" do @resource[:ensure] = :present @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end # you might do this if you were just auditing it "when no content, source, but ensure file" do @resource[:ensure] = :file @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end it "when source_or_content is nil and content not a checksum" do @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end # the content is munged so that if it's a checksum nil gets passed in it "when content is a checksum it should try to read from filebucket" do @content.should = "{md5}123abcd" @content.expects(:read_file_from_filebucket).once.returns('im_a_filebucket') @content.each_chunk_from(nil) { |chunk| chunk.should == 'im_a_filebucket' } end it "when running as puppet apply" do @content.class.expects(:standalone?).returns true source_or_content = stubs('source_or_content') source_or_content.expects(:content).once.returns :whoo @content.each_chunk_from(source_or_content) { |chunk| chunk.should == :whoo } end it "when running from source with a local file" do source_or_content = stubs('source_or_content') source_or_content.expects(:local?).returns true @content.expects(:chunk_file_from_disk).with(source_or_content).once.yields 'woot' @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } end it "when running from source with a remote file" do source_or_content = stubs('source_or_content') source_or_content.expects(:local?).returns false @content.expects(:chunk_file_from_source).with(source_or_content).once.yields 'woot' @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } end end end end diff --git a/spec/unit/type/file/ctime.rb b/spec/unit/type/file/ctime.rb old mode 100644 new mode 100755 index 9fb892aed..1700d4928 --- a/spec/unit/type/file/ctime.rb +++ b/spec/unit/type/file/ctime.rb @@ -1,35 +1,34 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:ctime) do require 'puppet_spec/files' include PuppetSpec::Files before do @filename = tmpfile('ctime') @resource = Puppet::Type.type(:file).new({:name => @filename}) end it "should be able to audit the file's ctime" do File.open(@filename, "w"){ } @resource[:audit] = [:ctime] # this .to_resource audit behavior is magical :-( @resource.to_resource[:ctime].should == File.stat(@filename).ctime end it "should return absent if auditing an absent file" do @resource[:audit] = [:ctime] @resource.to_resource[:ctime].should == :absent end it "should prevent the user from trying to set the ctime" do lambda { @resource[:ctime] = Time.now.to_s }.should raise_error(Puppet::Error, /ctime is read-only/) end end diff --git a/spec/unit/type/file/ensure_spec.rb b/spec/unit/type/file/ensure_spec.rb index c5351309e..8555ef035 100755 --- a/spec/unit/type/file/ensure_spec.rb +++ b/spec/unit/type/file/ensure_spec.rb @@ -1,85 +1,84 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' property = Puppet::Type.type(:file).attrclass(:ensure) describe property do before do # Wow that's a messy interface to the resource. @resource = stub 'resource', :[] => nil, :[]= => nil, :property => nil, :newattr => nil, :parameter => nil, :replace? => true @resource.stubs(:[]).returns "foo" @resource.stubs(:[]).with(:path).returns "/my/file" @ensure = property.new :resource => @resource end it "should be a subclass of Ensure" do property.superclass.must == Puppet::Property::Ensure end describe "when retrieving the current state" do it "should return :absent if the file does not exist" do @ensure = property.new(:resource => @resource) @resource.expects(:stat).returns nil @ensure.retrieve.should == :absent end it "should return the current file type if the file exists" do @ensure = property.new(:resource => @resource) stat = mock 'stat', :ftype => "directory" @resource.expects(:stat).returns stat @ensure.retrieve.should == :directory end end describe "when testing whether :ensure is in sync" do before do @ensure = property.new(:resource => @resource) @stat = stub 'stat', :ftype => "file" end it "should always be in sync if replace is 'false' unless the file is missing" do @ensure.should = :file @resource.expects(:replace?).returns false @ensure.safe_insync?(:link).should be_true end it "should be in sync if :ensure is set to :absent and the file does not exist" do @ensure.should = :absent @ensure.must be_safe_insync(:absent) end it "should not be in sync if :ensure is set to :absent and the file exists" do @ensure.should = :absent @ensure.should_not be_safe_insync(:file) end it "should be in sync if a normal file exists and :ensure is set to :present" do @ensure.should = :present @ensure.must be_safe_insync(:file) end it "should be in sync if a directory exists and :ensure is set to :present" do @ensure.should = :present @ensure.must be_safe_insync(:directory) end it "should be in sync if a symlink exists and :ensure is set to :present" do @ensure.should = :present @ensure.must be_safe_insync(:link) end it "should not be in sync if :ensure is set to :file and a directory exists" do @ensure.should = :file @ensure.should_not be_safe_insync(:directory) end end end diff --git a/spec/unit/type/file/group_spec.rb b/spec/unit/type/file/group_spec.rb index 37a6872bd..707a37cd6 100755 --- a/spec/unit/type/file/group_spec.rb +++ b/spec/unit/type/file/group_spec.rb @@ -1,123 +1,122 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' property = Puppet::Type.type(:file).attrclass(:group) describe property do before do @resource = stub 'resource', :line => "foo", :file => "bar" @resource.stubs(:[]).returns "foo" @resource.stubs(:[]).with(:path).returns "/my/file" @group = property.new :resource => @resource end it "should have a method for testing whether a group is valid" do @group.must respond_to(:validgroup?) end it "should return the found gid if a group is valid" do @group.expects(:gid).with("foo").returns 500 @group.validgroup?("foo").should == 500 end it "should return false if a group is not valid" do @group.expects(:gid).with("foo").returns nil @group.validgroup?("foo").should be_false end describe "when retrieving the current value" do it "should return :absent if the file cannot stat" do @resource.expects(:stat).returns nil @group.retrieve.should == :absent end it "should get the gid from the stat instance from the file" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat stat.expects(:gid).returns 500 @group.retrieve.should == 500 end it "should warn and return :silly if the found value is higher than the maximum uid value" do Puppet.settings.expects(:value).with(:maximum_uid).returns 500 stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat stat.expects(:gid).returns 1000 @group.expects(:warning) @group.retrieve.should == :silly end end describe "when determining if the file is in sync" do it "should directly compare the group values if the desired group is an integer" do @group.should = [10] @group.must be_safe_insync(10) end it "should treat numeric strings as integers" do @group.should = ["10"] @group.must be_safe_insync(10) end it "should convert the group name to an integer if the desired group is a string" do @group.expects(:gid).with("foo").returns 10 @group.should = %w{foo} @group.must be_safe_insync(10) end it "should not validate that groups exist when a group is specified as an integer" do @group.expects(:gid).never @group.validgroup?(10) end it "should fail if it cannot convert a group name to an integer" do @group.expects(:gid).with("foo").returns nil @group.should = %w{foo} lambda { @group.safe_insync?(10) }.should raise_error(Puppet::Error) end it "should return false if the groups are not equal" do @group.should = [10] @group.should_not be_safe_insync(20) end end describe "when changing the group" do before do @group.should = %w{one} @group.stubs(:gid).returns 500 end it "should chown the file if :links is set to :follow" do @resource.expects(:[]).with(:links).returns :follow File.expects(:chown) @group.sync end it "should lchown the file if :links is set to :manage" do @resource.expects(:[]).with(:links).returns :manage File.expects(:lchown) @group.sync end it "should use the first valid group in its 'should' list" do @group.should = %w{one two three} @group.expects(:validgroup?).with("one").returns nil @group.expects(:validgroup?).with("two").returns 500 @group.expects(:validgroup?).with("three").never File.expects(:chown).with(nil, 500, "/my/file") @group.sync end end end diff --git a/spec/unit/type/file/mtime.rb b/spec/unit/type/file/mtime.rb old mode 100644 new mode 100755 index fa61bc343..3222df095 --- a/spec/unit/type/file/mtime.rb +++ b/spec/unit/type/file/mtime.rb @@ -1,35 +1,34 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:mtime) do require 'puppet_spec/files' include PuppetSpec::Files before do @filename = tmpfile('mtime') @resource = Puppet::Type.type(:file).new({:name => @filename}) end it "should be able to audit the file's mtime" do File.open(@filename, "w"){ } @resource[:audit] = [:mtime] # this .to_resource audit behavior is magical :-( @resource.to_resource[:mtime].should == File.stat(@filename).mtime end it "should return absent if auditing an absent file" do @resource[:audit] = [:mtime] @resource.to_resource[:mtime].should == :absent end it "should prevent the user from trying to set the mtime" do lambda { @resource[:mtime] = Time.now.to_s }.should raise_error(Puppet::Error, /mtime is read-only/) end end diff --git a/spec/unit/type/file/owner_spec.rb b/spec/unit/type/file/owner_spec.rb index 375faa237..ed3bef1fe 100755 --- a/spec/unit/type/file/owner_spec.rb +++ b/spec/unit/type/file/owner_spec.rb @@ -1,150 +1,149 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' property = Puppet::Type.type(:file).attrclass(:owner) describe property do before do # FIXME: many of these tests exercise the provider rather than `owner` # and should be moved into provider tests. ~JW @provider = Puppet::Type.type(:file).provider(:posix).new @provider.stubs(:uid).with("one").returns(1) @resource = stub 'resource', :line => "foo", :file => "bar" @resource.stubs(:[]).returns "foo" @resource.stubs(:[]).with(:path).returns "/my/file" @resource.stubs(:provider).returns @provider @owner = property.new :resource => @resource end it "should have a method for testing whether an owner is valid" do @provider.must respond_to(:validuser?) end it "should return the found uid if an owner is valid" do @provider.expects(:uid).with("foo").returns 500 @provider.validuser?("foo").should == 500 end it "should return false if an owner is not valid" do @provider.expects(:uid).with("foo").returns nil @provider.validuser?("foo").should be_false end describe "when retrieving the current value" do it "should return :absent if the file cannot stat" do @resource.expects(:stat).returns nil @owner.retrieve.should == :absent end it "should get the uid from the stat instance from the file" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat stat.expects(:uid).returns 500 @owner.retrieve.should == 500 end it "should warn and return :silly if the found value is higher than the maximum uid value" do Puppet.settings.expects(:value).with(:maximum_uid).returns 500 stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat stat.expects(:uid).returns 1000 @provider.expects(:warning) @owner.retrieve.should == :silly end end describe "when determining if the file is in sync" do describe "and not running as root" do it "should warn once and return true" do Puppet.features.expects(:root?).returns false @provider.expects(:warnonce) @owner.should = [10] @owner.must be_safe_insync(20) end end before do Puppet.features.stubs(:root?).returns true end it "should be in sync if 'should' is not provided" do @owner.must be_safe_insync(10) end it "should directly compare the owner values if the desired owner is an integer" do @owner.should = [10] @owner.must be_safe_insync(10) end it "should treat numeric strings as integers" do @owner.should = ["10"] @owner.must be_safe_insync(10) end it "should convert the owner name to an integer if the desired owner is a string" do @provider.expects(:uid).with("foo").returns 10 @owner.should = %w{foo} @owner.must be_safe_insync(10) end it "should not validate that users exist when a user is specified as an integer" do @provider.expects(:uid).never @provider.validuser?(10) end it "should fail if it cannot convert an owner name to an integer" do @provider.expects(:uid).with("foo").returns nil @owner.should = %w{foo} lambda { @owner.safe_insync?(10) }.should raise_error(Puppet::Error) end it "should return false if the owners are not equal" do @owner.should = [10] @owner.should_not be_safe_insync(20) end end describe "when changing the owner" do before do @owner.should = %w{one} @owner.stubs(:path).returns "path" @owner.stubs(:uid).returns 500 end it "should chown the file if :links is set to :follow" do @resource.expects(:[]).with(:links).returns :follow File.expects(:chown) @owner.sync end it "should lchown the file if :links is set to :manage" do @resource.expects(:[]).with(:links).returns :manage File.expects(:lchown) @owner.sync end it "should use the first valid owner in its 'should' list" do @owner.should = %w{one two three} @provider.expects(:validuser?).with("one").returns nil @provider.expects(:validuser?).with("two").returns 500 @provider.expects(:validuser?).with("three").never File.expects(:chown).with(500, nil, "/my/file") @owner.sync end end end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb old mode 100644 new mode 100755 index 45e8b3b14..2622948d0 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -1,88 +1,87 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' [:seluser, :selrole, :seltype, :selrange].each do |param| property = Puppet::Type.type(:file).attrclass(param) describe property do before do @resource = Puppet::Type.type(:file).new :path => "/my/file" @sel = property.new :resource => @resource end it "retrieve on #{param} should return :absent if the file isn't statable" do @resource.expects(:stat).returns nil @sel.retrieve.should == :absent end it "should retrieve nil for #{param} if there is no SELinux support" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat @sel.expects(:get_selinux_current_context).with("/my/file").returns nil @sel.retrieve.should be_nil end it "should retrieve #{param} if a SELinux context is found with a range" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t:s0" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end @sel.retrieve.should == expectedresult end it "should retrieve #{param} if a SELinux context is found without a range" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; nil end @sel.retrieve.should == expectedresult end it "should handle no default gracefully" do @sel.expects(:get_selinux_default_context).with("/my/file").returns nil @sel.default.must be_nil end it "should be able to detect matchpathcon defaults" do @sel.stubs(:debug) @sel.expects(:get_selinux_default_context).with("/my/file").returns "user_u:role_r:type_t:s0" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end @sel.default.must == expectedresult end it "should return nil for defaults if selinux_ignore_defaults is true" do @resource[:selinux_ignore_defaults] = :true @sel.default.must be_nil end it "should be able to set a new context" do stat = stub 'stat', :ftype => "foo" @sel.should = %w{newone} @sel.expects(:set_selinux_context).with("/my/file", ["newone"], param) @sel.sync end it "should do nothing for safe_insync? if no SELinux support" do @sel.should = %{newcontext} @sel.expects(:selinux_support?).returns false @sel.safe_insync?("oldcontext").should == true end end end diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb index bb83e7531..5665d323d 100755 --- a/spec/unit/type/file/source_spec.rb +++ b/spec/unit/type/file/source_spec.rb @@ -1,272 +1,272 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' source = Puppet::Type.type(:file).attrclass(:source) describe Puppet::Type.type(:file).attrclass(:source) do before do # Wow that's a messy interface to the resource. @resource = stub 'resource', :[]= => nil, :property => nil, :catalog => stub("catalog", :dependent_data_expired? => false), :line => 0, :file => '' end it "should be a subclass of Parameter" do source.superclass.must == Puppet::Parameter end describe "when initializing" do it "should fail if the set values are not URLs" do s = source.new(:resource => @resource) URI.expects(:parse).with('foo').raises RuntimeError lambda { s.value = %w{foo} }.must raise_error(Puppet::Error) end it "should fail if the URI is not a local file, file URI, or puppet URI" do s = source.new(:resource => @resource) lambda { s.value = %w{http://foo/bar} }.must raise_error(Puppet::Error) end end it "should have a method for retrieving its metadata" do source.new(:resource => @resource).must respond_to(:metadata) end it "should have a method for setting its metadata" do source.new(:resource => @resource).must respond_to(:metadata=) end describe "when returning the metadata" do before do @metadata = stub 'metadata', :source= => nil end it "should return already-available metadata" do @source = source.new(:resource => @resource) @source.metadata = "foo" @source.metadata.should == "foo" end it "should return nil if no @should value is set and no metadata is available" do @source = source.new(:resource => @resource) @source.metadata.should be_nil end it "should collect its metadata using the Metadata class if it is not already set" do @source = source.new(:resource => @resource, :value => "/foo/bar") Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns @metadata @source.metadata end it "should use the metadata from the first found source" do metadata = stub 'metadata', :source= => nil @source = source.new(:resource => @resource, :value => ["/foo/bar", "/fee/booz"]) Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns nil Puppet::FileServing::Metadata.indirection.expects(:find).with("/fee/booz").returns metadata @source.metadata.should equal(metadata) end it "should store the found source as the metadata's source" do metadata = mock 'metadata' @source = source.new(:resource => @resource, :value => "/foo/bar") Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns metadata metadata.expects(:source=).with("/foo/bar") @source.metadata end it "should fail intelligently if an exception is encountered while querying for metadata" do @source = source.new(:resource => @resource, :value => "/foo/bar") Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").raises RuntimeError @source.expects(:fail).raises ArgumentError lambda { @source.metadata }.should raise_error(ArgumentError) end it "should fail if no specified sources can be found" do @source = source.new(:resource => @resource, :value => "/foo/bar") Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns nil @source.expects(:fail).raises RuntimeError lambda { @source.metadata }.should raise_error(RuntimeError) end it "should expire the metadata appropriately" do expirer = stub 'expired', :dependent_data_expired? => true metadata = stub 'metadata', :source= => nil Puppet::FileServing::Metadata.indirection.expects(:find).with("/fee/booz").returns metadata @source = source.new(:resource => @resource, :value => ["/fee/booz"]) @source.metadata = "foo" @source.stubs(:expirer).returns expirer @source.metadata.should_not == "foo" end end it "should have a method for setting the desired values on the resource" do source.new(:resource => @resource).must respond_to(:copy_source_values) end describe "when copying the source values" do before do @resource = Puppet::Type.type(:file).new :path => "/foo/bar" @source = source.new(:resource => @resource) @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file" @source.stubs(:metadata).returns @metadata end it "should fail if there is no metadata" do @source.stubs(:metadata).returns nil @source.expects(:devfail).raises ArgumentError lambda { @source.copy_source_values }.should raise_error(ArgumentError) end it "should set :ensure to the file type" do @metadata.stubs(:ftype).returns "file" @source.copy_source_values @resource[:ensure].must == :file end it "should not set 'ensure' if it is already set to 'absent'" do @metadata.stubs(:ftype).returns "file" @resource[:ensure] = :absent @source.copy_source_values @resource[:ensure].must == :absent end describe "and the source is a file" do before do @metadata.stubs(:ftype).returns "file" end it "should copy the metadata's owner, group, checksum, and mode to the resource if they are not set on the resource" do Puppet.features.expects(:root?).returns true @source.copy_source_values @resource[:owner].must == 100 @resource[:group].must == 200 @resource[:mode].must == "173" # Metadata calls it checksum, we call it content. @resource[:content].must == @metadata.checksum end it "should not copy the metadata's owner to the resource if it is already set" do @resource[:owner] = 1 @resource[:group] = 2 @resource[:mode] = 3 @resource[:content] = "foobar" @source.copy_source_values @resource[:owner].must == 1 @resource[:group].must == 2 @resource[:mode].must == "3" @resource[:content].should_not == @metadata.checksum end describe "and puppet is not running as root" do it "should not try to set the owner" do Puppet.features.expects(:root?).returns false @source.copy_source_values @resource[:owner].should be_nil end end end describe "and the source is a link" do it "should set the target to the link destination" do @metadata.stubs(:ftype).returns "link" + @metadata.stubs(:links).returns "manage" @resource.stubs(:[]) @resource.stubs(:[]=) @metadata.expects(:destination).returns "/path/to/symlink" @resource.expects(:[]=).with(:target, "/path/to/symlink") @source.copy_source_values end end end it "should have a local? method" do source.new(:resource => @resource).must be_respond_to(:local?) end context "when accessing source properties" do before(:each) do @source = source.new(:resource => @resource) @metadata = stub_everything @source.stubs(:metadata).returns(@metadata) end describe "for local sources" do before(:each) do @metadata.stubs(:ftype).returns "file" @metadata.stubs(:source).returns("file:///path/to/source") end it "should be local" do @source.must be_local end it "should be local if there is no scheme" do @metadata.stubs(:source).returns("/path/to/source") @source.must be_local end it "should be able to return the metadata source full path" do @source.full_path.should == "/path/to/source" end end describe "for remote sources" do before(:each) do @metadata.stubs(:ftype).returns "file" @metadata.stubs(:source).returns("puppet://server:8192/path/to/source") end it "should not be local" do @source.should_not be_local end it "should be able to return the metadata source full path" do @source.full_path.should == "/path/to/source" end it "should be able to return the source server" do @source.server.should == "server" end it "should be able to return the source port" do @source.port.should == 8192 end describe "which don't specify server or port" do before(:each) do @metadata.stubs(:source).returns("puppet:///path/to/source") end it "should return the default source server" do Puppet.settings.expects(:[]).with(:server).returns("myserver") @source.server.should == "myserver" end it "should return the default source port" do Puppet.settings.expects(:[]).with(:masterport).returns(1234) @source.port.should == 1234 end end end end end diff --git a/spec/unit/type/file/type.rb b/spec/unit/type/file/type.rb old mode 100644 new mode 100755 index 0d38b64cd..7d4af0e16 --- a/spec/unit/type/file/type.rb +++ b/spec/unit/type/file/type.rb @@ -1,20 +1,19 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:type) do require 'puppet_spec/files' include PuppetSpec::Files before do @filename = tmpfile('type') @resource = Puppet::Type.type(:file).new({:name => @filename}) end it "should prevent the user from trying to set the type" do lambda { @resource[:type] = "fifo" }.should raise_error(Puppet::Error, /type is read-only/) end end diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index b15d41d4b..683c3654b 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -1,1185 +1,1184 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:file) do before do Puppet.settings.stubs(:use) @real_posix = Puppet.features.posix? Puppet.features.stubs("posix?").returns(true) @path = Tempfile.new("puppetspec") pathname = @path.path @path.close!() @path = pathname @file = Puppet::Type::File.new(:name => @path) @catalog = Puppet::Resource::Catalog.new @file.catalog = @catalog end describe "when determining if recursion is enabled" do it "should default to recursion being disabled" do @file.should_not be_recurse end [true, "true", 10, "inf", "remote"].each do |value| it "should consider #{value} to enable recursion" do @file[:recurse] = value @file.must be_recurse end end [false, "false", 0].each do |value| it "should consider #{value} to disable recursion" do @file[:recurse] = value @file.should_not be_recurse end end end describe "#write" do it "should propagate failures encountered when renaming the temporary file" do File.stubs(:open) File.expects(:rename).raises ArgumentError file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") file.stubs(:validate_checksum?).returns(false) property = stub('content_property', :actual_content => "something", :length => "something".length) file.stubs(:property).with(:content).returns(property) lambda { file.write(:content) }.should raise_error(Puppet::Error) end it "should delegate writing to the content property" do filehandle = stub_everything 'fh' File.stubs(:open).yields(filehandle) File.stubs(:rename) property = stub('content_property', :actual_content => "something", :length => "something".length) file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") file.stubs(:validate_checksum?).returns(false) file.stubs(:property).with(:content).returns(property) property.expects(:write).with(filehandle) file.write(:content) end describe "when validating the checksum" do before { @file.stubs(:validate_checksum?).returns(true) } it "should fail if the checksum parameter and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b', :sum_file => 'checksum_b') @file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') @file.stubs(:property).with(:content).returns(property) lambda { @file.write :NOTUSED }.should raise_error(Puppet::Error) end end describe "when not validating the checksum" do before { @file.stubs(:validate_checksum?).returns(false) } it "should not fail if the checksum property and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b') @file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') @file.stubs(:property).with(:content).returns(property) lambda { @file.write :NOTUSED }.should_not raise_error(Puppet::Error) end end end it "should have a method for determining if the file is present" do @file.must respond_to(:exist?) end it "should be considered existent if it can be stat'ed" do @file.expects(:stat).returns mock('stat') @file.must be_exist end it "should be considered nonexistent if it can not be stat'ed" do @file.expects(:stat).returns nil @file.must_not be_exist end it "should have a method for determining if the file should be a normal file" do @file.must respond_to(:should_be_file?) end it "should be a file if :ensure is set to :file" do @file[:ensure] = :file @file.must be_should_be_file end it "should be a file if :ensure is set to :present and the file exists as a normal file" do @file.stubs(:stat).returns(mock('stat', :ftype => "file")) @file[:ensure] = :present @file.must be_should_be_file end it "should not be a file if :ensure is set to something other than :file" do @file[:ensure] = :directory @file.must_not be_should_be_file end it "should not be a file if :ensure is set to :present and the file exists but is not a normal file" do @file.stubs(:stat).returns(mock('stat', :ftype => "directory")) @file[:ensure] = :present @file.must_not be_should_be_file end it "should be a file if :ensure is not set and :content is" do @file[:content] = "foo" @file.must be_should_be_file end it "should be a file if neither :ensure nor :content is set but the file exists as a normal file" do @file.stubs(:stat).returns(mock("stat", :ftype => "file")) @file.must be_should_be_file end it "should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file" do @file.stubs(:stat).returns(mock("stat", :ftype => "directory")) @file.must_not be_should_be_file end describe "when using POSIX filenames" do describe "on POSIX systems" do before do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should autorequire its parent directory" do file = Puppet::Type::File.new(:path => "/foo/bar") dir = Puppet::Type::File.new(:path => "/foo") @catalog.add_resource file @catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire its parent dir if its parent dir is itself" do file = Puppet::Type::File.new(:path => "/") @catalog.add_resource file file.autorequire.should be_empty end it "should remove trailing slashes" do file = Puppet::Type::File.new(:path => "/foo/bar/baz/") file[:path].should == "/foo/bar/baz" end it "should remove double slashes" do file = Puppet::Type::File.new(:path => "/foo/bar//baz") file[:path].should == "/foo/bar/baz" end it "should remove trailing double slashes" do file = Puppet::Type::File.new(:path => "/foo/bar/baz//") file[:path].should == "/foo/bar/baz" end it "should leave a single slash alone" do file = Puppet::Type::File.new(:path => "/") file[:path].should == "/" end it "should accept a double-slash at the start of the path" do expect { file = Puppet::Type::File.new(:path => "//tmp/xxx") # REVISIT: This should be wrong, later. See the next test. # --daniel 2011-01-31 file[:path].should == '/tmp/xxx' }.should_not raise_error end # REVISIT: This is pending, because I don't want to try and audit the # entire codebase to make sure we get this right. POSIX treats two (and # exactly two) '/' characters at the start of the path specially. # # See sections 3.2 and 4.11, which allow DomainOS to be all special like # and still have the POSIX branding and all. --daniel 2011-01-31 it "should preserve the double-slash at the start of the path" end describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should refuse to work" do lambda { Puppet::Type::File.new(:path => "/foo/bar") }.should raise_error(Puppet::Error) end end end describe "when using Microsoft Windows filenames", :if => Puppet.features.microsoft_windows? do describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should autorequire its parent directory" do file = Puppet::Type::File.new(:path => "X:/foo/bar") dir = Puppet::Type::File.new(:path => "X:/foo") @catalog.add_resource file @catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire its parent dir if its parent dir is itself" do file = Puppet::Type::File.new(:path => "X:/") @catalog.add_resource file file.autorequire.should be_empty end it "should remove trailing slashes" do file = Puppet::Type::File.new(:path => "X:/foo/bar/baz/") file[:path].should == "X:/foo/bar/baz" end it "should remove double slashes" do file = Puppet::Type::File.new(:path => "X:/foo/bar//baz") file[:path].should == "X:/foo/bar/baz" end it "should remove trailing double slashes" do file = Puppet::Type::File.new(:path => "X:/foo/bar/baz//") file[:path].should == "X:/foo/bar/baz" end it "should leave a drive letter with a slash alone" do file = Puppet::Type::File.new(:path => "X:/") file[:path].should == "X:/" end it "should add a slash to a drive letter" do file = Puppet::Type::File.new(:path => "X:") file[:path].should == "X:/" end end describe "on POSIX systems" do before do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should refuse to work" do lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) end end end describe "when using UNC filenames" do describe "on Microsoft Windows systems", :if => Puppet.features.microsoft_windows? do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should autorequire its parent directory" do file = Puppet::Type::File.new(:path => "//server/foo/bar") dir = Puppet::Type::File.new(:path => "//server/foo") @catalog.add_resource file @catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire its parent dir if its parent dir is itself" do file = Puppet::Type::File.new(:path => "//server/foo") @catalog.add_resource file puts file.autorequire file.autorequire.should be_empty end it "should remove trailing slashes" do file = Puppet::Type::File.new(:path => "//server/foo/bar/baz/") file[:path].should == "//server/foo/bar/baz" end it "should remove double slashes" do file = Puppet::Type::File.new(:path => "//server/foo/bar//baz") file[:path].should == "//server/foo/bar/baz" end it "should remove trailing double slashes" do file = Puppet::Type::File.new(:path => "//server/foo/bar/baz//") file[:path].should == "//server/foo/bar/baz" end it "should remove a trailing slash from a sharename" do file = Puppet::Type::File.new(:path => "//server/foo/") file[:path].should == "//server/foo" end it "should not modify a sharename" do file = Puppet::Type::File.new(:path => "//server/foo") file[:path].should == "//server/foo" end end describe "on POSIX systems" do before do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should refuse to work" do lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) end end end describe "when initializing" do it "should set a desired 'ensure' value if none is set and 'content' is set" do file = Puppet::Type::File.new(:name => "/my/file", :content => "/foo/bar") file[:ensure].should == :file end it "should set a desired 'ensure' value if none is set and 'target' is set" do file = Puppet::Type::File.new(:name => "/my/file", :target => "/foo/bar") file[:ensure].should == :symlink end end describe "when validating attributes" do %w{path checksum backup recurse recurselimit source replace force ignore links purge sourceselect}.each do |attr| it "should have a '#{attr}' parameter" do Puppet::Type.type(:file).attrtype(attr.intern).should == :param end end %w{content target ensure owner group mode type}.each do |attr| it "should have a '#{attr}' property" do Puppet::Type.type(:file).attrtype(attr.intern).should == :property end end it "should have its 'path' attribute set as its namevar" do Puppet::Type.type(:file).key_attributes.should == [:path] end end describe "when managing links" do require 'tempfile' if @real_posix describe "on POSIX systems" do before do @basedir = tempfile Dir.mkdir(@basedir) @file = File.join(@basedir, "file") @link = File.join(@basedir, "link") File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } File.symlink(@file, @link) @resource = Puppet::Type.type(:file).new(:path => @link, :mode => "755") @catalog.add_resource @resource end after do remove_tmp_files end it "should default to managing the link" do @catalog.apply # I convert them to strings so they display correctly if there's an error. ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 end it "should be able to follow links" do @resource[:links] = :follow @catalog.apply ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 end end else # @real_posix # should recode tests using expectations instead of using the filesystem end describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should refuse to work with links" end end it "should be able to retrieve a stat instance for the file it is managing" do Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo").should respond_to(:stat) end describe "when stat'ing its file" do before do @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") @resource[:links] = :manage # so we always use :lstat end it "should use :stat if it is following links" do @resource[:links] = :follow File.expects(:stat) @resource.stat end it "should use :lstat if is it not following links" do @resource[:links] = :manage File.expects(:lstat) @resource.stat end it "should stat the path of the file" do File.expects(:lstat).with("/foo/bar") @resource.stat end # This only happens in testing. it "should return nil if the stat does not exist" do File.expects(:lstat).returns nil @resource.stat.should be_nil end it "should return nil if the file does not exist" do File.expects(:lstat).raises(Errno::ENOENT) @resource.stat.should be_nil end it "should return nil if the file cannot be stat'ed" do File.expects(:lstat).raises(Errno::EACCES) @resource.stat.should be_nil end it "should return the stat instance" do File.expects(:lstat).returns "mystat" @resource.stat.should == "mystat" end it "should cache the stat instance if it has a catalog and is applying" do stat = mock 'stat' File.expects(:lstat).returns stat catalog = Puppet::Resource::Catalog.new @resource.catalog = catalog catalog.stubs(:applying?).returns true @resource.stat.should equal(@resource.stat) end end describe "when flushing" do it "should flush all properties that respond to :flush" do @resource = Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo") @resource.parameter(:source).expects(:flush) @resource.flush end it "should reset its stat reference" do @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") File.expects(:lstat).times(2).returns("stat1").then.returns("stat2") @resource.stat.should == "stat1" @resource.flush @resource.stat.should == "stat2" end end it "should have a method for performing recursion" do @file.must respond_to(:perform_recursion) end describe "when executing a recursive search" do it "should use Metadata to do its recursion" do Puppet::FileServing::Metadata.indirection.expects(:search) @file.perform_recursion(@file[:path]) end it "should use the provided path as the key to the search" do Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| key == "/foo" } @file.perform_recursion("/foo") end it "should return the results of the metadata search" do Puppet::FileServing::Metadata.indirection.expects(:search).returns "foobar" @file.perform_recursion(@file[:path]).should == "foobar" end it "should pass its recursion value to the search" do @file[:recurse] = true Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } @file.perform_recursion(@file[:path]) end it "should pass true if recursion is remote" do @file[:recurse] = :remote Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } @file.perform_recursion(@file[:path]) end it "should pass its recursion limit value to the search" do @file[:recurselimit] = 10 Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurselimit] == 10 } @file.perform_recursion(@file[:path]) end it "should configure the search to ignore or manage links" do @file[:links] = :manage Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:links] == :manage } @file.perform_recursion(@file[:path]) end it "should pass its 'ignore' setting to the search if it has one" do @file[:ignore] = %w{.svn CVS} Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} } @file.perform_recursion(@file[:path]) end end it "should have a method for performing local recursion" do @file.must respond_to(:recurse_local) end describe "when doing local recursion" do before do @metadata = stub 'metadata', :relative_path => "my/file" end it "should pass its path to the :perform_recursion method" do @file.expects(:perform_recursion).with(@file[:path]).returns [@metadata] @file.stubs(:newchild) @file.recurse_local end it "should return an empty hash if the recursion returns nothing" do @file.expects(:perform_recursion).returns nil @file.recurse_local.should == {} end it "should create a new child resource with each generated metadata instance's relative path" do @file.expects(:perform_recursion).returns [@metadata] @file.expects(:newchild).with(@metadata.relative_path).returns "fiebar" @file.recurse_local end it "should not create a new child resource for the '.' directory" do @metadata.stubs(:relative_path).returns "." @file.expects(:perform_recursion).returns [@metadata] @file.expects(:newchild).never @file.recurse_local end it "should return a hash of the created resources with the relative paths as the hash keys" do @file.expects(:perform_recursion).returns [@metadata] @file.expects(:newchild).with("my/file").returns "fiebar" @file.recurse_local.should == {"my/file" => "fiebar"} end it "should set checksum_type to none if this file checksum is none" do @file[:checksum] = :none Puppet::FileServing::Metadata.indirection.expects(:search).with { |path,params| params[:checksum_type] == :none }.returns [@metadata] @file.expects(:newchild).with("my/file").returns "fiebar" @file.recurse_local end end it "should have a method for performing link recursion" do @file.must respond_to(:recurse_link) end describe "when doing link recursion" do before do @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory" @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file" @resource = stub 'file', :[]= => nil end it "should pass its target to the :perform_recursion method" do @file[:target] = "mylinks" @file.expects(:perform_recursion).with("mylinks").returns [@first] @file.stubs(:newchild).returns @resource @file.recurse_link({}) end it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do @first.stubs(:relative_path).returns "." @file[:target] = "mylinks" @file.expects(:perform_recursion).with("mylinks").returns [@first] @file.stubs(:newchild).never @file.expects(:[]=).with(:ensure, :directory) @file.recurse_link({}) end it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do @file.expects(:perform_recursion).returns [@first, @second] @file.expects(:newchild).with(@first.relative_path).returns @resource @file.recurse_link("second" => @resource) end it "should not create a new child resource for paths that already exist in the children hash" do @file.expects(:perform_recursion).returns [@first] @file.expects(:newchild).never @file.recurse_link("first" => @resource) end it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory" do file = stub 'file' file.expects(:[]=).with(:target, "/my/second") file.expects(:[]=).with(:ensure, :link) @file.stubs(:perform_recursion).returns [@first, @second] @file.recurse_link("first" => @resource, "second" => file) end it "should :ensure to :directory if the file is a directory" do file = stub 'file' file.expects(:[]=).with(:ensure, :directory) @file.stubs(:perform_recursion).returns [@first, @second] @file.recurse_link("first" => file, "second" => @resource) end it "should return a hash with both created and existing resources with the relative paths as the hash keys" do file = stub 'file', :[]= => nil @file.expects(:perform_recursion).returns [@first, @second] @file.stubs(:newchild).returns file @file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file} end end it "should have a method for performing remote recursion" do @file.must respond_to(:recurse_remote) end describe "when doing remote recursion" do before do @file[:source] = "puppet://foo/bar" @first = Puppet::FileServing::Metadata.new("/my", :relative_path => "first") @second = Puppet::FileServing::Metadata.new("/my", :relative_path => "second") @first.stubs(:ftype).returns "directory" @second.stubs(:ftype).returns "directory" @parameter = stub 'property', :metadata= => nil @resource = stub 'file', :[]= => nil, :parameter => @parameter end it "should pass its source to the :perform_recursion method" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] @file.stubs(:newchild).returns @resource @file.recurse_remote({}) end it "should not recurse when the remote file is not a directory" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => ".") data.stubs(:ftype).returns "file" @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] @file.expects(:newchild).never @file.recurse_remote({}) end it "should set the source of each returned file to the searched-for URI plus the found relative path" do @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path) @file.expects(:perform_recursion).returns [@first] @file.stubs(:newchild).returns @resource @file.recurse_remote({}) end it "should create a new resource for any relative file paths that do not already have a resource" do @file.stubs(:perform_recursion).returns [@first] @file.expects(:newchild).with("first").returns @resource @file.recurse_remote({}).should == {"first" => @resource} end it "should not create a new resource for any relative file paths that do already have a resource" do @file.stubs(:perform_recursion).returns [@first] @file.expects(:newchild).never @file.recurse_remote("first" => @resource) end it "should set the source of each resource to the source of the metadata" do @file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path)) @file.recurse_remote("first" => @resource) end # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already # filed, and when it's fixed, we'll just fix the whole flow. it "should set the checksum type to :md5 if the remote file is a file" do @first.stubs(:ftype).returns "file" @file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:checksum, :md5) @file.recurse_remote("first" => @resource) end it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do @file.stubs(:perform_recursion).returns [@first] @resource.expects(:parameter).with(:source).returns @parameter @parameter.expects(:metadata=).with(@first) @file.recurse_remote("first" => @resource) end it "should not create a new resource for the '.' file" do @first.stubs(:relative_path).returns "." @file.stubs(:perform_recursion).returns [@first] @file.expects(:newchild).never @file.recurse_remote({}) end it "should store the metadata in the main file's source property if the relative path is '.'" do @first.stubs(:relative_path).returns "." @file.stubs(:perform_recursion).returns [@first] @file.parameter(:source).expects(:metadata=).with @first @file.recurse_remote("first" => @resource) end describe "and multiple sources are provided" do describe "and :sourceselect is set to :first" do it "should create file instances for the results for the first source to return any values" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") @file[:source] = %w{/one /two /three /four} @file.expects(:perform_recursion).with("/one").returns nil @file.expects(:perform_recursion).with("/two").returns [] @file.expects(:perform_recursion).with("/three").returns [data] @file.expects(:perform_recursion).with("/four").never @file.expects(:newchild).with("foobar").returns @resource @file.recurse_remote({}) end end describe "and :sourceselect is set to :all" do before do @file[:sourceselect] = :all end it "should return every found file that is not in a previous source" do klass = Puppet::FileServing::Metadata @file[:source] = %w{/one /two /three /four} @file.stubs(:newchild).returns @resource one = [klass.new("/one", :relative_path => "a")] @file.expects(:perform_recursion).with("/one").returns one @file.expects(:newchild).with("a").returns @resource two = [klass.new("/two", :relative_path => "a"), klass.new("/two", :relative_path => "b")] @file.expects(:perform_recursion).with("/two").returns two @file.expects(:newchild).with("b").returns @resource three = [klass.new("/three", :relative_path => "a"), klass.new("/three", :relative_path => "c")] @file.expects(:perform_recursion).with("/three").returns three @file.expects(:newchild).with("c").returns @resource @file.expects(:perform_recursion).with("/four").returns [] @file.recurse_remote({}) end end end end describe "when specifying both source, and content properties" do before do @file[:source] = '/one' @file[:content] = 'file contents' end it "should raise an exception" do lambda {@file.validate }.should raise_error(/You cannot specify more than one of/) end end describe "when using source" do before do @file[:source] = '/one' end Puppet::Type::File::ParameterChecksum.value_collection.values.reject {|v| v == :none}.each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do @file[:checksum] = checksum_type end it 'should validate' do lambda { @file.validate }.should_not raise_error end end end describe "with checksum 'none'" do before do @file[:checksum] = :none end it 'should raise an exception when validating' do lambda { @file.validate }.should raise_error(/You cannot specify source when using checksum 'none'/) end end end describe "when using content" do before do @file[:content] = 'file contents' end (Puppet::Type::File::ParameterChecksum.value_collection.values - SOURCE_ONLY_CHECKSUMS).each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do @file[:checksum] = checksum_type end it 'should validate' do lambda { @file.validate }.should_not raise_error end end end SOURCE_ONLY_CHECKSUMS.each do |checksum_type| describe "with checksum '#{checksum_type}'" do it 'should raise an exception when validating' do @file[:checksum] = checksum_type lambda { @file.validate }.should raise_error(/You cannot specify content when using checksum '#{checksum_type}'/) end end end end describe "when returning resources with :eval_generate" do before do @graph = stub 'graph', :add_edge => nil @catalog.stubs(:relationship_graph).returns @graph @file.catalog = @catalog @file[:recurse] = true end it "should recurse if recursion is enabled" do resource = stub('resource', :[] => "resource") @file.expects(:recurse?).returns true @file.expects(:recurse).returns [resource] @file.eval_generate.should == [resource] end it "should not recurse if recursion is disabled" do @file.expects(:recurse?).returns false @file.expects(:recurse).never @file.eval_generate.should == [] end it "should return each resource found through recursion" do foo = stub 'foo', :[] => "/foo" bar = stub 'bar', :[] => "/bar" bar2 = stub 'bar2', :[] => "/bar" @file.expects(:recurse).returns [foo, bar] @file.eval_generate.should == [foo, bar] end end describe "when recursing" do before do @file[:recurse] = true @metadata = Puppet::FileServing::Metadata end describe "and a source is set" do before { @file[:source] = "/my/source" } it "should pass the already-discovered resources to recurse_remote" do @file.stubs(:recurse_local).returns(:foo => "bar") @file.expects(:recurse_remote).with(:foo => "bar").returns [] @file.recurse end end describe "and a target is set" do before { @file[:target] = "/link/target" } it "should use recurse_link" do @file.stubs(:recurse_local).returns(:foo => "bar") @file.expects(:recurse_link).with(:foo => "bar").returns [] @file.recurse end end it "should use recurse_local if recurse is not remote" do @file.expects(:recurse_local).returns({}) @file.recurse end it "should not use recurse_local if recurse remote" do @file[:recurse] = :remote @file.expects(:recurse_local).never @file.recurse end it "should return the generated resources as an array sorted by file path" do one = stub 'one', :[] => "/one" two = stub 'two', :[] => "/one/two" three = stub 'three', :[] => "/three" @file.expects(:recurse_local).returns(:one => one, :two => two, :three => three) @file.recurse.should == [one, two, three] end describe "and purging is enabled" do before do @file[:purge] = true end it "should configure each file to be removed" do local = stub 'local' local.stubs(:[]).with(:source).returns nil # Thus, a local file local.stubs(:[]).with(:path).returns "foo" @file.expects(:recurse_local).returns("local" => local) local.expects(:[]=).with(:ensure, :absent) @file.recurse end it "should not remove files that exist in the remote repository" do @file["source"] = "/my/file" @file.expects(:recurse_local).returns({}) remote = stub 'remote' remote.stubs(:[]).with(:source).returns "/whatever" # Thus, a remote file remote.stubs(:[]).with(:path).returns "foo" @file.expects(:recurse_remote).with { |hash| hash["remote"] = remote } remote.expects(:[]=).with(:ensure, :absent).never @file.recurse end end describe "and making a new child resource" do it "should not copy the parent resource's parent" do Puppet::Type.type(:file).expects(:new).with { |options| ! options.include?(:parent) } @file.newchild("my/path") end {:recurse => true, :target => "/foo/bar", :ensure => :present, :alias => "yay", :source => "/foo/bar"}.each do |param, value| it "should not pass on #{param} to the sub resource" do @file = Puppet::Type::File.new(:name => @path, param => value, :catalog => @catalog) @file.class.expects(:new).with { |params| params[param].nil? } @file.newchild("sub/file") end end it "should copy all of the parent resource's 'should' values that were set at initialization" do file = @file.class.new(:path => "/foo/bar", :owner => "root", :group => "wheel") @catalog.add_resource(file) file.class.expects(:new).with { |options| options[:owner] == "root" and options[:group] == "wheel" } file.newchild("my/path") end it "should not copy default values to the new child" do @file.class.expects(:new).with { |params| params[:backup].nil? } @file.newchild("my/path") end it "should not copy values to the child which were set by the source" do @file[:source] = "/foo/bar" metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever" @file.parameter(:source).stubs(:metadata).returns metadata @file.parameter(:source).copy_source_values @file.class.expects(:new).with { |params| params[:group].nil? } @file.newchild("my/path") end end end describe "when setting the backup" do it "should default to 'puppet'" do Puppet::Type::File.new(:name => "/my/file")[:backup].should == "puppet" end it "should allow setting backup to 'false'" do (!Puppet::Type::File.new(:name => "/my/file", :backup => false)[:backup]).should be_true end it "should set the backup to '.puppet-bak' if it is set to true" do Puppet::Type::File.new(:name => "/my/file", :backup => true)[:backup].should == ".puppet-bak" end it "should support any other backup extension" do Puppet::Type::File.new(:name => "/my/file", :backup => ".bak")[:backup].should == ".bak" end it "should set the filebucket when backup is set to a string matching the name of a filebucket in the catalog" do catalog = Puppet::Resource::Catalog.new bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file = Puppet::Type::File.new(:name => "/my/file") catalog.add_resource file file[:backup] = "foo" file.bucket.should == bucket_resource.bucket end it "should find filebuckets added to the catalog after the file resource was created" do catalog = Puppet::Resource::Catalog.new file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") catalog.add_resource file bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file.bucket.should == bucket_resource.bucket end it "should have a nil filebucket if backup is false" do catalog = Puppet::Resource::Catalog.new bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file = Puppet::Type::File.new(:name => "/my/file", :backup => false) catalog.add_resource file file.bucket.should be_nil end it "should have a nil filebucket if backup is set to a string starting with '.'" do catalog = Puppet::Resource::Catalog.new bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") catalog.add_resource file file.bucket.should be_nil end it "should fail if there's no catalog and backup is not false" do file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") lambda { file.bucket }.should raise_error(Puppet::Error) end it "should fail if a non-existent catalog is specified" do file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") catalog = Puppet::Resource::Catalog.new catalog.add_resource file lambda { file.bucket }.should raise_error(Puppet::Error) end it "should be able to use the default filebucket without a catalog" do file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") file.bucket.should be_instance_of(Puppet::FileBucket::Dipper) end it "should look up the filebucket during finish()" do file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") file.expects(:bucket) file.finish end end describe "when retrieving the current file state" do it "should copy the source values if the 'source' parameter is set" do file = Puppet::Type::File.new(:name => "/my/file", :source => "/foo/bar") file.parameter(:source).expects(:copy_source_values) file.retrieve end end describe ".title_patterns" do before do @type_class = Puppet::Type.type(:file) end it "should have a regexp that captures the entire string, except for a terminating slash" do patterns = @type_class.title_patterns string = "abc/\n\tdef/" patterns[0][0] =~ string $1.should == "abc/\n\tdef" end end describe "when auditing" do it "should not fail if creating a new file if group is not set" do File.exists?(@path).should == false file = Puppet::Type::File.new(:name => @path, :audit => "all", :content => "content") catalog = Puppet::Resource::Catalog.new catalog.add_resource(file) Puppet::Util::Storage.stubs(:store) # to prevent the catalog from trying to write state.yaml transaction = catalog.apply transaction.report.resource_statuses["File[#{@path}]"].failed.should == false File.exists?(@path).should == true end it "should not log errors if creating a new file with ensure present and no content" do File.exists?(@path).should == false file = Puppet::Type::File.new(:name => @path, :audit => "content", :ensure => "present") catalog = Puppet::Resource::Catalog.new catalog.add_resource(file) Puppet::Util::Storage.stubs(:store) # to prevent the catalog from trying to write state.yaml catalog.apply @logs.reject {|l| l.level == :notice }.should be_empty end end describe "when specifying both source and checksum" do it 'should use the specified checksum when source is first' do @file[:source] = '/foo' @file[:checksum] = :md5lite @file[:checksum].should be :md5lite end it 'should use the specified checksum when source is last' do @file[:checksum] = :md5lite @file[:source] = '/foo' @file[:checksum].should be :md5lite end end end diff --git a/spec/unit/type/filebucket_spec.rb b/spec/unit/type/filebucket_spec.rb old mode 100644 new mode 100755 index 7fcbacc4f..3c5311184 --- a/spec/unit/type/filebucket_spec.rb +++ b/spec/unit/type/filebucket_spec.rb @@ -1,74 +1,73 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:filebucket) do describe "when validating attributes" do %w{name server port path}.each do |attr| it "should have a '#{attr}' parameter" do Puppet::Type.type(:filebucket).attrtype(attr.intern).should == :param end end it "should have its 'name' attribute set as its namevar" do Puppet::Type.type(:filebucket).key_attributes.should == [:name] end end it "should use the clientbucketdir as the path by default path" do Puppet.settings[:clientbucketdir] = "/my/bucket" Puppet::Type.type(:filebucket).new(:name => "main")[:path].should == Puppet[:clientbucketdir] end it "should use the masterport as the path by default port" do Puppet.settings[:masterport] = 50 Puppet::Type.type(:filebucket).new(:name => "main")[:port].should == Puppet[:masterport] end it "should use the server as the path by default server" do Puppet.settings[:server] = "myserver" Puppet::Type.type(:filebucket).new(:name => "main")[:server].should == Puppet[:server] end it "be local by default" do bucket = Puppet::Type.type(:filebucket).new :name => "main" bucket.bucket.should be_local end it "not be local if path is false" do bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false bucket.bucket.should_not be_local end it "be local if both a path and a server are specified" do bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "puppet", :path => "/my/path" bucket.bucket.should be_local end describe "when creating the filebucket" do before do @bucket = stub 'bucket', :name= => nil end it "should use any provided path" do bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => "/foo/bar" Puppet::FileBucket::Dipper.expects(:new).with(:Path => "/foo/bar").returns @bucket bucket.bucket end it "should use any provided server and port" do bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "myserv", :port => "myport", :path => false Puppet::FileBucket::Dipper.expects(:new).with(:Server => "myserv", :Port => "myport").returns @bucket bucket.bucket end it "should use the default server if the path is unset and no server is provided" do Puppet.settings[:server] = "myserv" bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false Puppet::FileBucket::Dipper.expects(:new).with { |args| args[:Server] == "myserv" }.returns @bucket bucket.bucket end end end diff --git a/spec/unit/type/group_spec.rb b/spec/unit/type/group_spec.rb index 43fdfcd4d..42fd3fb7b 100755 --- a/spec/unit/type/group_spec.rb +++ b/spec/unit/type/group_spec.rb @@ -1,57 +1,63 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:group) do before do ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") @class = Puppet::Type.type(:group) end it "should have a default provider" do @class.defaultprovider.should_not be_nil end it "should have a default provider inheriting from Puppet::Provider" do @class.defaultprovider.ancestors.should be_include(Puppet::Provider) end + it "should have a system_groups feature" do + @class.provider_feature(:system_groups).should_not be_nil + end + describe "when validating attributes" do [:name, :allowdupe].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:ensure, :gid].each do |param| it "should have a #{param} property" do @class.attrtype(param).should == :property end end it "should convert gids provided as strings into integers" do @class.new(:name => "foo", :gid => "15")[:gid].should == 15 end it "should accepts gids provided as integers" do @class.new(:name => "foo", :gid => 15)[:gid].should == 15 end end - # #1407 - we need to declare the allowdupe param as boolean. it "should have a boolean method for determining if duplicates are allowed" do @class.new(:name => "foo").methods.should be_include("allowdupe?") end + it "should have a boolean method for determining if system groups are allowed" do + @class.new(:name => "foo").methods.should be_include("system?") + end + it "should call 'create' to create the group" do group = @class.new(:name => "foo", :ensure => :present) group.provider.expects(:create) group.parameter(:ensure).sync end it "should call 'delete' to remove the group" do group = @class.new(:name => "foo", :ensure => :absent) group.provider.expects(:delete) group.parameter(:ensure).sync end end diff --git a/spec/unit/type/host_spec.rb b/spec/unit/type/host_spec.rb index 60ce73c6d..602c428af 100755 --- a/spec/unit/type/host_spec.rb +++ b/spec/unit/type/host_spec.rb @@ -1,130 +1,129 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' host = Puppet::Type.type(:host) describe host do before do @class = host @catalog = Puppet::Resource::Catalog.new @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider ].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:ip, :target, :host_aliases, :comment, :ensure].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end it "should have a list host_aliases" do @class.attrclass(:host_aliases).ancestors.should be_include(Puppet::Property::OrderedList) end end describe "when validating values" do it "should support present as a value for ensure" do proc { @class.new(:name => "foo", :ensure => :present) }.should_not raise_error end it "should support absent as a value for ensure" do proc { @class.new(:name => "foo", :ensure => :absent) }.should_not raise_error end it "should accept IPv4 addresses" do proc { @class.new(:name => "foo", :ip => '10.96.0.1') }.should_not raise_error end it "should accept long IPv6 addresses" do # Taken from wikipedia article about ipv6 proc { @class.new(:name => "foo", :ip => '2001:0db8:85a3:08d3:1319:8a2e:0370:7344') }.should_not raise_error end it "should accept one host_alias" do proc { @class.new(:name => "foo", :host_aliases => 'alias1') }.should_not raise_error end it "should accept multiple host_aliases" do proc { @class.new(:name => "foo", :host_aliases => [ 'alias1', 'alias2' ]) }.should_not raise_error end it "should accept shortened IPv6 addresses" do proc { @class.new(:name => "foo", :ip => '2001:db8:0:8d3:0:8a2e:70:7344') }.should_not raise_error proc { @class.new(:name => "foo", :ip => '::ffff:192.0.2.128') }.should_not raise_error proc { @class.new(:name => "foo", :ip => '::1') }.should_not raise_error end it "should not accept malformed IPv4 addresses like 192.168.0.300" do proc { @class.new(:name => "foo", :ip => '192.168.0.300') }.should raise_error end it "should not accept malformed IP addresses like 2001:0dg8:85a3:08d3:1319:8a2e:0370:7344" do proc { @class.new(:name => "foo", :ip => '2001:0dg8:85a3:08d3:1319:8a2e:0370:7344') }.should raise_error end it "should not accept spaces in resourcename" do proc { @class.new(:name => "foo bar") }.should raise_error end it "should not accept host_aliases with spaces" do proc { @class.new(:name => "foo", :host_aliases => [ 'well_formed', 'not wellformed' ]) }.should raise_error end it "should not accept empty host_aliases" do proc { @class.new(:name => "foo", :host_aliases => ['alias1','']) }.should raise_error end end describe "when syncing" do it "should send the first value to the provider for ip property" do @ip = @class.attrclass(:ip).new(:resource => @resource, :should => %w{192.168.0.1 192.168.0.2}) @provider.expects(:ip=).with '192.168.0.1' @ip.sync end it "should send the first value to the provider for comment property" do @comment = @class.attrclass(:comment).new(:resource => @resource, :should => %w{Bazinga Notme}) @provider.expects(:comment=).with 'Bazinga' @comment.sync end it "should send the joined array to the provider for host_alias" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @provider.expects(:host_aliases=).with 'foo bar' @host_aliases.sync end it "should also use the specified delimiter for joining" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.stubs(:delimiter).returns "\t" @provider.expects(:host_aliases=).with "foo\tbar" @host_aliases.sync end it "should care about the order of host_aliases" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.insync?(%w{foo bar}).should == true @host_aliases.insync?(%w{bar foo}).should == false end it "should not consider aliases to be in sync if should is a subset of current" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.insync?(%w{foo bar anotherone}).should == false end end end diff --git a/spec/unit/type/interface_spec.rb b/spec/unit/type/interface_spec.rb new file mode 100755 index 000000000..68f4c765f --- /dev/null +++ b/spec/unit/type/interface_spec.rb @@ -0,0 +1,93 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:interface) do + it "should have a 'name' parameter'" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1")[:name].should == "FastEthernet 0/1" + end + + it "should have a 'device_url' parameter'" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :device_url => :device)[:device_url].should == :device + end + + it "should have an ensure property" do + Puppet::Type.type(:interface).attrtype(:ensure).should == :property + end + + [:description, :speed, :duplex, :native_vlan, :encapsulation, :mode, :allowed_trunk_vlans, :etherchannel, :ipaddress].each do |p| + it "should have a #{p} property" do + Puppet::Type.type(:interface).attrtype(p).should == :property + end + end + + describe "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type.type(:interface).defaultprovider, :clear => nil + Puppet::Type.type(:interface).defaultprovider.stubs(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :present) + end + + it "should support :shutdown as a value to :ensure" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :shutdown) + end + + it "should support :no_shutdown as a value to :ensure" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :no_shutdown) + end + + describe "especially speed" do + it "should allow a number" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :speed => "100") + end + + it "should allow :auto" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :speed => :auto) + end + end + + describe "especially duplex" do + it "should allow :half" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :half) + end + + it "should allow :full" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :full) + end + + it "should allow :auto" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :auto) + end + end + + describe "especially ipaddress" do + it "should allow ipv4 addresses" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "192.168.0.1/24") + end + + it "should allow arrays of ipv4 addresses" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => ["192.168.0.1/24", "192.168.1.0/24"]) + end + + it "should allow ipv6 addresses" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64") + end + + it "should allow ipv6 options" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64 link-local") + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64 eui-64") + end + + it "should allow a mix of ipv4 and ipv6" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => ["192.168.0.1/24", "f0e9::/64 link-local"]) + end + + it "should munge ip addresses to a computer format" do + Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "192.168.0.1/24")[:ipaddress].should == [[24, IPAddr.new('192.168.0.1'), nil]] + end + end + end +end diff --git a/spec/unit/type/macauthorization_spec.rb b/spec/unit/type/macauthorization_spec.rb index e0d3b9433..8ab30834b 100755 --- a/spec/unit/type/macauthorization_spec.rb +++ b/spec/unit/type/macauthorization_spec.rb @@ -1,111 +1,110 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' macauth_type = Puppet::Type.type(:macauthorization) describe Puppet::Type.type(:macauthorization), "when checking macauthorization objects" do before do authplist = {} authplist["rules"] = { "foorule" => "foo" } authplist["rights"] = { "fooright" => "foo" } provider_class = macauth_type.provider(macauth_type.providers[0]) Plist.stubs(:parse_xml).with("/etc/authorization").returns(authplist) macauth_type.stubs(:defaultprovider).returns provider_class @resource = macauth_type.new(:name => 'foo') end describe "when validating attributes" do parameters = [:name,] properties = [:auth_type, :allow_root, :authenticate_user, :auth_class, :comment, :group, :k_of_n, :mechanisms, :rule, :session_owner, :shared, :timeout, :tries] parameters.each do |parameter| it "should have a #{parameter} parameter" do macauth_type.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{parameter} parameter" do macauth_type.attrclass(parameter).doc.should be_instance_of(String) end end properties.each do |property| it "should have a #{property} property" do macauth_type.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do macauth_type.attrclass(property).doc.should be_instance_of(String) end end end describe "when validating properties" do it "should have a default provider inheriting from Puppet::Provider" do macauth_type.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should be able to create an instance" do lambda { macauth_type.new(:name => 'foo') }.should_not raise_error end it "should support :present as a value to :ensure" do lambda { macauth_type.new(:name => "foo", :ensure => :present) }.should_not raise_error end it "should support :absent as a value to :ensure" do lambda { macauth_type.new(:name => "foo", :ensure => :absent) }.should_not raise_error end end [:k_of_n, :timeout, :tries].each do |property| describe "when managing the #{property} property" do it "should convert number-looking strings into actual numbers" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = "300" prop.should.must == 300 end it "should support integers as a value" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = 300 prop.should.must == 300 end it "should raise an error for non-integer values" do prop = macauth_type.attrclass(property).new(:resource => @resource) lambda { prop.should = "foo" }.should raise_error(Puppet::Error) end end end [:allow_root, :authenticate_user, :session_owner, :shared].each do |property| describe "when managing the #{property} property" do it "should convert boolean-looking false strings into actual booleans" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = "false" prop.should.must == :false end it "should convert boolean-looking true strings into actual booleans" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = "true" prop.should.must == :true end it "should raise an error for non-boolean values" do prop = macauth_type.attrclass(property).new(:resource => @resource) lambda { prop.should = "foo" }.should raise_error(Puppet::Error) end end end end diff --git a/spec/unit/type/maillist_spec.rb b/spec/unit/type/maillist_spec.rb index 7e96b4760..ae5fcd5b3 100755 --- a/spec/unit/type/maillist_spec.rb +++ b/spec/unit/type/maillist_spec.rb @@ -1,42 +1,41 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' maillist = Puppet::Type.type(:maillist) describe maillist do before do @provider_class = Puppet::Type.type(:maillist).provider(:mailman) @provider = stub 'provider', :class => @provider_class, :clear => nil @provider.stubs(:respond_to).with(:aliases).returns(true) @provider_class.stubs(:new).returns(@provider) Puppet::Type.type(:maillist).stubs(:defaultprovider).returns(@provider_class) @maillist = Puppet::Type.type(:maillist).new( :name => 'test' ) @catalog = Puppet::Resource::Catalog.new @maillist.catalog = @catalog end it "should generate aliases unless they already exist" do # Mail List aliases are careful not to stomp on managed Mail Alias aliases # test1 is an unmanaged alias from /etc/aliases Puppet::Type.type(:mailalias).provider(:aliases).stubs(:target_object).returns( StringIO.new("test1: root\n") ) # test2 is a managed alias from the manifest dupe = Puppet::Type.type(:mailalias).new( :name => 'test2' ) @catalog.add_resource dupe @provider.stubs(:aliases).returns({"test1" => 'this will get included', "test2" => 'this will dropped', "test3" => 'this will get included'}) generated = @maillist.generate generated.map{ |x| x.name }.sort.should == ['test1', 'test3'] generated.map{ |x| x.class }.should == [Puppet::Type::Mailalias, Puppet::Type::Mailalias] end end diff --git a/spec/unit/type/mcx_spec.rb b/spec/unit/type/mcx_spec.rb index 4b60a5c96..02f691a52 100755 --- a/spec/unit/type/mcx_spec.rb +++ b/spec/unit/type/mcx_spec.rb @@ -1,100 +1,79 @@ -#!/usr/bin/env ruby -#-- -# Copyright (C) 2008 Jeffrey J McCune. - -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# Author: Jeff McCune - -# Most of this code copied from /spec/type/service.rb - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/type/mcx' mcx_type = Puppet::Type.type(:mcx) describe mcx_type, "when validating attributes" do properties = [:ensure, :content] parameters = [:name, :ds_type, :ds_name] parameters.each do |p| it "should have a #{p} parameter" do mcx_type.attrclass(p).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{p} parameter" do mcx_type.attrclass(p).doc.should be_instance_of(String) end end properties.each do |p| it "should have a #{p} property" do mcx_type.attrclass(p).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{p} property" do mcx_type.attrclass(p).doc.should be_instance_of(String) end end end describe mcx_type, "default values" do before :each do provider_class = mcx_type.provider(mcx_type.providers[0]) mcx_type.stubs(:defaultprovider).returns provider_class end it "should be nil for :ds_type" do mcx_type.new(:name => '/Foo/bar')[:ds_type].should be_nil end it "should be nil for :ds_name" do mcx_type.new(:name => '/Foo/bar')[:ds_name].should be_nil end it "should be nil for :content" do mcx_type.new(:name => '/Foo/bar')[:content].should be_nil end end describe mcx_type, "when validating properties" do before :each do provider_class = mcx_type.provider(mcx_type.providers[0]) mcx_type.stubs(:defaultprovider).returns provider_class end it "should be able to create an instance" do lambda { mcx_type.new(:name => '/Foo/bar') }.should_not raise_error end it "should support :present as a value to :ensure" do lambda { mcx_type.new(:name => "/Foo/bar", :ensure => :present) }.should_not raise_error end it "should support :absent as a value to :ensure" do lambda { mcx_type.new(:name => "/Foo/bar", :ensure => :absent) }.should_not raise_error end end diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb index 4638e9a2e..9ef76992a 100755 --- a/spec/unit/type/mount_spec.rb +++ b/spec/unit/type/mount_spec.rb @@ -1,330 +1,329 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:mount) do it "should have a :refreshable feature that requires the :remount method" do Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount] end it "should have no default value for :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay") mount.should(:ensure).should be_nil end it "should have :name as the only keyattribut" do Puppet::Type.type(:mount).key_attributes.should == [:name] end end describe Puppet::Type.type(:mount), "when validating attributes" do [:name, :remounts, :provider].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:mount).attrtype(param).should == :param end end [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| it "should have a #{param} property" do Puppet::Type.type(:mount).attrtype(param).should == :property end end end describe Puppet::Type.type(:mount)::Ensure, "when validating values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider) end it "should alias :present to :defined as a value to :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) mount.should(:ensure).should == :defined end it "should support :present as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) end it "should support :defined as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :defined) end it "should support :unmounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) end it "should support :absent as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :absent) end it "should support :mounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) end end describe Puppet::Type.type(:mount)::Ensure do before :each do provider_properties = {} @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :property_hash => provider_properties Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) @ensure = @mount.property(:ensure) end def mount_stub(params) Puppet::Type.type(:mount).validproperties.each do |prop| unless params[prop] params[prop] = :absent @mount[prop] = :absent end end params.each do |param, value| @provider.stubs(param).returns(value) end end describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do def test_ensure_change(options) @provider.stubs(:get).with(:ensure).returns options[:from] @provider.stubs(:ensure).returns options[:from] @provider.stubs(:mounted?).returns([:mounted,:ghost].include? options[:from]) @provider.expects(:create).times(options[:create] || 0) @provider.expects(:destroy).times(options[:destroy] || 0) @provider.expects(:mount).never @provider.expects(:unmount).times(options[:unmount] || 0) @ensure.stubs(:syncothers) @ensure.should = options[:to] @ensure.sync (!!@provider.property_hash[:needs_mount]).should == (!!options[:mount]) end it "should create itself when changing from :ghost to :present" do test_ensure_change(:from => :ghost, :to => :present, :create => 1) end it "should create itself when changing from :absent to :present" do test_ensure_change(:from => :absent, :to => :present, :create => 1) end it "should create itself and unmount when changing from :ghost to :unmounted" do test_ensure_change(:from => :ghost, :to => :unmounted, :create => 1, :unmount => 1) end it "should unmount resource when changing from :mounted to :unmounted" do test_ensure_change(:from => :mounted, :to => :unmounted, :unmount => 1) end it "should create itself when changing from :absent to :unmounted" do test_ensure_change(:from => :absent, :to => :unmounted, :create => 1) end it "should unmount resource when changing from :ghost to :absent" do test_ensure_change(:from => :ghost, :to => :absent, :unmount => 1) end it "should unmount and destroy itself when changing from :mounted to :absent" do test_ensure_change(:from => :mounted, :to => :absent, :destroy => 1, :unmount => 1) end it "should destroy itself when changing from :unmounted to :absent" do test_ensure_change(:from => :unmounted, :to => :absent, :destroy => 1) end it "should create itself when changing from :ghost to :mounted" do test_ensure_change(:from => :ghost, :to => :mounted, :create => 1) end it "should create itself and mount when changing from :absent to :mounted" do test_ensure_change(:from => :absent, :to => :mounted, :create => 1, :mount => 1) end it "should mount resource when changing from :unmounted to :mounted" do test_ensure_change(:from => :unmounted, :to => :mounted, :mount => 1) end it "should be in sync if it is :absent and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:absent).should == true end it "should be out of sync if it is :absent and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :absent and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :absent and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :mounted and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:mounted).should == false end it "should be in sync if it is :mounted and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:mounted).should == true end it "should be in sync if it is :mounted and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:mounted).should == true end it "should be out in sync if it is :mounted and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:mounted).should == false end it "should be out of sync if it is :unmounted and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:unmounted).should == false end it "should be in sync if it is :unmounted and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:unmounted).should == true end it "should be out of sync if it is :unmounted and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:unmounted).should == false end it "should be in sync if it is :unmounted and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:unmounted).should == true end it "should be out of sync if it is :ghost and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:ghost).should == false end end describe Puppet::Type.type(:mount), "when responding to refresh" do pending "2.6.x specifies slightly different behavior and the desired behavior needs to be clarified and revisited. See ticket #4904" do it "should remount if it is supposed to be mounted" do @mount[:ensure] = "mounted" @provider.expects(:remount) @mount.refresh end it "should not remount if it is supposed to be present" do @mount[:ensure] = "present" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be absent" do @mount[:ensure] = "absent" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be defined" do @mount[:ensure] = "defined" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be unmounted" do @mount[:ensure] = "unmounted" @provider.expects(:remount).never @mount.refresh end it "should not remount swap filesystems" do @mount[:ensure] = "mounted" @mount[:fstype] = "swap" @provider.expects(:remount).never @mount.refresh end end end end describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :remount => nil Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) {:device => "/foo/bar", :blockdevice => "/other/bar", :target => "/what/ever", :fstype => 'eh', :options => "", :pass => 0, :dump => 0, :atboot => 0, :ensure => :mounted}.each do |param, value| @mount.provider.stubs(param).returns value @mount[param] = value end @mount.provider.stubs(:mounted?).returns true # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @mount end it "should use the provider to change the dump value" do @mount.provider.expects(:dump).returns 0 @mount.provider.expects(:dump=).with(1) @mount[:dump] = 1 @catalog.apply end it "should umount before flushing changes to disk" do syncorder = sequence('syncorder') @mount.provider.expects(:options).returns 'soft' @mount.provider.expects(:ensure).returns :mounted @mount.provider.expects(:unmount).in_sequence(syncorder) @mount.provider.expects(:options=).in_sequence(syncorder).with 'hard' @mount.expects(:flush).in_sequence(syncorder) # Call inside syncothers @mount.expects(:flush).in_sequence(syncorder) # I guess transaction or anything calls flush again @mount[:ensure] = :unmounted @mount[:options] = 'hard' @catalog.apply end end diff --git a/spec/unit/type/nagios_spec.rb b/spec/unit/type/nagios_spec.rb index 1515c2ae9..d650723c8 100755 --- a/spec/unit/type/nagios_spec.rb +++ b/spec/unit/type/nagios_spec.rb @@ -1,63 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/external/nagios' describe "Nagios resource types" do Nagios::Base.eachtype do |name, nagios_type| puppet_type = Puppet::Type.type("nagios_#{name}") it "should have a valid type for #{name}" do puppet_type.should_not be_nil end next unless puppet_type describe puppet_type do it "should be defined as a Puppet resource type" do puppet_type.should_not be_nil end it "should have documentation" do puppet_type.instance_variable_get("@doc").should_not == "" end it "should have #{nagios_type.namevar} as its key attribute" do puppet_type.key_attributes.should == [nagios_type.namevar] end it "should have documentation for its #{nagios_type.namevar} parameter" do puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil end it "should have an ensure property" do puppet_type.should be_validproperty(:ensure) end it "should have a target property" do puppet_type.should be_validproperty(:target) end it "should have documentation for its target property" do puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil end nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| it "should have a #{param} property" do puppet_type.should be_validproperty(param) end it "should have documentation for its #{param} property" do puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil end end nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| it "should have not have a #{param} property" do puppet_type.should_not be_validproperty(:param) end end end end end diff --git a/spec/unit/type/noop_metaparam_spec.rb b/spec/unit/type/noop_metaparam_spec.rb index 6f8f87e6c..f4241d417 100755 --- a/spec/unit/type/noop_metaparam_spec.rb +++ b/spec/unit/type/noop_metaparam_spec.rb @@ -1,37 +1,36 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/type' describe Puppet::Type.type(:file).attrclass(:noop) do before do Puppet.settings.stubs(:use) @file = Puppet::Type.newfile :path => "/what/ever" end it "should accept true as a value" do lambda { @file[:noop] = true }.should_not raise_error end it "should accept false as a value" do lambda { @file[:noop] = false }.should_not raise_error end describe "when set on a resource" do it "should default to the :noop setting" do Puppet.settings.expects(:value).with(:noop).returns "myval" @file.noop.should == "myval" end it "should prefer true values from the attribute" do @file[:noop] = true @file.noop.should be_true end it "should prefer false values from the attribute" do @file[:noop] = false @file.noop.should be_false end end end diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb index 3db9c77be..e75f7d290 100755 --- a/spec/unit/type/package_spec.rb +++ b/spec/unit/type/package_spec.rb @@ -1,240 +1,239 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:package) do before do Puppet::Util::Storage.stubs(:store) end it "should have an :installable feature that requires the :install method" do Puppet::Type.type(:package).provider_feature(:installable).methods.should == [:install] end it "should have an :uninstallable feature that requires the :uninstall method" do Puppet::Type.type(:package).provider_feature(:uninstallable).methods.should == [:uninstall] end it "should have an :upgradeable feature that requires :update and :latest methods" do Puppet::Type.type(:package).provider_feature(:upgradeable).methods.should == [:update, :latest] end it "should have a :purgeable feature that requires the :purge latest method" do Puppet::Type.type(:package).provider_feature(:purgeable).methods.should == [:purge] end it "should have a :versionable feature" do Puppet::Type.type(:package).provider_feature(:versionable).should_not be_nil end it "should default to being installed" do pkg = Puppet::Type.type(:package).new(:name => "yay") pkg.should(:ensure).should == :present end describe "when validating attributes" do [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:package).attrtype(param).should == :param end end it "should have an ensure property" do Puppet::Type.type(:package).attrtype(:ensure).should == :property end end describe "when validating attribute values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil Puppet::Type.type(:package).defaultprovider.expects(:new).returns(@provider) end it "should support :present as a value to :ensure" do Puppet::Type.type(:package).new(:name => "yay", :ensure => :present) end it "should alias :installed to :present as a value to :ensure" do pkg = Puppet::Type.type(:package).new(:name => "yay", :ensure => :installed) pkg.should(:ensure).should == :present end it "should support :absent as a value to :ensure" do Puppet::Type.type(:package).new(:name => "yay", :ensure => :absent) end it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do @provider.expects(:satisfies?).with([:purgeable]).returns(true) Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) end it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do @provider.expects(:satisfies?).with([:purgeable]).returns(false) proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) end it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do @provider.expects(:satisfies?).with([:upgradeable]).returns(true) Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) end it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do @provider.expects(:satisfies?).with([:upgradeable]).returns(false) proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) end it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do @provider.expects(:satisfies?).with([:versionable]).returns(true) Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") end it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do @provider.expects(:satisfies?).with([:versionable]).returns(false) proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) end it "should accept any string as an argument to :source" do proc { Puppet::Type.type(:package).new(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) end end module PackageEvaluationTesting def setprops(properties) @provider.stubs(:properties).returns(properties) end end describe Puppet::Type.type(:package) do before :each do @provider = stub 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) Puppet::Type.type(:package).defaultprovider.stubs(:instances).returns([]) @package = Puppet::Type.type(:package).new(:name => "yay") @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@package) end describe Puppet::Type.type(:package), "when it should be purged" do include PackageEvaluationTesting before { @package[:ensure] = :purged } it "should do nothing if it is :purged" do @provider.expects(:properties).returns(:ensure => :purged).at_least_once @catalog.apply end [:absent, :installed, :present, :latest].each do |state| it "should purge if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:purge) @catalog.apply end end end describe Puppet::Type.type(:package), "when it should be absent" do include PackageEvaluationTesting before { @package[:ensure] = :absent } [:purged, :absent].each do |state| it "should do nothing if it is #{state.to_s}" do @provider.expects(:properties).returns(:ensure => state).at_least_once @catalog.apply end end [:installed, :present, :latest].each do |state| it "should uninstall if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:uninstall) @catalog.apply end end end describe Puppet::Type.type(:package), "when it should be present" do include PackageEvaluationTesting before { @package[:ensure] = :present } [:present, :latest, "1.0"].each do |state| it "should do nothing if it is #{state.to_s}" do @provider.expects(:properties).returns(:ensure => state).at_least_once @catalog.apply end end [:purged, :absent].each do |state| it "should install if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:install) @catalog.apply end end end describe Puppet::Type.type(:package), "when it should be latest" do include PackageEvaluationTesting before { @package[:ensure] = :latest } [:purged, :absent].each do |state| it "should upgrade if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:update) @catalog.apply end end it "should upgrade if the current version is not equal to the latest version" do @provider.stubs(:properties).returns(:ensure => "1.0") @provider.stubs(:latest).returns("2.0") @provider.expects(:update) @catalog.apply end it "should do nothing if it is equal to the latest version" do @provider.stubs(:properties).returns(:ensure => "1.0") @provider.stubs(:latest).returns("1.0") @provider.expects(:update).never @catalog.apply end it "should do nothing if the provider returns :present as the latest version" do @provider.stubs(:properties).returns(:ensure => :present) @provider.stubs(:latest).returns("1.0") @provider.expects(:update).never @catalog.apply end end describe Puppet::Type.type(:package), "when it should be a specific version" do include PackageEvaluationTesting before { @package[:ensure] = "1.0" } [:purged, :absent].each do |state| it "should install if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:install) @catalog.apply end end it "should do nothing if the current version is equal to the desired version" do @provider.stubs(:properties).returns(:ensure => "1.0") @provider.expects(:install).never @catalog.apply end it "should install if the current version is not equal to the specified version" do @provider.stubs(:properties).returns(:ensure => "2.0") @provider.expects(:install) @catalog.apply end end end end diff --git a/spec/unit/type/resources_spec.rb b/spec/unit/type/resources_spec.rb old mode 100644 new mode 100755 index 020ab9b81..aedc58c7c --- a/spec/unit/type/resources_spec.rb +++ b/spec/unit/type/resources_spec.rb @@ -1,102 +1,101 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' resources = Puppet::Type.type(:resources) # There are still plenty of tests to port over from test/. describe resources do describe "when initializing" do it "should fail if the specified resource type does not exist" do Puppet::Type.stubs(:type).with { |x| x.to_s.downcase == "resources"}.returns resources Puppet::Type.expects(:type).with("nosuchtype").returns nil lambda { resources.new :name => "nosuchtype" }.should raise_error(Puppet::Error) end it "should not fail when the specified resource type exists" do lambda { resources.new :name => "file" }.should_not raise_error end it "should set its :resource_type attribute" do resources.new(:name => "file").resource_type.should == Puppet::Type.type(:file) end end describe "#generate" do before do @host1 = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') @catalog = Puppet::Resource::Catalog.new @context = Puppet::Transaction.new(@catalog) end describe "when dealing with non-purging resources" do before do @resources = Puppet::Type.type(:resources).new(:name => 'host') end it "should not generate any resource" do @resources.generate.should be_empty end end describe "when the catalog contains a purging resource" do before do @resources = Puppet::Type.type(:resources).new(:name => 'host', :purge => true) @purgeable_resource = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') @catalog.add_resource @resources end it "should not generate a duplicate of that resource" do Puppet::Type.type(:host).stubs(:instances).returns [@host1] @catalog.add_resource @host1 @resources.generate.collect { |r| r.ref }.should_not include(@host1.ref) end it "should not include the skipped users" do res = Puppet::Type.type(:resources).new :name => :user, :purge => true res.catalog = Puppet::Resource::Catalog.new users = [ Puppet::Type.type(:user).new(:name => "root") ] Puppet::Type.type(:user).expects(:instances).returns users list = res.generate names = list.collect { |r| r[:name] } names.should_not be_include("root") end describe "when generating a purgeable resource" do it "should be included in the generated resources" do Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] @resources.generate.collect { |r| r.ref }.should include(@purgeable_resource.ref) end end describe "when the instance's do not have an ensure property" do it "should not be included in the generated resources" do @no_ensure_resource = Puppet::Type.type(:exec).new(:name => '/usr/bin/env echo') Puppet::Type.type(:host).stubs(:instances).returns [@no_ensure_resource] @resources.generate.collect { |r| r.ref }.should_not include(@no_ensure_resource.ref) end end describe "when the instance's ensure property does not accept absent" do it "should not be included in the generated resources" do @no_absent_resource = Puppet::Type.type(:service).new(:name => 'foobar') Puppet::Type.type(:host).stubs(:instances).returns [@no_absent_resource] @resources.generate.collect { |r| r.ref }.should_not include(@no_absent_resource.ref) end end describe "when checking the instance fails" do it "should not be included in the generated resources" do @purgeable_resource = Puppet::Type.type(:host).new(:name => 'foobar') Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] @resources.expects(:check).with(@purgeable_resource).returns(false) @resources.generate.collect { |r| r.ref }.should_not include(@purgeable_resource.ref) end end end end end diff --git a/spec/unit/type/schedule_spec.rb b/spec/unit/type/schedule_spec.rb index 59d199812..7599411e4 100755 --- a/spec/unit/type/schedule_spec.rb +++ b/spec/unit/type/schedule_spec.rb @@ -1,305 +1,304 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' module ScheduleTesting def format(time) time.strftime("%H:%M:%S") end def diff(unit, incr, method, count) diff = Time.now.to_i.send(method, incr * count) Time.at(diff) end def day(method, count) diff(:hour, 3600 * 24, method, count) end def hour(method, count) diff(:hour, 3600, method, count) end def min(method, count) diff(:min, 60, method, count) end end describe Puppet::Type.type(:schedule) do before :each do Puppet[:ignoreschedules] = false @schedule = Puppet::Type.type(:schedule).new(:name => "testing") end describe Puppet::Type.type(:schedule) do include ScheduleTesting it "should default to :distance for period-matching" do @schedule[:periodmatch].should == :distance end it "should default to a :repeat of 1" do @schedule[:repeat].should == 1 end it "should never match when the period is :never" do @schedule[:period] = :never @schedule.match?.should be_false end end describe Puppet::Type.type(:schedule), "when producing default schedules" do include ScheduleTesting %w{hourly daily weekly monthly never}.each do |period| period = period.to_sym it "should produce a #{period} schedule with the period set appropriately" do schedules = Puppet::Type.type(:schedule).mkdefaultschedules schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type.type(:schedule)) end end it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do schedules = Puppet::Type.type(:schedule).mkdefaultschedules schedules.find { |s| s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 }.should be_instance_of(Puppet::Type.type(:schedule)) end end describe Puppet::Type.type(:schedule), "when matching ranges" do include ScheduleTesting it "should match when the start time is before the current time and the end time is after the current time" do @schedule[:range] = "#{format(Time.now - 10)} - #{format(Time.now + 10)}" @schedule.match?.should be_true end it "should not match when the start time is after the current time" do @schedule[:range] = "#{format(Time.now + 5)} - #{format(Time.now + 10)}" @schedule.match?.should be_false end it "should not match when the end time is previous to the current time" do @schedule[:range] = "#{format(Time.now - 10)} - #{format(Time.now - 5)}" @schedule.match?.should be_false end end describe Puppet::Type.type(:schedule), "when matching hourly by distance" do include ScheduleTesting before do @schedule[:period] = :hourly @schedule[:periodmatch] = :distance end it "should match an hour ago" do @schedule.match?(hour("-", 1)).should be_true end it "should not match now" do @schedule.match?(Time.now).should be_false end it "should not match 59 minutes ago" do @schedule.match?(min("-", 59)).should be_false end end describe Puppet::Type.type(:schedule), "when matching daily by distance" do include ScheduleTesting before do @schedule[:period] = :daily @schedule[:periodmatch] = :distance end it "should match when the previous time was one day ago" do @schedule.match?(day("-", 1)).should be_true end it "should not match when the previous time is now" do @schedule.match?(Time.now).should be_false end it "should not match when the previous time was 23 hours ago" do @schedule.match?(hour("-", 23)).should be_false end end describe Puppet::Type.type(:schedule), "when matching weekly by distance" do include ScheduleTesting before do @schedule[:period] = :weekly @schedule[:periodmatch] = :distance end it "should match seven days ago" do @schedule.match?(day("-", 7)).should be_true end it "should not match now" do @schedule.match?(Time.now).should be_false end it "should not match six days ago" do @schedule.match?(day("-", 6)).should be_false end end describe Puppet::Type.type(:schedule), "when matching monthly by distance" do include ScheduleTesting before do @schedule[:period] = :monthly @schedule[:periodmatch] = :distance end it "should match 32 days ago" do @schedule.match?(day("-", 32)).should be_true end it "should not match now" do @schedule.match?(Time.now).should be_false end it "should not match 27 days ago" do @schedule.match?(day("-", 27)).should be_false end end describe Puppet::Type.type(:schedule), "when matching hourly by number" do include ScheduleTesting before do @schedule[:period] = :hourly @schedule[:periodmatch] = :number end it "should match if the times are one minute apart and the current minute is 0" do current = Time.utc(2008, 1, 1, 0, 0, 0) previous = Time.utc(2007, 12, 31, 23, 59, 0) Time.stubs(:now).returns(current) @schedule.match?(previous).should be_true end it "should not match if the times are 59 minutes apart and the current minute is 59" do current = Time.utc(2009, 2, 1, 12, 59, 0) previous = Time.utc(2009, 2, 1, 12, 0, 0) Time.stubs(:now).returns(current) @schedule.match?(previous).should be_false end end describe Puppet::Type.type(:schedule), "when matching daily by number" do include ScheduleTesting before do @schedule[:period] = :daily @schedule[:periodmatch] = :number end it "should match if the times are one minute apart and the current minute and hour are 0" do current = Time.utc(2010, "nov", 7, 0, 0, 0) # Now set the previous time to one minute before that previous = current - 60 Time.stubs(:now).returns(current) @schedule.match?(previous).should be_true end it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do # Reset the previous time to 00:00:00 previous = Time.utc(2010, "nov", 7, 0, 0, 0) # Set the current time to 23:59 now = previous + (23 * 3600) + (59 * 60) Time.stubs(:now).returns(now) @schedule.match?(previous).should be_false end end describe Puppet::Type.type(:schedule), "when matching weekly by number" do include ScheduleTesting before do @schedule[:period] = :weekly @schedule[:periodmatch] = :number end it "should match if the previous time is prior to the most recent Sunday" do now = Time.utc(2010, "nov", 11, 0, 0, 0) # Thursday Time.stubs(:now).returns(now) previous = Time.utc(2010, "nov", 6, 23, 59, 59) # Sat @schedule.match?(previous).should be_true end it "should not match if the previous time is after the most recent Saturday" do now = Time.utc(2010, "nov", 11, 0, 0, 0) # Thursday Time.stubs(:now).returns(now) previous = Time.utc(2010, "nov", 7, 0, 0, 0) # Sunday @schedule.match?(previous).should be_false end end describe Puppet::Type.type(:schedule), "when matching monthly by number" do include ScheduleTesting before do @schedule[:period] = :monthly @schedule[:periodmatch] = :number end it "should match when the previous time is prior to the first day of this month" do now = Time.utc(2010, "nov", 8, 00, 59, 59) Time.stubs(:now).returns(now) previous = Time.utc(2010, "oct", 31, 23, 59, 59) @schedule.match?(previous).should be_true end it "should not match when the previous time is after the last day of last month" do now = Time.utc(2010, "nov", 8, 00, 59, 59) Time.stubs(:now).returns(now) previous = Time.utc(2010, "nov", 1, 0, 0, 0) @schedule.match?(previous).should be_false end end describe Puppet::Type.type(:schedule), "when matching with a repeat greater than one" do include ScheduleTesting before do @schedule[:period] = :daily @schedule[:repeat] = 2 end it "should fail if the periodmatch is 'number'" do @schedule[:periodmatch] = :number proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) end it "should match if the previous run was further away than the distance divided by the repeat" do previous = Time.now - (3600 * 13) @schedule.match?(previous).should be_true end it "should not match if the previous run was closer than the distance divided by the repeat" do previous = Time.now - (3600 * 11) @schedule.match?(previous).should be_false end end end diff --git a/spec/unit/type/selboolean_spec.rb b/spec/unit/type/selboolean_spec.rb index 41b254f25..aa495af4a 100755 --- a/spec/unit/type/selboolean_spec.rb +++ b/spec/unit/type/selboolean_spec.rb @@ -1,45 +1,44 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:selboolean), "when validating attributes" do [:name, :persistent].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:selboolean).attrtype(param).should == :param end end it "should have a value property" do Puppet::Type.type(:selboolean).attrtype(:value).should == :property end end describe Puppet::Type.type(:selboolean), "when validating values" do before do @class = Puppet::Type.type(:selboolean) @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true @class.stubs(:defaultprovider).returns(@provider_class) @class.stubs(:provider).returns(@provider_class) @provider = stub 'provider', :class => @provider_class, :clear => nil @provider_class.stubs(:new).returns(@provider) end it "should support :on as a value to :value" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on) end it "should support :off as a value to :value" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :off) end it "should support :true as a value to :persistent" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :true) end it "should support :false as a value to :persistent" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :false) end end diff --git a/spec/unit/type/selmodule_spec.rb b/spec/unit/type/selmodule_spec.rb index 37453d8ed..c067de01d 100755 --- a/spec/unit/type/selmodule_spec.rb +++ b/spec/unit/type/selmodule_spec.rb @@ -1,18 +1,17 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:selmodule), "when validating attributes" do [:name, :selmoduledir, :selmodulepath].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:selmodule).attrtype(param).should == :param end end [:ensure, :syncversion].each do |param| it "should have a #{param} property" do Puppet::Type.type(:selmodule).attrtype(param).should == :property end end end diff --git a/spec/unit/type/service_spec.rb b/spec/unit/type/service_spec.rb index b11c4d7e7..40270e7c8 100755 --- a/spec/unit/type/service_spec.rb +++ b/spec/unit/type/service_spec.rb @@ -1,234 +1,233 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:service) do it "should have an :enableable feature that requires the :enable, :disable, and :enabled? methods" do Puppet::Type.type(:service).provider_feature(:enableable).methods.should == [:disable, :enable, :enabled?] end it "should have a :refreshable feature that requires the :restart method" do Puppet::Type.type(:service).provider_feature(:refreshable).methods.should == [:restart] end end describe Puppet::Type.type(:service), "when validating attributes" do [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:service).attrtype(param).should == :param end end [:ensure, :enable].each do |param| it "should have an #{param} property" do Puppet::Type.type(:service).attrtype(param).should == :property end end end describe Puppet::Type.type(:service), "when validating attribute values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:service).defaultprovider, :clear => nil, :controllable? => false Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(@provider) end it "should support :running as a value to :ensure" do Puppet::Type.type(:service).new(:name => "yay", :ensure => :running) end it "should support :stopped as a value to :ensure" do Puppet::Type.type(:service).new(:name => "yay", :ensure => :stopped) end it "should alias the value :true to :running in :ensure" do svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => true) svc.should(:ensure).should == :running end it "should alias the value :false to :stopped in :ensure" do svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => false) svc.should(:ensure).should == :stopped end it "should support :true as a value to :enable" do Puppet::Type.type(:service).new(:name => "yay", :enable => :true) end it "should support :false as a value to :enable" do Puppet::Type.type(:service).new(:name => "yay", :enable => :false) end it "should support :true as a value to :hasstatus" do Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :true) end it "should support :false as a value to :hasstatus" do Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :false) end it "should specify :true as the default value of hasstatus" do Puppet::Type.type(:service).new(:name => "yay")[:hasstatus].should == :true end it "should support :true as a value to :hasrestart" do Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :true) end it "should support :false as a value to :hasrestart" do Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :false) end it "should allow setting the :enable parameter if the provider has the :enableable feature" do Puppet::Type.type(:service).defaultprovider.stubs(:supports_parameter?).returns(true) Puppet::Type.type(:service).defaultprovider.expects(:supports_parameter?).with(Puppet::Type.type(:service).attrclass(:enable)).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :enable => true) svc.should(:enable).should == :true end it "should not allow setting the :enable parameter if the provider is missing the :enableable feature" do Puppet::Type.type(:service).defaultprovider.stubs(:supports_parameter?).returns(true) Puppet::Type.type(:service).defaultprovider.expects(:supports_parameter?).with(Puppet::Type.type(:service).attrclass(:enable)).returns(false) svc = Puppet::Type.type(:service).new(:name => "yay", :enable => true) svc.should(:enable).should be_nil end it "should split paths on ':'" do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :path => "/one/two:/three/four") svc[:path].should == %w{/one/two /three/four} end it "should accept arrays of paths joined by ':'" do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :path => ["/one:/two", "/three:/four"]) svc[:path].should == %w{/one /two /three /four} end end describe Puppet::Type.type(:service), "when setting default attribute values" do it "should default to the provider's default path if one is available" do FileTest.stubs(:directory?).returns(true) FileTest.stubs(:exist?).returns(true) Puppet::Type.type(:service).defaultprovider.stubs(:respond_to?).returns(true) Puppet::Type.type(:service).defaultprovider.stubs(:defpath).returns("testing") svc = Puppet::Type.type(:service).new(:name => "other") svc[:path].should == ["testing"] end it "should default 'pattern' to the binary if one is provided" do svc = Puppet::Type.type(:service).new(:name => "other", :binary => "/some/binary") svc[:pattern].should == "/some/binary" end it "should default 'pattern' to the name if no pattern is provided" do svc = Puppet::Type.type(:service).new(:name => "other") svc[:pattern].should == "other" end it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do provider = stub 'provider', :controllable? => true, :class => Puppet::Type.type(:service).defaultprovider, :clear => nil Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(provider) svc = Puppet::Type.type(:service).new(:name => "nfs.client") svc[:control].should == "NFS_CLIENT_START" end end describe Puppet::Type.type(:service), "when retrieving the host's current state" do before do @service = Puppet::Type.type(:service).new(:name => "yay") end it "should use the provider's status to determine whether the service is running" do @service.provider.expects(:status).returns(:yepper) @service[:ensure] = :running @service.property(:ensure).retrieve.should == :yepper end it "should ask the provider whether it is enabled" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service.provider.expects(:enabled?).returns(:yepper) @service[:enable] = true @service.property(:enable).retrieve.should == :yepper end end describe Puppet::Type.type(:service), "when changing the host" do before do @service = Puppet::Type.type(:service).new(:name => "yay") end it "should start the service if it is supposed to be running" do @service[:ensure] = :running @service.provider.expects(:start) @service.property(:ensure).sync end it "should stop the service if it is supposed to be stopped" do @service[:ensure] = :stopped @service.provider.expects(:stop) @service.property(:ensure).sync end it "should enable the service if it is supposed to be enabled" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service[:enable] = true @service.provider.expects(:enable) @service.property(:enable).sync end it "should disable the service if it is supposed to be disabled" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service[:enable] = false @service.provider.expects(:disable) @service.property(:enable).sync end it "should sync the service's enable state when changing the state of :ensure if :enable is being managed" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service[:enable] = false @service[:ensure] = :stopped @service.property(:enable).expects(:retrieve).returns("whatever") @service.property(:enable).expects(:insync?).returns(false) @service.property(:enable).expects(:sync) @service.provider.stubs(:stop) @service.property(:ensure).sync end end describe Puppet::Type.type(:service), "when refreshing the service" do before do @service = Puppet::Type.type(:service).new(:name => "yay") end it "should restart the service if it is running" do @service[:ensure] = :running @service.provider.expects(:status).returns(:running) @service.provider.expects(:restart) @service.refresh end it "should restart the service if it is running, even if it is supposed to stopped" do @service[:ensure] = :stopped @service.provider.expects(:status).returns(:running) @service.provider.expects(:restart) @service.refresh end it "should not restart the service if it is not running" do @service[:ensure] = :running @service.provider.expects(:status).returns(:stopped) @service.refresh end it "should add :ensure as a property if it is not being managed" do @service.provider.expects(:status).returns(:running) @service.provider.expects(:restart) @service.refresh end end diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb index 287fca3ab..a5f167165 100755 --- a/spec/unit/type/ssh_authorized_key_spec.rb +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -1,152 +1,151 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) describe ssh_authorized_key do before do @class = Puppet::Type.type(:ssh_authorized_key) @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true @class.stubs(:defaultprovider).returns(@provider_class) @class.stubs(:provider).returns(@provider_class) @provider = stub 'provider', :class => @provider_class, :file_path => "/tmp/whatever", :clear => nil @provider_class.stubs(:new).returns(@provider) @catalog = Puppet::Resource::Catalog.new end it "should have a name parameter" do @class.attrtype(:name).should == :param end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end it "should have a :provider parameter" do @class.attrtype(:provider).should == :param end it "should have an ensure property" do @class.attrtype(:ensure).should == :property end it "should support :present as a value for :ensure" do proc { @class.new(:name => "whev", :ensure => :present, :user => "nobody") }.should_not raise_error end it "should support :absent as a value for :ensure" do proc { @class.new(:name => "whev", :ensure => :absent, :user => "nobody") }.should_not raise_error end it "should have an type property" do @class.attrtype(:type).should == :property end it "should support ssh-dss as an type value" do proc { @class.new(:name => "whev", :type => "ssh-dss", :user => "nobody") }.should_not raise_error end it "should support ssh-rsa as an type value" do proc { @class.new(:name => "whev", :type => "ssh-rsa", :user => "nobody") }.should_not raise_error end it "should support :dsa as an type value" do proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody") }.should_not raise_error end it "should support :rsa as an type value" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody") }.should_not raise_error end it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa in the ssh_authorized_key_type" do proc { @class.new(:name => "whev", :type => :something) }.should raise_error(Puppet::Error) end it "should have an key property" do @class.attrtype(:key).should == :property end it "should have an user property" do @class.attrtype(:user).should == :property end it "should have an options property" do @class.attrtype(:options).should == :property end it "'s options property should return well formed string of arrays from is_to_s" do resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) resource.property(:options).is_to_s(["a","b","c"]).should == "a,b,c" end it "'s options property should return well formed string of arrays from is_to_s" do resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) resource.property(:options).should_to_s(["a","b","c"]).should == "a,b,c" end it "should have a target property" do @class.attrtype(:target).should == :property end describe "when neither user nor target is specified" do it "should raise an error" do proc do @class.create( :name => "Test", :key => "AAA", :type => "ssh-rsa", :ensure => :present) end.should raise_error(Puppet::Error) end end describe "when both target and user are specified" do it "should use target" do resource = @class.create( :name => "Test", :user => "root", :target => "/tmp/blah") resource.should(:target).should == "/tmp/blah" end end describe "when user is specified" do it "should determine target" do resource = @class.create( :name => "Test", :user => "root") target = File.expand_path("~root/.ssh/authorized_keys") resource.should(:target).should == target end # Bug #2124 - ssh_authorized_key always changes target if target is not defined it "should not raise spurious change events" do resource = @class.new(:name => "Test", :user => "root") target = File.expand_path("~root/.ssh/authorized_keys") resource.property(:target).safe_insync?(target).should == true end end describe "when calling validate" do it "should not crash on a non-existant user" do resource = @class.create( :name => "Test", :user => "ihopesuchuserdoesnotexist") proc { resource.validate }.should_not raise_error end end end diff --git a/spec/unit/type/sshkey_spec.rb b/spec/unit/type/sshkey_spec.rb old mode 100644 new mode 100755 index 8315d7ba9..ba3406976 --- a/spec/unit/type/sshkey_spec.rb +++ b/spec/unit/type/sshkey_spec.rb @@ -1,71 +1,70 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' sshkey = Puppet::Type.type(:sshkey) describe sshkey do before do @class = sshkey end it "should have :name its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:host_aliases, :ensure, :key, :type].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end end describe "when validating values" do it "should support ssh-dss as a type value" do proc { @class.new(:name => "foo", :type => "ssh-dss") }.should_not raise_error end it "should support ssh-rsa as a type value" do proc { @class.new(:name => "whev", :type => "ssh-rsa") }.should_not raise_error end it "should alias :dsa to ssh-dss as a value for type" do key = @class.new(:name => "whev", :type => :dsa) key.should(:type).should == :'ssh-dss' end it "should alias :rsa to ssh-rsa as a value for type" do key = @class.new(:name => "whev", :type => :rsa) key.should(:type).should == :'ssh-rsa' end it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa for type" do proc { @class.new(:name => "whev", :type => :'ssh-dsa') }.should raise_error(Puppet::Error) end it "should accept one host_alias" do proc { @class.new(:name => "foo", :host_aliases => 'foo.bar.tld') }.should_not raise_error end it "should accept multiple host_aliases as an array" do proc { @class.new(:name => "foo", :host_aliases => ['foo.bar.tld','10.0.9.9']) }.should_not raise_error end it "should not accept spaces in any host_alias" do proc { @class.new(:name => "foo", :host_aliases => ['foo.bar.tld','foo bar']) }.should raise_error(Puppet::Error) end it "should not accept aliases in the resourcename" do proc { @class.new(:name => 'host,host.domain,ip') }.should raise_error(Puppet::Error) end end end diff --git a/spec/unit/type/stage_spec.rb b/spec/unit/type/stage_spec.rb old mode 100644 new mode 100755 index 19922f112..f5fed6c3c --- a/spec/unit/type/stage_spec.rb +++ b/spec/unit/type/stage_spec.rb @@ -1,9 +1,8 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type.type(:stage) do it "should have a 'name' parameter'" do Puppet::Type.type(:stage).new(:name => :foo)[:name].should == :foo end end diff --git a/spec/unit/type/tidy_spec.rb b/spec/unit/type/tidy_spec.rb index 56bdf6c19..cb030634b 100755 --- a/spec/unit/type/tidy_spec.rb +++ b/spec/unit/type/tidy_spec.rb @@ -1,425 +1,424 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/file_bucket/dipper' tidy = Puppet::Type.type(:tidy) describe tidy do before do @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" Puppet.settings.stubs(:use) # for an unknown reason some of these specs fails when run individually # with a failed expectation on File.lstat in the autoloader. File.stubs(:lstat) end it "should use :lstat when stating a file" do resource = tidy.new :path => "/foo/bar", :age => "1d" stat = mock 'stat' File.expects(:lstat).with("/foo/bar").returns stat resource.stat("/foo/bar").should == stat end [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:tidy).attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} param" do Puppet::Type.type(:tidy).attrclass(param).doc.should be_instance_of(String) end end describe "when validating parameter values" do describe "for 'recurse'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should allow 'true'" do lambda { @tidy[:recurse] = true }.should_not raise_error end it "should allow 'false'" do lambda { @tidy[:recurse] = false }.should_not raise_error end it "should allow integers" do lambda { @tidy[:recurse] = 10 }.should_not raise_error end it "should allow string representations of integers" do lambda { @tidy[:recurse] = "10" }.should_not raise_error end it "should allow 'inf'" do lambda { @tidy[:recurse] = "inf" }.should_not raise_error end it "should not allow arbitrary values" do lambda { @tidy[:recurse] = "whatever" }.should raise_error end end describe "for 'matches'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should object if matches is given with recurse is not specified" do lambda { @tidy[:matches] = '*.doh' }.should raise_error end it "should object if matches is given and recurse is 0" do lambda { @tidy[:recurse] = 0; @tidy[:matches] = '*.doh' }.should raise_error end it "should object if matches is given and recurse is false" do lambda { @tidy[:recurse] = false; @tidy[:matches] = '*.doh' }.should raise_error end it "should not object if matches is given and recurse is > 0" do lambda { @tidy[:recurse] = 1; @tidy[:matches] = '*.doh' }.should_not raise_error end it "should not object if matches is given and recurse is true" do lambda { @tidy[:recurse] = true; @tidy[:matches] = '*.doh' }.should_not raise_error end end end describe "when matching files by age" do convertors = { :second => 1, :minute => 60 } convertors[:hour] = convertors[:minute] * 60 convertors[:day] = convertors[:hour] * 24 convertors[:week] = convertors[:day] * 7 convertors.each do |unit, multiple| it "should consider a #{unit} to be #{multiple} seconds" do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :age => "5#{unit.to_s[0..0]}" @tidy[:age].should == 5 * multiple end end end describe "when matching files by size" do convertors = { :b => 0, :kb => 1, :mb => 2, :gb => 3, :tb => 4 } convertors.each do |unit, multiple| it "should consider a #{unit} to be 1024^#{multiple} bytes" do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :size => "5#{unit}" total = 5 multiple.times { total *= 1024 } @tidy[:size].should == total end end end describe "when tidying" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "directory" File.stubs(:lstat).with(@basepath).returns @stat end describe "and generating files" do it "should set the backup on the file if backup is set on the tidy instance" do @tidy[:backup] = "whatever" Puppet::Type.type(:file).expects(:new).with { |args| args[:backup] == "whatever" } @tidy.mkfile(@basepath) end it "should set the file's path to the tidy's path" do Puppet::Type.type(:file).expects(:new).with { |args| args[:path] == @basepath } @tidy.mkfile(@basepath) end it "should configure the file for deletion" do Puppet::Type.type(:file).expects(:new).with { |args| args[:ensure] == :absent } @tidy.mkfile(@basepath) end it "should force deletion on the file" do Puppet::Type.type(:file).expects(:new).with { |args| args[:force] == true } @tidy.mkfile(@basepath) end it "should do nothing if the targeted file does not exist" do File.expects(:lstat).with(@basepath).raises Errno::ENOENT @tidy.generate.should == [] end end describe "and recursion is not used" do it "should generate a file resource if the file should be tidied" do @tidy.expects(:tidy?).with(@basepath).returns true file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") @tidy.expects(:mkfile).with(@basepath).returns file @tidy.generate.should == [file] end it "should do nothing if the file should not be tidied" do @tidy.expects(:tidy?).with(@basepath).returns false @tidy.expects(:mkfile).never @tidy.generate.should == [] end end describe "and recursion is used" do before do @tidy[:recurse] = true Puppet::FileServing::Fileset.any_instance.stubs(:stat).returns mock("stat") @fileset = Puppet::FileServing::Fileset.new(@basepath) Puppet::FileServing::Fileset.stubs(:new).returns @fileset end it "should use a Fileset for infinite recursion" do Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should use a Fileset for limited recursion" do @tidy[:recurse] = 42 Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true, :recurselimit => 42).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should generate a file resource for every file that should be tidied but not for files that should not be tidied" do @fileset.expects(:files).returns %w{. one two} @tidy.expects(:tidy?).with(@basepath).returns true @tidy.expects(:tidy?).with(@basepath+"/one").returns true @tidy.expects(:tidy?).with(@basepath+"/two").returns false file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") @tidy.expects(:mkfile).with(@basepath).returns file @tidy.expects(:mkfile).with(@basepath+"/one").returns file @tidy.generate end end describe "and determining whether a file matches provided glob patterns" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :recurse => 1 @tidy[:matches] = %w{*foo* *bar*} @stat = mock 'stat' @matcher = @tidy.parameter(:matches) end it "should always convert the globs to an array" do @matcher.value = "*foo*" @matcher.value.should == %w{*foo*} end it "should return true if any pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.must be_tidy("/file/yaybarness", @stat) end it "should return false if no pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.should_not be_tidy("/file/yayness", @stat) end end describe "and determining whether a file is too old" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat' @tidy[:age] = "1s" @tidy[:type] = "mtime" @ager = @tidy.parameter(:age) end it "should use the age type specified" do @tidy[:type] = :ctime @stat.expects(:ctime).returns(Time.now) @ager.tidy?(@basepath, @stat) end it "should return false if the file is more recent than the specified age" do @stat.expects(:mtime).returns(Time.now) @ager.should_not be_tidy(@basepath, @stat) end it "should return true if the file is older than the specified age" do @stat.expects(:mtime).returns(Time.now - 10) @ager.must be_tidy(@basepath, @stat) end end describe "and determining whether a file is too large" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" @tidy[:size] = "1kb" @sizer = @tidy.parameter(:size) end it "should return false if the file is smaller than the specified size" do @stat.expects(:size).returns(4) # smaller than a kilobyte @sizer.should_not be_tidy(@basepath, @stat) end it "should return true if the file is larger than the specified size" do @stat.expects(:size).returns(1500) # larger than a kilobyte @sizer.must be_tidy(@basepath, @stat) end it "should return true if the file is equal to the specified size" do @stat.expects(:size).returns(1024) @sizer.must be_tidy(@basepath, @stat) end end describe "and determining whether a file should be tidied" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" File.stubs(:lstat).with(@basepath).returns @stat end it "should not try to recurse if the file does not exist" do @tidy[:recurse] = true File.stubs(:lstat).with(@basepath).returns nil @tidy.generate.should == [] end it "should not be tidied if the file does not exist" do File.expects(:lstat).with(@basepath).raises Errno::ENOENT @tidy.should_not be_tidy(@basepath) end it "should not be tidied if the user has no access to the file" do File.expects(:lstat).with(@basepath).raises Errno::EACCES @tidy.should_not be_tidy(@basepath) end it "should not be tidied if it is a directory and rmdirs is set to false" do stat = mock 'stat', :ftype => "directory" File.expects(:lstat).with(@basepath).returns stat @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match any provided globs" do @tidy[:recurse] = 1 @tidy[:matches] = "globs" matches = @tidy.parameter(:matches) matches.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match aging requirements" do @tidy[:age] = "1d" ager = @tidy.parameter(:age) ager.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match size requirements" do @tidy[:size] = "1b" sizer = @tidy.parameter(:size) sizer.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should tidy a file if age and size are set but only size matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns true @tidy.parameter(:age).stubs(:tidy?).returns false @tidy.should be_tidy(@basepath) end it "should tidy a file if age and size are set but only age matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns false @tidy.parameter(:age).stubs(:tidy?).returns true @tidy.should be_tidy(@basepath) end it "should tidy all files if neither age nor size is set" do @tidy.must be_tidy(@basepath) end it "should sort the results inversely by path length, so files are added to the catalog before their directories" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = Puppet::FileServing::Fileset.new(@basepath) Puppet::FileServing::Fileset.expects(:new).returns fileset fileset.expects(:files).returns %w{. one one/two} @tidy.stubs(:tidy?).returns true @tidy.generate.collect { |r| r[:path] }.should == [@basepath+"/one/two", @basepath+"/one", @basepath] end end it "should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = mock 'fileset' Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns fileset fileset.expects(:files).returns %w{. one two one/subone two/subtwo one/subone/ssone} @tidy.stubs(:tidy?).returns true result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash } { @basepath => [ @basepath+"/one", @basepath+"/two" ], @basepath+"/one" => [@basepath+"/one/subone"], @basepath+"/two" => [@basepath+"/two/subtwo"], @basepath+"/one/subone" => [@basepath+"/one/subone/ssone"] }.each do |parent, children| children.each do |child| ref = Puppet::Resource.new(:file, child) result[parent][:require].find { |req| req.to_s == ref.to_s }.should_not be_nil end end end end end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index 6421e6049..71c9e1857 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -1,336 +1,335 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' user = Puppet::Type.type(:user) describe user do before do ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil end it "should have a default provider inheriting from Puppet::Provider" do user.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should be able to create a instance" do user.new(:name => "foo").should_not be_nil end it "should have an allows_duplicates feature" do user.provider_feature(:allows_duplicates).should_not be_nil end it "should have an manages_homedir feature" do user.provider_feature(:manages_homedir).should_not be_nil end it "should have an manages_passwords feature" do user.provider_feature(:manages_passwords).should_not be_nil end it "should have a manages_solaris_rbac feature" do user.provider_feature(:manages_solaris_rbac).should_not be_nil end it "should have a manages_expiry feature" do user.provider_feature(:manages_expiry).should_not be_nil end it "should have a manages_password_age feature" do user.provider_feature(:manages_password_age).should_not be_nil end it "should have a system_users feature" do user.provider_feature(:system_users).should_not be_nil end describe "instances" do it "should have a valid provider" do user.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) end it "should delegate existence questions to its provider" do instance = user.new(:name => "foo") instance.provider.expects(:exists?).returns "eh" instance.exists?.should == "eh" end end properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :password_min_age, :password_max_age, :groups, :roles, :auths, :profiles, :project, :keys, :expiry] properties.each do |property| it "should have a #{property} property" do user.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do user.attrclass(property).doc.should be_instance_of(String) end end list_properties = [:groups, :roles, :auths] list_properties.each do |property| it "should have a list '#{property}'" do user.attrclass(property).ancestors.should be_include(Puppet::Property::List) end end it "should have an ordered list 'profiles'" do user.attrclass(:profiles).ancestors.should be_include(Puppet::Property::OrderedList) end it "should have key values 'keys'" do user.attrclass(:keys).ancestors.should be_include(Puppet::Property::KeyValue) end describe "when retrieving all current values" do before do @user = user.new(:name => "foo", :uid => 10) end it "should return a hash containing values for all set properties" do @user[:gid] = 10 @user.property(:ensure).expects(:retrieve).returns :present @user.property(:uid).expects(:retrieve).returns 15 @user.property(:gid).expects(:retrieve).returns 15 values = @user.retrieve [@user.property(:uid), @user.property(:gid)].each { |property| values.should be_include(property) } end it "should set all values to :absent if the user is absent" do @user.property(:ensure).expects(:retrieve).returns :absent @user.property(:uid).expects(:retrieve).never @user.retrieve[@user.property(:uid)].should == :absent end it "should include the result of retrieving each property's current value if the user is present" do @user.property(:ensure).expects(:retrieve).returns :present @user.property(:uid).expects(:retrieve).returns 15 @user.retrieve[@user.property(:uid)].should == 15 end end describe "when managing the ensure property" do before do @ensure = user.attrclass(:ensure).new(:resource => @resource) end it "should support a :present value" do lambda { @ensure.should = :present }.should_not raise_error end it "should support an :absent value" do lambda { @ensure.should = :absent }.should_not raise_error end it "should call :create on the provider when asked to sync to the :present state" do @provider.expects(:create) @ensure.should = :present @ensure.sync end it "should call :delete on the provider when asked to sync to the :absent state" do @provider.expects(:delete) @ensure.should = :absent @ensure.sync end describe "and determining the current state" do it "should return :present when the provider indicates the user exists" do @provider.expects(:exists?).returns true @ensure.retrieve.should == :present end it "should return :absent when the provider indicates the user does not exist" do @provider.expects(:exists?).returns false @ensure.retrieve.should == :absent end end end describe "when managing the uid property" do it "should convert number-looking strings into actual numbers" do uid = user.attrclass(:uid).new(:resource => @resource) uid.should = "50" uid.should.must == 50 end it "should support UIDs as numbers" do uid = user.attrclass(:uid).new(:resource => @resource) uid.should = 50 uid.should.must == 50 end it "should :absent as a value" do uid = user.attrclass(:uid).new(:resource => @resource) uid.should = :absent uid.should.must == :absent end end describe "when managing the gid" do it "should :absent as a value" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = :absent gid.should.must == :absent end it "should convert number-looking strings into actual numbers" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = "50" gid.should.must == 50 end it "should support GIDs specified as integers" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = 50 gid.should.must == 50 end it "should support groups specified by name" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = "foo" gid.should.must == "foo" end describe "when testing whether in sync" do before do @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) end it "should return true if no 'should' values are set" do @gid = user.attrclass(:gid).new(:resource => @resource) @gid.must be_safe_insync(500) end it "should return true if any of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 @gid.must be_safe_insync(500) end it "should return false if none of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 @gid.should_not be_safe_insync(700) end end describe "when syncing" do before do @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) end it "should use the first found, specified group as the desired value and send it to the provider" do Puppet::Util.expects(:gid).with("foo").returns nil Puppet::Util.expects(:gid).with("bar").returns 500 @provider.expects(:gid=).with 500 @gid.sync end end end describe "when managing expiry" do before do @expiry = user.attrclass(:expiry).new(:resource => @resource) end it "should fail if given an invalid date" do lambda { @expiry.should = "200-20-20" }.should raise_error(Puppet::Error) end end describe "when managing minimum password age" do before do @age = user.attrclass(:password_min_age).new(:resource => @resource) end it "should accept a negative minimum age" do expect { @age.should = -1 }.should_not raise_error end it "should fail with an empty minimum age" do expect { @age.should = '' }.should raise_error(Puppet::Error) end end describe "when managing maximum password age" do before do @age = user.attrclass(:password_max_age).new(:resource => @resource) end it "should accept a negative maximum age" do expect { @age.should = -1 }.should_not raise_error end it "should fail with an empty maximum age" do expect { @age.should = '' }.should raise_error(Puppet::Error) end end describe "when managing passwords" do before do @password = user.attrclass(:password).new(:resource => @resource, :should => "mypass") end it "should not include the password in the change log when adding the password" do @password.change_to_s(:absent, "mypass").should_not be_include("mypass") end it "should not include the password in the change log when changing the password" do @password.change_to_s("other", "mypass").should_not be_include("mypass") end it "should fail if a ':' is included in the password" do lambda { @password.should = "some:thing" }.should raise_error(Puppet::Error) end it "should allow the value to be set to :absent" do lambda { @password.should = :absent }.should_not raise_error end end describe "when manages_solaris_rbac is enabled" do before do @provider.stubs(:satisfies?).returns(false) @provider.expects(:satisfies?).with([:manages_solaris_rbac]).returns(true) end it "should support a :role value for ensure" do @ensure = user.attrclass(:ensure).new(:resource => @resource) lambda { @ensure.should = :role }.should_not raise_error end end describe "when user has roles" do before do # To test this feature, we have to support it. user.new(:name => "foo").provider.class.stubs(:feature?).returns(true) end it "should autorequire roles" do testuser = Puppet::Type.type(:user).new(:name => "testuser") testuser[:roles] = "testrole" testrole = Puppet::Type.type(:user).new(:name => "testrole") config = Puppet::Resource::Catalog.new :testing do |conf| [testuser, testrole].each { |resource| conf.add_resource resource } end Puppet::Type::User::ProviderDirectoryservice.stubs(:get_macosx_version_major).returns "10.5" rel = testuser.autorequire[0] rel.source.ref.should == testrole.ref rel.target.ref.should == testuser.ref end end end diff --git a/spec/unit/type/vlan_spec.rb b/spec/unit/type/vlan_spec.rb new file mode 100755 index 000000000..2983a58e9 --- /dev/null +++ b/spec/unit/type/vlan_spec.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type.type(:vlan) do + it "should have a 'name' parameter'" do + Puppet::Type.type(:vlan).new(:name => "200")[:name].should == "200" + end + + it "should have a 'device_url' parameter'" do + Puppet::Type.type(:vlan).new(:name => "200", :device_url => :device)[:device_url].should == :device + end + + it "should have an ensure property" do + Puppet::Type.type(:vlan).attrtype(:ensure).should == :property + end + + it "should have a description property" do + Puppet::Type.type(:vlan).attrtype(:description).should == :property + end + + describe "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type.type(:vlan).defaultprovider, :clear => nil + Puppet::Type.type(:vlan).defaultprovider.stubs(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type.type(:vlan).new(:name => "200", :ensure => :present) + end + + it "should support :absent as a value to :ensure" do + Puppet::Type.type(:vlan).new(:name => "200", :ensure => :absent) + end + + it "should fail if vlan name is not a number" do + lambda { Puppet::Type.type(:vlan).new(:name => "notanumber", :ensure => :present) }.should raise_error + end + end +end diff --git a/spec/unit/type/whit_spec.rb b/spec/unit/type/whit_spec.rb old mode 100644 new mode 100755 index 0a3324afa..4d0949900 --- a/spec/unit/type/whit_spec.rb +++ b/spec/unit/type/whit_spec.rb @@ -1,11 +1,10 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' whit = Puppet::Type.type(:whit).new(:name => "Foo::Bar") describe whit do it "should stringify in a way that users will regognise" do whit.to_s.should == "(Foo::Bar)" end end diff --git a/spec/unit/type/zfs_spec.rb b/spec/unit/type/zfs_spec.rb index 88193fca3..e53c25e02 100755 --- a/spec/unit/type/zfs_spec.rb +++ b/spec/unit/type/zfs_spec.rb @@ -1,45 +1,44 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' zfs = Puppet::Type.type(:zfs) describe zfs do properties = [:ensure, :mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir] properties.each do |property| it "should have a #{property} property" do zfs.attrclass(property).ancestors.should be_include(Puppet::Property) end end parameters = [:name] parameters.each do |parameter| it "should have a #{parameter} parameter" do zfs.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end end it "should autorequire the containing zfss and the zpool" do provider = mock "provider" provider.stubs(:name).returns(:solaris) zfs.stubs(:defaultprovider).returns(provider) Puppet::Type.type(:zpool).stubs(:defaultprovider).returns(provider) foo_pool = Puppet::Type.type(:zpool).new(:name => "foo") foo_bar_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar") foo_bar_baz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz") foo_bar_baz_buz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz/buz") config = Puppet::Resource::Catalog.new :testing do |conf| [foo_pool, foo_bar_zfs, foo_bar_baz_zfs, foo_bar_baz_buz_zfs].each { |resource| conf.add_resource resource } end req = foo_bar_baz_buz_zfs.autorequire.collect { |edge| edge.source.ref } [foo_pool.ref, foo_bar_zfs.ref, foo_bar_baz_zfs.ref].each { |ref| req.include?(ref).should == true } end end diff --git a/spec/unit/type/zone_spec.rb b/spec/unit/type/zone_spec.rb index e479fb1e9..eb3d33bb0 100755 --- a/spec/unit/type/zone_spec.rb +++ b/spec/unit/type/zone_spec.rb @@ -1,80 +1,79 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' zone = Puppet::Type.type(:zone) describe zone do before do zone = Puppet::Type.type(:zone) provider = stub 'provider' provider.stubs(:name).returns(:solaris) zone.stubs(:defaultprovider).returns(provider) resource = stub 'resource', :resource => nil, :provider => provider, :line => nil, :file => nil end parameters = [:create_args, :install_args, :sysidcfg, :path, :realhostname] parameters.each do |parameter| it "should have a #{parameter} parameter" do zone.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end end properties = [:ip, :iptype, :autoboot, :pool, :shares, :inherit] properties.each do |property| it "should have a #{property} property" do zone.attrclass(property).ancestors.should be_include(Puppet::Property) end end it "should be invalid when :path is missing" do lambda { zone.new(:name => "dummy") }.should raise_error end it "should be invalid when :ip is missing a \":\" and iptype is :shared" do lambda { zone.new(:name => "dummy", :ip => "if") }.should raise_error end it "should be invalid when :ip has a \":\" and iptype is :exclusive" do lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4", :iptype => :exclusive) }.should raise_error end it "should be invalid when :ip has two \":\" and iptype is :exclusive" do lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4:2.3.4.5", :iptype => :exclusive) }.should raise_error end it "should be valid when :iptype is :shared and using interface and ip" do zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4") end it "should be valid when :iptype is :shared and using interface, ip and default route" do zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4:2.3.4.5") end it "should be valid when :iptype is :exclusive and using interface" do zone.new(:name => "dummy", :path => "/dummy", :ip => "if", :iptype => :exclusive) end it "should auto-require :dataset entries" do fs = 'random-pool/some-zfs' # ick provider = stub 'zfs::provider' provider.stubs(:name).returns(:solaris) Puppet::Type.type(:zfs).stubs(:defaultprovider).returns(provider) catalog = Puppet::Resource::Catalog.new zfs_instance = Puppet::Type.type(:zfs).new(:name => fs) catalog.add_resource zfs_instance zone_instance = zone.new(:name => "dummy", :path => "/foo", :ip => 'en1:1.0.0.0', :dataset => fs) catalog.add_resource zone_instance catalog.relationship_graph.dependencies(zone_instance).should == [zfs_instance] end end diff --git a/spec/unit/type/zpool_spec.rb b/spec/unit/type/zpool_spec.rb index 96e7d548c..9f1800073 100755 --- a/spec/unit/type/zpool_spec.rb +++ b/spec/unit/type/zpool_spec.rb @@ -1,110 +1,109 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' zpool = Puppet::Type.type(:zpool) describe zpool do before do @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil end properties = [:ensure, :disk, :mirror, :raidz, :spare, :log] properties.each do |property| it "should have a #{property} property" do zpool.attrclass(property).ancestors.should be_include(Puppet::Property) end end parameters = [:pool, :raid_parity] parameters.each do |parameter| it "should have a #{parameter} parameter" do zpool.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end end end vdev_property = Puppet::Property::VDev describe vdev_property do before do vdev_property.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = vdev_property.new(:resource => @resource) end it "should be insync if the devices are the same" do @property.should = ["dev1 dev2"] @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the devices are not the same" do @property.should = ["dev1 dev3"] @property.safe_insync?(["dev2 dev1"]).must be_false end it "should be insync if the devices are the same and the should values are comma seperated" do @property.should = ["dev1", "dev2"] @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the device is absent and should has a value" do @property.should = ["dev1", "dev2"] @property.safe_insync?(:absent).must be_false end it "should be insync if the device is absent and should is absent" do @property.should = [:absent] @property.safe_insync?(:absent).must be_true end end multi_vdev_property = Puppet::Property::MultiVDev describe multi_vdev_property do before do multi_vdev_property.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = multi_vdev_property.new(:resource => @resource) end it "should be insync if the devices are the same" do @property.should = ["dev1 dev2"] @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the devices are not the same" do @property.should = ["dev1 dev3"] @property.safe_insync?(["dev2 dev1"]).must be_false end it "should be out of sync if the device is absent and should has a value" do @property.should = ["dev1", "dev2"] @property.safe_insync?(:absent).must be_false end it "should be insync if the device is absent and should is absent" do @property.should = [:absent] @property.safe_insync?(:absent).must be_true end describe "when there are multiple lists of devices" do it "should be in sync if each group has the same devices" do @property.should = ["dev1 dev2", "dev3 dev4"] @property.safe_insync?(["dev2 dev1", "dev3 dev4"]).must be_true end it "should be out of sync if any group has the different devices" do @property.should = ["dev1 devX", "dev3 dev4"] @property.safe_insync?(["dev2 dev1", "dev3 dev4"]).must be_false end it "should be out of sync if devices are in the wrong group" do @property.should = ["dev1 dev2", "dev3 dev4"] @property.safe_insync?(["dev2 dev3", "dev1 dev4"]).must be_false end end end diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb index f9372fced..ca291c4fa 100755 --- a/spec/unit/type_spec.rb +++ b/spec/unit/type_spec.rb @@ -1,584 +1,583 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Type do it "should include the Cacher module" do Puppet::Type.ancestors.should be_include(Puppet::Util::Cacher) end it "should consider a parameter to be valid if it is a valid parameter" do Puppet::Type.type(:mount).should be_valid_parameter(:path) end it "should consider a parameter to be valid if it is a valid property" do Puppet::Type.type(:mount).should be_valid_parameter(:fstype) end it "should consider a parameter to be valid if it is a valid metaparam" do Puppet::Type.type(:mount).should be_valid_parameter(:noop) end it "should use its catalog as its expirer" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.catalog = catalog resource.expirer.should equal(catalog) end it "should do nothing when asked to expire when it has no catalog" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) lambda { resource.expire }.should_not raise_error end it "should be able to retrieve a property by name" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.property(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) end it "should be able to retrieve a parameter by name" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.parameter(:name).must be_instance_of(Puppet::Type.type(:mount).attrclass(:name)) end it "should be able to retrieve a property by name using the :parameter method" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.parameter(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) end it "should be able to retrieve all set properties" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) props = resource.properties props.should_not be_include(nil) [:fstype, :ensure, :pass].each do |name| props.should be_include(resource.parameter(name)) end end it "should have a method for setting default values for resources" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:set_default) end it "should do nothing for attributes that have no defaults and no specified value" do Puppet::Type.type(:mount).new(:name => "foo").parameter(:noop).should be_nil end it "should have a method for adding tags" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:tags) end it "should use the tagging module" do Puppet::Type.type(:mount).ancestors.should be_include(Puppet::Util::Tagging) end it "should delegate to the tagging module when tags are added" do resource = Puppet::Type.type(:mount).new(:name => "foo") resource.stubs(:tag).with(:mount) resource.expects(:tag).with(:tag1, :tag2) resource.tags = [:tag1,:tag2] end it "should add the current type as tag" do resource = Puppet::Type.type(:mount).new(:name => "foo") resource.stubs(:tag) resource.expects(:tag).with(:mount) resource.tags = [:tag1,:tag2] end it "should have a method to know if the resource is exported" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:exported?) end it "should have a method to know if the resource is virtual" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:virtual?) end it "should consider its version to be its catalog version" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.version = 50 catalog.add_resource resource resource.version.should == 50 end it "should consider its version to be zero if it has no catalog" do Puppet::Type.type(:mount).new(:name => "foo").version.should == 0 end it "should provide source_descriptors" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.version = 50 catalog.add_resource resource resource.source_descriptors.should == {:tags=>["mount", "foo"], :path=>"/Mount[foo]"} end it "should consider its type to be the name of its class" do Puppet::Type.type(:mount).new(:name => "foo").type.should == :mount end it "should use any provided noop value" do Puppet::Type.type(:mount).new(:name => "foo", :noop => true).must be_noop end it "should use the global noop value if none is provided" do Puppet[:noop] = true Puppet::Type.type(:mount).new(:name => "foo").must be_noop end it "should not be noop if in a non-host_config catalog" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource resource.should_not be_noop end describe "when creating an event" do before do @resource = Puppet::Type.type(:mount).new :name => "foo" end it "should have the resource's reference as the resource" do @resource.event.resource.should == "Mount[foo]" end it "should have the resource's log level as the default log level" do @resource[:loglevel] = :warning @resource.event.default_log_level.should == :warning end {:file => "/my/file", :line => 50, :tags => %{foo bar}}.each do |attr, value| it "should set the #{attr}" do @resource.stubs(attr).returns value @resource.event.send(attr).should == value end end it "should allow specification of event attributes" do @resource.event(:status => "noop").status.should == "noop" end end describe "when choosing a default provider" do it "should choose the provider with the highest specificity" do # Make a fake type type = Puppet::Type.newtype(:defaultprovidertest) do newparam(:name) do end end basic = type.provide(:basic) {} greater = type.provide(:greater) {} basic.stubs(:specificity).returns 1 greater.stubs(:specificity).returns 2 type.defaultprovider.should equal(greater) end end describe "when initializing" do describe "and passed a TransObject" do it "should fail" do trans = Puppet::TransObject.new("/foo", :mount) lambda { Puppet::Type.type(:mount).new(trans) }.should raise_error(Puppet::DevError) end end describe "and passed a Puppet::Resource instance" do it "should set its title to the title of the resource if the resource type is equal to the current type" do resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "/other"}) Puppet::Type.type(:mount).new(resource).title.should == "/foo" end it "should set its title to the resource reference if the resource type is not equal to the current type" do resource = Puppet::Resource.new(:user, "foo") Puppet::Type.type(:mount).new(resource).title.should == "User[foo]" end [:line, :file, :catalog, :exported, :virtual].each do |param| it "should copy '#{param}' from the resource if present" do resource = Puppet::Resource.new(:mount, "/foo") resource.send(param.to_s + "=", "foo") resource.send(param.to_s + "=", "foo") Puppet::Type.type(:mount).new(resource).send(param).should == "foo" end end it "should copy any tags from the resource" do resource = Puppet::Resource.new(:mount, "/foo") resource.tag "one", "two" tags = Puppet::Type.type(:mount).new(resource).tags tags.should be_include("one") tags.should be_include("two") end it "should copy the resource's parameters as its own" do resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:atboot => true, :fstype => "boo"}) params = Puppet::Type.type(:mount).new(resource).to_hash params[:fstype].should == "boo" params[:atboot].should == true end end describe "and passed a Hash" do it "should extract the title from the hash" do Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" end it "should work when hash keys are provided as strings" do Puppet::Type.type(:mount).new("title" => "/yay").title.should == "/yay" end it "should work when hash keys are provided as symbols" do Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" end it "should use the name from the hash as the title if no explicit title is provided" do Puppet::Type.type(:mount).new(:name => "/yay").title.should == "/yay" end it "should use the Resource Type's namevar to determine how to find the name in the hash" do Puppet::Type.type(:file).new(:path => "/yay").title.should == "/yay" end [:catalog].each do |param| it "should extract '#{param}' from the hash if present" do Puppet::Type.type(:mount).new(:name => "/yay", param => "foo").send(param).should == "foo" end end it "should use any remaining hash keys as its parameters" do resource = Puppet::Type.type(:mount).new(:title => "/foo", :catalog => "foo", :atboot => true, :fstype => "boo") resource[:fstype].must == "boo" resource[:atboot].must == true end end it "should fail if any invalid attributes have been provided" do lambda { Puppet::Type.type(:mount).new(:title => "/foo", :nosuchattr => "whatever") }.should raise_error(Puppet::Error) end it "should set its name to the resource's title if the resource does not have a :name or namevar parameter set" do resource = Puppet::Resource.new(:mount, "/foo") Puppet::Type.type(:mount).new(resource).name.should == "/foo" end it "should fail if no title, name, or namevar are provided" do lambda { Puppet::Type.type(:file).new(:atboot => true) }.should raise_error(Puppet::Error) end it "should set the attributes in the order returned by the class's :allattrs method" do Puppet::Type.type(:mount).stubs(:allattrs).returns([:name, :atboot, :noop]) resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot", :noop => "whatever"}) set = [] Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| set << param true end.returns(stub_everything("a property")) Puppet::Type.type(:mount).new(resource) set[-1].should == :noop set[-2].should == :atboot end it "should always set the name and then default provider before anything else" do Puppet::Type.type(:mount).stubs(:allattrs).returns([:provider, :name, :atboot]) resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot"}) set = [] Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| set << param true end.returns(stub_everything("a property")) Puppet::Type.type(:mount).new(resource) set[0].should == :name set[1].should == :provider end # This one is really hard to test :/ it "should each default immediately if no value is provided" do defaults = [] Puppet::Type.type(:package).any_instance.stubs(:set_default).with { |value| defaults << value; true } Puppet::Type.type(:package).new :name => "whatever" defaults[0].should == :provider end it "should retain a copy of the originally provided parameters" do Puppet::Type.type(:mount).new(:name => "foo", :atboot => true, :noop => false).original_parameters.should == {:atboot => true, :noop => false} end it "should delete the name via the namevar from the originally provided parameters" do Puppet::Type.type(:file).new(:name => "/foo").original_parameters[:path].should be_nil end end it "should have a class method for converting a hash into a Puppet::Resource instance" do Puppet::Type.type(:mount).must respond_to(:hash2resource) end describe "when converting a hash to a Puppet::Resource instance" do before do @type = Puppet::Type.type(:mount) end it "should treat a :title key as the title of the resource" do @type.hash2resource(:name => "/foo", :title => "foo").title.should == "foo" end it "should use the name from the hash as the title if no explicit title is provided" do @type.hash2resource(:name => "foo").title.should == "foo" end it "should use the Resource Type's namevar to determine how to find the name in the hash" do @type.stubs(:key_attributes).returns([ :myname ]) @type.hash2resource(:myname => "foo").title.should == "foo" end [:catalog].each do |attr| it "should use any provided #{attr}" do @type.hash2resource(:name => "foo", attr => "eh").send(attr).should == "eh" end end it "should set all provided parameters on the resource" do @type.hash2resource(:name => "foo", :fstype => "boo", :boot => "fee").to_hash.should == {:name => "foo", :fstype => "boo", :boot => "fee"} end it "should not set the title as a parameter on the resource" do @type.hash2resource(:name => "foo", :title => "eh")[:title].should be_nil end it "should not set the catalog as a parameter on the resource" do @type.hash2resource(:name => "foo", :catalog => "eh")[:catalog].should be_nil end it "should treat hash keys equivalently whether provided as strings or symbols" do resource = @type.hash2resource("name" => "foo", "title" => "eh", "fstype" => "boo") resource.title.should == "eh" resource[:name].should == "foo" resource[:fstype].should == "boo" end end describe "when retrieving current property values" do before do @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) @resource.property(:ensure).stubs(:retrieve).returns :absent end it "should fail if its provider is unsuitable" do @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) @resource.provider.class.expects(:suitable?).returns false lambda { @resource.retrieve_resource }.should raise_error(Puppet::Error) end it "should return a Puppet::Resource instance with its type and title set appropriately" do result = @resource.retrieve_resource result.should be_instance_of(Puppet::Resource) result.type.should == "Mount" result.title.should == "foo" end it "should set the name of the returned resource if its own name and title differ" do @resource[:name] = "my name" @resource.title = "other name" @resource.retrieve_resource[:name].should == "my name" end it "should provide a value for all set properties" do values = @resource.retrieve_resource [:ensure, :fstype, :pass].each { |property| values[property].should_not be_nil } end it "should provide a value for 'ensure' even if no desired value is provided" do @resource = Puppet::Type.type(:file).new(:path => "/my/file/that/can't/exist") end it "should not call retrieve on non-ensure properties if the resource is absent and should consider the property absent" do @resource.property(:ensure).expects(:retrieve).returns :absent @resource.property(:fstype).expects(:retrieve).never @resource.retrieve_resource[:fstype].should == :absent end it "should include the result of retrieving each property's current value if the resource is present" do @resource.property(:ensure).expects(:retrieve).returns :present @resource.property(:fstype).expects(:retrieve).returns 15 @resource.retrieve_resource[:fstype] == 15 end end describe ".title_patterns" do describe "when there's one namevar" do before do @type_class = Puppet::Type.type(:notify) @type_class.stubs(:key_attributes).returns([:one]) end it "should have a default pattern for when there's one namevar" do patterns = @type_class.title_patterns patterns.length.should == 1 patterns[0].length.should == 2 end it "should have a regexp that captures the entire string" do patterns = @type_class.title_patterns string = "abc\n\tdef" patterns[0][0] =~ string $1.should == "abc\n\tdef" end end end describe "when in a catalog" do before do @catalog = Puppet::Resource::Catalog.new @container = Puppet::Type.type(:component).new(:name => "container") @one = Puppet::Type.type(:file).new(:path => "/file/one") @two = Puppet::Type.type(:file).new(:path => "/file/two") @catalog.add_resource @container @catalog.add_resource @one @catalog.add_resource @two @catalog.add_edge @container, @one @catalog.add_edge @container, @two end it "should have no parent if there is no in edge" do @container.parent.should be_nil end it "should set its parent to its in edge" do @one.parent.ref.should == @container.ref end after do @catalog.clear(true) end end it "should have a 'stage' metaparam" do Puppet::Type.metaparamclass(:stage).should be_instance_of(Class) end end describe Puppet::Type::RelationshipMetaparam do it "should be a subclass of Puppet::Parameter" do Puppet::Type::RelationshipMetaparam.superclass.should equal(Puppet::Parameter) end it "should be able to produce a list of subclasses" do Puppet::Type::RelationshipMetaparam.should respond_to(:subclasses) end describe "when munging relationships" do before do @resource = Puppet::Type.type(:mount).new :name => "/foo" @metaparam = Puppet::Type.metaparamclass(:require).new :resource => @resource end it "should accept Puppet::Resource instances" do ref = Puppet::Resource.new(:file, "/foo") @metaparam.munge(ref)[0].should equal(ref) end it "should turn any string into a Puppet::Resource" do @metaparam.munge("File[/ref]")[0].should be_instance_of(Puppet::Resource) end end it "should be able to validate relationships" do Puppet::Type.metaparamclass(:require).new(:resource => mock("resource")).should respond_to(:validate_relationship) end it "should fail if any specified resource is not found in the catalog" do catalog = mock 'catalog' resource = stub 'resource', :catalog => catalog, :ref => "resource" param = Puppet::Type.metaparamclass(:require).new(:resource => resource, :value => %w{Foo[bar] Class[test]}) catalog.expects(:resource).with("Foo[bar]").returns "something" catalog.expects(:resource).with("Class[Test]").returns nil param.expects(:fail).with { |string| string.include?("Class[Test]") } param.validate_relationship end end describe Puppet::Type.metaparamclass(:check) do it "should warn and create an instance of ':audit'" do file = Puppet::Type.type(:file).new :path => "/foo" file.expects(:warning) file[:check] = :mode file[:audit].should == [:mode] end end describe Puppet::Type.metaparamclass(:audit) do before do @resource = Puppet::Type.type(:file).new :path => "/foo" end it "should default to being nil" do @resource[:audit].should be_nil end it "should specify all possible properties when asked to audit all properties" do @resource[:audit] = :all list = @resource.class.properties.collect { |p| p.name } @resource[:audit].should == list end it "should accept the string 'all' to specify auditing all possible properties" do @resource[:audit] = 'all' list = @resource.class.properties.collect { |p| p.name } @resource[:audit].should == list end it "should fail if asked to audit an invalid property" do lambda { @resource[:audit] = :foobar }.should raise_error(Puppet::Error) end it "should create an attribute instance for each auditable property" do @resource[:audit] = :mode @resource.parameter(:mode).should_not be_nil end it "should accept properties specified as a string" do @resource[:audit] = "mode" @resource.parameter(:mode).should_not be_nil end it "should not create attribute instances for parameters, only properties" do @resource[:audit] = :noop @resource.parameter(:noop).should be_nil end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:path, :mode, :owner] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) res = Puppet::Type.type(:file).new( :title => '/my/file', :path => '/my/file', :owner => 'root', :content => 'hello' ) res.uniqueness_key.should == [ nil, 'root', '/my/file'] end end end diff --git a/spec/unit/util/autoload/file_cache_spec.rb b/spec/unit/util/autoload/file_cache_spec.rb index 5ad820e4c..cdde9bb48 100755 --- a/spec/unit/util/autoload/file_cache_spec.rb +++ b/spec/unit/util/autoload/file_cache_spec.rb @@ -1,159 +1,158 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/autoload/file_cache' class FileCacheTester include Puppet::Util::Autoload::FileCache end describe Puppet::Util::Autoload::FileCache do before do @cacher = FileCacheTester.new end after do Puppet::Util::Autoload::FileCache.clear end describe "when checking whether files exist" do it "should have a method for testing whether a file exists" do @cacher.should respond_to(:file_exist?) end it "should use lstat to determine whether a file exists" do File.expects(:lstat).with("/my/file") @cacher.file_exist?("/my/file") end it "should consider a file as absent if its lstat fails" do File.expects(:lstat).with("/my/file").raises Errno::ENOENT @cacher.should_not be_file_exist("/my/file") end it "should consider a file as absent if the directory is absent" do File.expects(:lstat).with("/my/file").raises Errno::ENOTDIR @cacher.should_not be_file_exist("/my/file") end it "should consider a file as absent permissions are missing" do File.expects(:lstat).with("/my/file").raises Errno::EACCES @cacher.should_not be_file_exist("/my/file") end it "should raise non-fs exceptions" do File.expects(:lstat).with("/my/file").raises ArgumentError lambda { @cacher.file_exist?("/my/file") }.should raise_error(ArgumentError) end it "should consider a file as present if its lstat succeeds" do File.expects(:lstat).with("/my/file").returns mock("stat") @cacher.should be_file_exist("/my/file") end it "should not stat a file twice in quick succession when the file is missing" do File.expects(:lstat).with("/my/file").once.raises Errno::ENOENT @cacher.should_not be_file_exist("/my/file") @cacher.should_not be_file_exist("/my/file") end it "should not stat a file twice in quick succession when the file is present" do File.expects(:lstat).with("/my/file").once.returns mock("stat") @cacher.should be_file_exist("/my/file") @cacher.should be_file_exist("/my/file") end it "should expire cached data after 15 seconds" do now = Time.now later = now + 16 Time.expects(:now).times(3).returns(now).then.returns(later).then.returns(later) File.expects(:lstat).with("/my/file").times(2).returns(mock("stat")).then.raises Errno::ENOENT @cacher.should be_file_exist("/my/file") @cacher.should_not be_file_exist("/my/file") end it "should share cached data across autoload instances" do File.expects(:lstat).with("/my/file").once.returns mock("stat") other = Puppet::Util::Autoload.new("bar", "tmp") @cacher.should be_file_exist("/my/file") other.should be_file_exist("/my/file") end end describe "when checking whether files exist" do before do @stat = stub 'stat', :directory? => true end it "should have a method for determining whether a directory exists" do @cacher.should respond_to(:directory_exist?) end it "should use lstat to determine whether a directory exists" do File.expects(:lstat).with("/my/file").returns @stat @cacher.directory_exist?("/my/file") end it "should consider a directory as absent if its lstat fails" do File.expects(:lstat).with("/my/file").raises Errno::ENOENT @cacher.should_not be_directory_exist("/my/file") end it "should consider a file as absent if the directory is absent" do File.expects(:lstat).with("/my/file").raises Errno::ENOTDIR @cacher.should_not be_directory_exist("/my/file") end it "should consider a file as absent permissions are missing" do File.expects(:lstat).with("/my/file").raises Errno::EACCES @cacher.should_not be_directory_exist("/my/file") end it "should raise non-fs exceptions" do File.expects(:lstat).with("/my/file").raises ArgumentError lambda { @cacher.directory_exist?("/my/file") }.should raise_error(ArgumentError) end it "should consider a directory as present if its lstat succeeds and the stat is of a directory" do @stat.expects(:directory?).returns true File.expects(:lstat).with("/my/file").returns @stat @cacher.should be_directory_exist("/my/file") end it "should consider a directory as absent if its lstat succeeds and the stat is not of a directory" do @stat.expects(:directory?).returns false File.expects(:lstat).with("/my/file").returns @stat @cacher.should_not be_directory_exist("/my/file") end it "should not stat a directory twice in quick succession when the file is missing" do File.expects(:lstat).with("/my/file").once.raises Errno::ENOENT @cacher.should_not be_directory_exist("/my/file") @cacher.should_not be_directory_exist("/my/file") end it "should not stat a directory twice in quick succession when the file is present" do File.expects(:lstat).with("/my/file").once.returns @stat @cacher.should be_directory_exist("/my/file") @cacher.should be_directory_exist("/my/file") end it "should not consider a file to be a directory based on cached data" do @stat.stubs(:directory?).returns false File.stubs(:lstat).with("/my/file").returns @stat @cacher.file_exist?("/my/file") @cacher.should_not be_directory_exist("/my/file") end it "should share cached data across autoload instances" do File.expects(:lstat).with("/my/file").once.returns @stat other = Puppet::Util::Autoload.new("bar", "tmp") @cacher.should be_directory_exist("/my/file") other.should be_directory_exist("/my/file") end end end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 808885dd7..6d49b57dc 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -1,120 +1,119 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/autoload' describe Puppet::Util::Autoload do before do @autoload = Puppet::Util::Autoload.new("foo", "tmp") @autoload.stubs(:eachdir).yields "/my/dir" end it "should use the Cacher module" do Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Cacher) end describe "when building the search path" do it "should collect all of the plugins and lib directories that exist in the current environment's module path" do Puppet.settings.expects(:value).with(:environment).returns "foo" Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a:/b:/c" Dir.expects(:entries).with("/a").returns %w{one two} Dir.expects(:entries).with("/b").returns %w{one two} FileTest.stubs(:directory?).returns false FileTest.expects(:directory?).with("/a").returns true FileTest.expects(:directory?).with("/b").returns true %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib}.each do |d| FileTest.expects(:directory?).with(d).returns true end @autoload.module_directories.should == %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib} end it "should not look for lib directories in directories starting with '.'" do Puppet.settings.expects(:value).with(:environment).returns "foo" Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a" Dir.expects(:entries).with("/a").returns %w{. ..} FileTest.expects(:directory?).with("/a").returns true FileTest.expects(:directory?).with("/a/./lib").never FileTest.expects(:directory?).with("/a/./plugins").never FileTest.expects(:directory?).with("/a/../lib").never FileTest.expects(:directory?).with("/a/../plugins").never @autoload.module_directories end it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do Puppet.stubs(:[]).with(:libdir).returns(%w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR)) @autoload.expects(:module_directories).returns %w{/one /two} @autoload.search_directories.should == %w{/one /two /libdir1 /lib/dir/two /third/lib/dir} + $LOAD_PATH end it "should include in its search path all of the search directories that have a subdirectory matching the autoload path" do @autoload = Puppet::Util::Autoload.new("foo", "loaddir") @autoload.expects(:search_directories).returns %w{/one /two /three} FileTest.expects(:directory?).with("/one/loaddir").returns true FileTest.expects(:directory?).with("/two/loaddir").returns false FileTest.expects(:directory?).with("/three/loaddir").returns true @autoload.searchpath.should == ["/one/loaddir", "/three/loaddir"] end end it "should include its FileCache module" do Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Autoload::FileCache) end describe "when loading a file" do before do @autoload.stubs(:searchpath).returns %w{/a} end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do @autoload.stubs(:file_exist?).returns true Kernel.expects(:load).raises error lambda { @autoload.load("foo") }.should raise_error(Puppet::Error) end end it "should not raise an error if the file is missing" do @autoload.load("foo").should == false end it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do @autoload.stubs(:file_exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") $LOADED_FEATURES.should be_include("tmp/myfile.rb") end end describe "when loading all files" do before do @autoload.stubs(:searchpath).returns %w{/a} Dir.stubs(:glob).returns "/path/to/file.rb" @autoload.class.stubs(:loaded?).returns(false) end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die an if a #{error.to_s} exception is thrown" do Kernel.expects(:require).raises error lambda { @autoload.loadall }.should raise_error(Puppet::Error) end end it "should require the full path to the file" do Kernel.expects(:require).with("/path/to/file.rb") @autoload.loadall end end end diff --git a/spec/unit/util/backups_spec.rb b/spec/unit/util/backups_spec.rb index 3d707a58c..611c19304 100755 --- a/spec/unit/util/backups_spec.rb +++ b/spec/unit/util/backups_spec.rb @@ -1,158 +1,157 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/backups' describe Puppet::Util::Backups do before do FileTest.stubs(:exists?).returns true end describe "when backing up a file" do it "should noop if the file does not exist" do FileTest.expects(:exists?).returns false file = Puppet::Type.type(:file).new(:name => '/no/such/file') file.expects(:bucket).never file.perform_backup end it "should succeed silently if self[:backup] is false" do file = Puppet::Type.type(:file).new(:name => '/no/such/file', :backup => false) file.expects(:bucket).never FileTest.expects(:exists?).never file.perform_backup end it "a bucket should be used when provided" do path = '/my/file' File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') bucket = stub('bucket', 'name' => 'foo') file.stubs(:bucket).returns bucket bucket.expects(:backup).with(path).returns("mysum") file.perform_backup end it "should propagate any exceptions encountered when backing up to a filebucket" do path = '/my/file' File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') bucket = stub('bucket', 'name' => 'foo') file.stubs(:bucket).returns bucket bucket.expects(:backup).raises ArgumentError lambda { file.perform_backup }.should raise_error(ArgumentError) end describe "and no filebucket is configured" do it "should remove any local backup if one exists" do path = '/my/file' FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") File.expects(:unlink).with(backup) FileUtils.stubs(:cp_r) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup end it "should fail when the old backup can't be removed" do path = '/my/file' FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") File.expects(:unlink).raises ArgumentError FileUtils.expects(:cp_r).never file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') lambda { file.perform_backup }.should raise_error(Puppet::Error) end it "should not try to remove backups that don't exist" do path = '/my/file' FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).raises(Errno::ENOENT) File.expects(:unlink).never FileUtils.stubs(:cp_r) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup end it "a copy should be created in the local directory" do path = '/my/file' FileTest.stubs(:exists?).with(path).returns true FileUtils.expects(:cp_r).with(path, path + ".foo", :preserve => true) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup.should be_true end it "should propagate exceptions if no backup can be created" do path = '/my/file' FileTest.stubs(:exists?).with(path).returns true FileUtils.expects(:cp_r).raises ArgumentError file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') lambda { file.perform_backup }.should raise_error(Puppet::Error) end end end describe "when backing up a directory" do it "a bucket should work when provided" do path = '/my/dir' File.stubs(:file?).returns true Find.expects(:find).with(path).yields("/my/dir/file") bucket = stub('bucket', :name => "eh") bucket.expects(:backup).with("/my/dir/file").returns true file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') file.stubs(:bucket).returns bucket File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) file.perform_backup end it "should do nothing when recursing" do path = '/my/dir' bucket = stub('bucket', :name => "eh") bucket.expects(:backup).never file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true) file.stubs(:bucket).returns bucket File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) Find.expects(:find).never file.perform_backup end end end diff --git a/spec/unit/util/cache_accumulator_spec.rb b/spec/unit/util/cache_accumulator_spec.rb old mode 100644 new mode 100755 index 4ed9abf5c..9c35cc353 --- a/spec/unit/util/cache_accumulator_spec.rb +++ b/spec/unit/util/cache_accumulator_spec.rb @@ -1,75 +1,74 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/rails/cache_accumulator' describe Puppet::Util::CacheAccumulator do before :each do @test_class = Class.new do attr_accessor :name include Puppet::Util::CacheAccumulator accumulates :name def initialize(n) self.name = n end end end it 'should delegate to underlying find_or_create_by_* method and accumulate results' do @test_class.expects(:find_or_create_by_name).with('foo').returns(@test_class.new('foo')).once obj = @test_class.accumulate_by_name('foo') obj.name.should == 'foo' @test_class.accumulate_by_name('foo').should == obj end it 'should delegate bulk lookups to find with appropriate arguments and returning result count' do @test_class.expects(:find).with( :all, :conditions => {:name => ['a', 'b', 'c']} ).returns(['a','b','c'].collect {|n| @test_class.new(n)}).once @test_class.accumulate_by_name('a', 'b', 'c').should == 3 end it 'should only need find_or_create_by_name lookup for missing bulk entries' do @test_class.expects(:find).with( :all, :conditions => {:name => ['a', 'b']} ).returns([ @test_class.new('a') ]).once @test_class.expects(:find_or_create_by_name).with('b').returns(@test_class.new('b')).once @test_class.expects(:find_or_create_by_name).with('a').never @test_class.accumulate_by_name('a','b').should == 1 @test_class.accumulate_by_name('a').name.should == 'a' @test_class.accumulate_by_name('b').name.should == 'b' end it 'should keep consumer classes separate' do @alt_class = Class.new do attr_accessor :name include Puppet::Util::CacheAccumulator accumulates :name def initialize(n) self.name = n end end name = 'foo' @test_class.expects(:find_or_create_by_name).with(name).returns(@test_class.new(name)).once @alt_class.expects(:find_or_create_by_name).with(name).returns(@alt_class.new(name)).once [@test_class, @alt_class].each do |klass| klass.accumulate_by_name(name).name.should == name klass.accumulate_by_name(name).class.should == klass end end it 'should clear accumulated cache with reset_*_accumulator' do # figure out how to test this appropriately... end end diff --git a/spec/unit/util/cacher_spec.rb b/spec/unit/util/cacher_spec.rb index 125382e84..2e43b4e20 100755 --- a/spec/unit/util/cacher_spec.rb +++ b/spec/unit/util/cacher_spec.rb @@ -1,185 +1,184 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/cacher' class ExpirerTest include Puppet::Util::Cacher::Expirer end class CacheTest @@init_count = 0 include Puppet::Util::Cacher cached_attr(:instance_cache) { Time.now } end describe Puppet::Util::Cacher::Expirer do before do @expirer = ExpirerTest.new end it "should be able to test whether a timestamp is expired" do @expirer.should respond_to(:dependent_data_expired?) end it "should be able to expire all values" do @expirer.should respond_to(:expire) end it "should consider any value to be valid if it has never been expired" do @expirer.should_not be_dependent_data_expired(Time.now) end it "should consider any value created after expiration to be expired" do @expirer.expire @expirer.should be_dependent_data_expired(Time.now - 1) end end describe Puppet::Util::Cacher do it "should be extended with the Expirer module" do Puppet::Util::Cacher.singleton_class.ancestors.should be_include(Puppet::Util::Cacher::Expirer) end it "should support defining cached attributes" do CacheTest.methods.should be_include("cached_attr") end it "should default to the Cacher module as its expirer" do CacheTest.new.expirer.should equal(Puppet::Util::Cacher) end describe "when using cached attributes" do before do @expirer = ExpirerTest.new @object = CacheTest.new @object.stubs(:expirer).returns @expirer end it "should create a getter for the cached attribute" do @object.should respond_to(:instance_cache) end it "should return a value calculated from the provided block" do time = Time.now Time.stubs(:now).returns time @object.instance_cache.should equal(time) end it "should return the cached value from the getter every time if the value is not expired" do @object.instance_cache.should equal(@object.instance_cache) end it "should regenerate and return a new value using the provided block if the value has been expired" do value = @object.instance_cache @expirer.expire @object.instance_cache.should_not equal(value) end it "should be able to trigger expiration on its expirer" do @expirer.expects(:expire) @object.expire end it "should do nothing when asked to expire when no expirer is available" do cacher = CacheTest.new class << cacher def expirer nil end end lambda { cacher.expire }.should_not raise_error end it "should be able to cache false values" do @object.expects(:init_instance_cache).returns false @object.instance_cache.should be_false @object.instance_cache.should be_false end it "should cache values again after expiration" do @object.instance_cache @expirer.expire @object.instance_cache.should equal(@object.instance_cache) end it "should always consider a value expired if it has no expirer" do @object.stubs(:expirer).returns nil @object.instance_cache.should_not equal(@object.instance_cache) end it "should allow writing of the attribute" do @object.should respond_to(:instance_cache=) end it "should correctly configure timestamps for expiration when the cached attribute is written to" do @object.instance_cache = "foo" @expirer.expire @object.instance_cache.should_not == "foo" end it "should allow specification of a ttl for cached attributes" do klass = Class.new do include Puppet::Util::Cacher end klass.cached_attr(:myattr, :ttl => 5) { Time.now } klass.attr_ttl(:myattr).should == 5 end it "should allow specification of a ttl as a string" do klass = Class.new do include Puppet::Util::Cacher end klass.cached_attr(:myattr, :ttl => "5") { Time.now } klass.attr_ttl(:myattr).should == 5 end it "should fail helpfully if the ttl cannot be converted to an integer" do klass = Class.new do include Puppet::Util::Cacher end lambda { klass.cached_attr(:myattr, :ttl => "yep") { Time.now } }.should raise_error(ArgumentError) end it "should not check for a ttl expiration if the class does not support that method" do klass = Class.new do extend Puppet::Util::Cacher end klass.singleton_class.cached_attr(:myattr) { "eh" } klass.myattr end it "should automatically expire cached attributes whose ttl has expired, even if no expirer is present" do klass = Class.new do def self.to_s "CacheTestClass" end include Puppet::Util::Cacher attr_accessor :value end klass.cached_attr(:myattr, :ttl => 5) { self.value += 1; self.value } now = Time.now later = Time.now + 15 instance = klass.new instance.value = 0 instance.myattr.should == 1 Time.expects(:now).returns later # This call should get the new Time value, which should expire the old value instance.myattr.should == 2 end end end diff --git a/spec/unit/util/checksums_spec.rb b/spec/unit/util/checksums_spec.rb index 954dc650b..146544201 100755 --- a/spec/unit/util/checksums_spec.rb +++ b/spec/unit/util/checksums_spec.rb @@ -1,161 +1,161 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-22. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/util/checksums' describe Puppet::Util::Checksums do before do @summer = Object.new @summer.extend(Puppet::Util::Checksums) end content_sums = [:md5, :md5lite, :sha1, :sha1lite] file_only = [:ctime, :mtime, :none] content_sums.each do |sumtype| it "should be able to calculate #{sumtype} sums from strings" do @summer.should be_respond_to(sumtype) end end [content_sums, file_only].flatten.each do |sumtype| it "should be able to calculate #{sumtype} sums from files" do @summer.should be_respond_to(sumtype.to_s + "_file") end end [content_sums, file_only].flatten.each do |sumtype| it "should be able to calculate #{sumtype} sums from stream" do @summer.should be_respond_to(sumtype.to_s + "_stream") end end it "should have a method for determining whether a given string is a checksum" do @summer.should respond_to(:checksum?) end %w{{md5}asdfasdf {sha1}asdfasdf {ctime}asdasdf {mtime}asdfasdf}.each do |sum| it "should consider #{sum} to be a checksum" do @summer.should be_checksum(sum) end end %w{{nosuchsum}asdfasdf {a}asdfasdf {ctime}}.each do |sum| it "should not consider #{sum} to be a checksum" do @summer.should_not be_checksum(sum) end end it "should have a method for stripping a sum type from an existing checksum" do @summer.sumtype("{md5}asdfasdfa").should == "md5" end it "should have a method for stripping the data from a checksum" do @summer.sumdata("{md5}asdfasdfa").should == "asdfasdfa" end it "should return a nil sumtype if the checksum does not mention a checksum type" do @summer.sumtype("asdfasdfa").should be_nil end {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass| describe("when using #{sum}") do it "should use #{klass} to calculate string checksums" do klass.expects(:hexdigest).with("mycontent").returns "whatever" @summer.send(sum, "mycontent").should == "whatever" end it "should use incremental #{klass} sums to calculate file checksums" do digest = mock 'digest' klass.expects(:new).returns digest file = "/path/to/my/file" fh = mock 'filehandle' fh.expects(:read).with(4096).times(3).returns("firstline").then.returns("secondline").then.returns(nil) #fh.expects(:read).with(512).returns("secondline") #fh.expects(:read).with(512).returns(nil) File.expects(:open).with(file, "r").yields(fh) digest.expects(:<<).with "firstline" digest.expects(:<<).with "secondline" digest.expects(:hexdigest).returns :mydigest @summer.send(sum.to_s + "_file", file).should == :mydigest end it "should yield #{klass} to the given block to calculate stream checksums" do digest = mock 'digest' klass.expects(:new).returns digest digest.expects(:hexdigest).returns :mydigest @summer.send(sum.to_s + "_stream") do |sum| sum.should == digest end.should == :mydigest end end end {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1}.each do |sum, klass| describe("when using #{sum}") do it "should use #{klass} to calculate string checksums from the first 512 characters of the string" do content = "this is a test" * 100 klass.expects(:hexdigest).with(content[0..511]).returns "whatever" @summer.send(sum, content).should == "whatever" end it "should use #{klass} to calculate a sum from the first 512 characters in the file" do digest = mock 'digest' klass.expects(:new).returns digest file = "/path/to/my/file" fh = mock 'filehandle' fh.expects(:read).with(512).returns('my content') File.expects(:open).with(file, "r").yields(fh) digest.expects(:<<).with "my content" digest.expects(:hexdigest).returns :mydigest @summer.send(sum.to_s + "_file", file).should == :mydigest end end end [:ctime, :mtime].each do |sum| describe("when using #{sum}") do it "should use the '#{sum}' on the file to determine the ctime" do file = "/my/file" stat = mock 'stat', sum => "mysum" File.expects(:stat).with(file).returns(stat) @summer.send(sum.to_s + "_file", file).should == "mysum" end it "should return nil for streams" do expectation = stub "expectation" expectation.expects(:do_something!).at_least_once @summer.send(sum.to_s + "_stream"){ |checksum| checksum << "anything" ; expectation.do_something! }.should be_nil end end end describe "when using the none checksum" do it "should return an empty string" do @summer.none_file("/my/file").should == "" end it "should return an empty string for streams" do expectation = stub "expectation" expectation.expects(:do_something!).at_least_once @summer.none_stream{ |checksum| checksum << "anything" ; expectation.do_something! }.should == "" end end end diff --git a/spec/unit/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb index a7d261dcf..81612ee5c 100755 --- a/spec/unit/util/command_line_spec.rb +++ b/spec/unit/util/command_line_spec.rb @@ -1,137 +1,142 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/command_line' describe Puppet::Util::CommandLine do include PuppetSpec::Files before do @tty = stub("tty", :tty? => true ) @pipe = stub("pipe", :tty? => false) end it "should pull off the first argument if it looks like a subcommand" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ client --help whatever.pp }, @tty ) command_line.subcommand_name.should == "client" command_line.args.should == %w{ --help whatever.pp } end it "should use 'apply' if the first argument looks like a .pp file" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ whatever.pp }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ whatever.pp } end it "should use 'apply' if the first argument looks like a .rb file" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ whatever.rb }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ whatever.rb } end it "should use 'apply' if the first argument looks like a flag" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ --debug }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ --debug } end it "should use 'apply' if the first argument is -" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ - }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ - } end it "should return nil if the first argument is --help" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ --help }, @tty ) command_line.subcommand_name.should == nil end it "should return nil if there are no arguments on a tty" do command_line = Puppet::Util::CommandLine.new("puppet", [], @tty ) command_line.subcommand_name.should == nil command_line.args.should == [] end it "should use 'apply' if there are no arguments on a pipe" do command_line = Puppet::Util::CommandLine.new("puppet", [], @pipe ) command_line.subcommand_name.should == "apply" command_line.args.should == [] end it "should return the executable name if it is not puppet" do command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty ) command_line.subcommand_name.should == "puppetmasterd" end it "should translate subcommand names into their legacy equivalent" do command_line = Puppet::Util::CommandLine.new("puppet", ["master"], @tty) command_line.legacy_executable_name.should == "puppetmasterd" end it "should leave legacy command names alone" do command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty) command_line.legacy_executable_name.should == "puppetmasterd" end describe "when the subcommand is not implemented" do it "should find and invoke an executable with a hyphenated name" do commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) Puppet::Util.expects(:which).with('puppet-whatever').returns('/dev/null/puppet-whatever') commandline.expects(:system).with('/dev/null/puppet-whatever', 'argument') commandline.execute end describe "and an external implementation cannot be found" do it "should abort and show the usage message" do commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) Puppet::Util.expects(:which).with('puppet-whatever').returns(nil) commandline.expects(:system).never - commandline.expects(:usage_message).returns("the usage message") - commandline.expects(:abort).with{|x| x =~ /the usage message/}.raises("stubbed abort") + text = Puppet::Face[:help, :current].help + commandline.expects(:puts).with { |x| x =~ /Unknown Puppet subcommand/ } + commandline.expects(:puts).with text - lambda{ commandline.execute }.should raise_error('stubbed abort') + commandline.execute end end end describe 'when loading commands' do before do @core_apps = %w{describe filebucket kick queue resource agent cert apply doc master} @command_line = Puppet::Util::CommandLine.new("foo", %w{ client --help whatever.pp }, @tty ) end + it "should expose available_subcommands as a class method" do + @core_apps.each do |command| + @command_line.available_subcommands.should include command + end + end it 'should be able to find all existing commands' do @core_apps.each do |command| @command_line.available_subcommands.should include command end end describe 'when multiple paths have applications' do before do @dir=tmpdir('command_line_plugin_test') @appdir="#{@dir}/puppet/application" FileUtils.mkdir_p(@appdir) FileUtils.touch("#{@appdir}/foo.rb") $LOAD_PATH.unshift(@dir) # WARNING: MUST MATCH THE AFTER ACTIONS! end it 'should be able to find commands from both paths' do found = @command_line.available_subcommands found.should include 'foo' @core_apps.each { |cmd| found.should include cmd } end after do $LOAD_PATH.shift # WARNING: MUST MATCH THE BEFORE ACTIONS! end end end end diff --git a/spec/unit/util/constant_inflector_spec.rb b/spec/unit/util/constant_inflector_spec.rb index 29574fd5b..cf2e8f892 100755 --- a/spec/unit/util/constant_inflector_spec.rb +++ b/spec/unit/util/constant_inflector_spec.rb @@ -1,70 +1,70 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-02-12. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/util/constant_inflector' describe Puppet::Util::ConstantInflector, "when converting file names to constants" do before do @inflector = Object.new @inflector.extend(Puppet::Util::ConstantInflector) end it "should capitalize terms" do @inflector.file2constant("file").should == "File" end it "should switch all '/' characters to double colons" do @inflector.file2constant("file/other").should == "File::Other" end it "should remove underscores and capitalize the proceeding letter" do @inflector.file2constant("file_other").should == "FileOther" end it "should correctly replace as many underscores as exist in the file name" do @inflector.file2constant("two_under_scores/with_some_more_underscores").should == "TwoUnderScores::WithSomeMoreUnderscores" end it "should collapse multiple underscores" do @inflector.file2constant("many___scores").should == "ManyScores" end it "should correctly handle file names deeper than two directories" do @inflector.file2constant("one_two/three_four/five_six").should == "OneTwo::ThreeFour::FiveSix" end end describe Puppet::Util::ConstantInflector, "when converting constnats to file names" do before do @inflector = Object.new @inflector.extend(Puppet::Util::ConstantInflector) end it "should convert them to a string if necessary" do @inflector.constant2file(Puppet::Util::ConstantInflector).should be_instance_of(String) end it "should accept string inputs" do @inflector.constant2file("Puppet::Util::ConstantInflector").should be_instance_of(String) end it "should downcase all terms" do @inflector.constant2file("Puppet").should == "puppet" end it "should convert '::' to '/'" do @inflector.constant2file("Puppet::Util::Constant").should == "puppet/util/constant" end it "should convert mid-word capitalization to an underscore" do @inflector.constant2file("OneTwo::ThreeFour").should == "one_two/three_four" end it "should correctly handle constants with more than two parts" do @inflector.constant2file("OneTwoThree::FourFiveSixSeven").should == "one_two_three/four_five_six_seven" end end diff --git a/spec/unit/util/errors_spec.rb b/spec/unit/util/errors_spec.rb index da1b8b0bd..d51a15ef4 100755 --- a/spec/unit/util/errors_spec.rb +++ b/spec/unit/util/errors_spec.rb @@ -1,38 +1,37 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/errors' class ErrorTester include Puppet::Util::Errors attr_accessor :line, :file end describe Puppet::Util::Errors do before do @tester = ErrorTester.new end it "should provide a 'fail' method" do @tester.should respond_to(:fail) end it "should provide a 'devfail' method" do @tester.should respond_to(:devfail) end it "should raise any provided error when failing" do lambda { @tester.fail(Puppet::ParseError, "stuff") }.should raise_error(Puppet::ParseError) end it "should default to Puppet::Error when failing" do lambda { @tester.fail("stuff") }.should raise_error(Puppet::Error) end it "should have a method for converting error context into a string" do @tester.file = "/my/file" @tester.line = 50 @tester.error_context.should == " at /my/file:50" end end diff --git a/spec/unit/util/execution_spec.rb b/spec/unit/util/execution_spec.rb old mode 100644 new mode 100755 index 312dd3b8e..5b8b8a527 --- a/spec/unit/util/execution_spec.rb +++ b/spec/unit/util/execution_spec.rb @@ -1,49 +1,48 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Util::Execution do include Puppet::Util::Execution describe "#withenv" do before :each do @original_path = ENV["PATH"] @new_env = {:PATH => "/some/bogus/path"} end it "should change environment variables within the block then reset environment variables to their original values" do withenv @new_env do ENV["PATH"].should == "/some/bogus/path" end ENV["PATH"].should == @original_path end it "should reset environment variables to their original values even if the block fails" do begin withenv @new_env do ENV["PATH"].should == "/some/bogus/path" raise "This is a failure" end rescue end ENV["PATH"].should == @original_path end it "should reset environment variables even when they are set twice" do # Setting Path & Environment parameters in Exec type can cause weirdness @new_env["PATH"] = "/someother/bogus/path" withenv @new_env do # When assigning duplicate keys, can't guarantee order of evaluation ENV["PATH"].should =~ /\/some.*\/bogus\/path/ end ENV["PATH"].should == @original_path end it "should remove any new environment variables after the block ends" do @new_env[:FOO] = "bar" withenv @new_env do ENV["FOO"].should == "bar" end ENV["FOO"].should == nil end end end diff --git a/spec/unit/util/execution_stub_spec.rb b/spec/unit/util/execution_stub_spec.rb old mode 100644 new mode 100755 index 14cf9c67a..34987689c --- a/spec/unit/util/execution_stub_spec.rb +++ b/spec/unit/util/execution_stub_spec.rb @@ -1,35 +1,34 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Util::ExecutionStub do it "should use the provided stub code when 'set' is called" do Puppet::Util::ExecutionStub.set do |command, options| command.should == ['/bin/foo', 'bar'] "stub output" end Puppet::Util::ExecutionStub.current_value.should_not == nil Puppet::Util.execute(['/bin/foo', 'bar']).should == "stub output" end it "should automatically restore normal execution at the conclusion of each spec test" do # Note: this test relies on the previous test creating a stub. Puppet::Util::ExecutionStub.current_value.should == nil end it "should restore normal execution after 'reset' is called" do true_command = Puppet::Util.which('true') # Note: "true" exists at different paths in different OSes stub_call_count = 0 Puppet::Util::ExecutionStub.set do |command, options| command.should == [true_command] stub_call_count += 1 'stub called' end Puppet::Util.execute([true_command]).should == 'stub called' stub_call_count.should == 1 Puppet::Util::ExecutionStub.reset Puppet::Util::ExecutionStub.current_value.should == nil Puppet::Util.execute([true_command]).should == '' stub_call_count.should == 1 end end diff --git a/spec/unit/util/feature_spec.rb b/spec/unit/util/feature_spec.rb index 365428752..15375e0b1 100755 --- a/spec/unit/util/feature_spec.rb +++ b/spec/unit/util/feature_spec.rb @@ -1,72 +1,71 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/feature' describe Puppet::Util::Feature do before do @features = Puppet::Util::Feature.new("features") @features.stubs(:warn) end it "should consider undefined features to be absent" do @features.should_not be_defined_feature end it "should be able to add new features" do @features.add(:myfeature) {} @features.should respond_to(:myfeature?) end it "should call associated code when loading a feature" do $loaded_feature = false @features.add(:myfeature) { $loaded_feature = true} $loaded_feature.should be_true end it "should consider a feature absent when the feature load fails" do @features.add(:failer) { raise "foo" } @features.should_not be_failer end it "should consider a feature to be absent when the feature load returns false" do @features.add(:failer) { false } @features.should_not be_failer end it "should consider a feature to be present when the feature load returns true" do @features.add(:available) { true } @features.should be_available end it "should cache the results of a feature load" do $loaded_feature = 0 @features.add(:myfeature) { $loaded_feature += 1 } @features.myfeature? @features.myfeature? $loaded_feature.should == 1 end it "should support features with libraries" do lambda { @features.add(:puppet, :libs => %w{puppet}) }.should_not raise_error end it "should consider a feature to be present if all of its libraries are present" do @features.add(:myfeature, :libs => %w{foo bar}) @features.expects(:require).with("foo") @features.expects(:require).with("bar") @features.should be_myfeature end it "should log and consider a feature to be absent if any of its libraries are absent" do @features.add(:myfeature, :libs => %w{foo bar}) @features.expects(:require).with("foo").raises(LoadError) @features.stubs(:require).with("bar") Puppet.expects(:debug) @features.should_not be_myfeature end end diff --git a/spec/unit/util/file_locking_spec.rb b/spec/unit/util/file_locking_spec.rb index e9dbe9d7b..261474263 100755 --- a/spec/unit/util/file_locking_spec.rb +++ b/spec/unit/util/file_locking_spec.rb @@ -1,160 +1,159 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/file_locking' class FileLocker include Puppet::Util::FileLocking end describe Puppet::Util::FileLocking do it "should have a module method for getting a read lock on files" do Puppet::Util::FileLocking.should respond_to(:readlock) end it "should have a module method for getting a write lock on files" do Puppet::Util::FileLocking.should respond_to(:writelock) end it "should have an instance method for getting a read lock on files" do FileLocker.new.private_methods.should be_include("readlock") end it "should have an instance method for getting a write lock on files" do FileLocker.new.private_methods.should be_include("writelock") end describe "when acquiring a read lock" do before do File.stubs(:exists?).with('/file').returns true File.stubs(:file?).with('/file').returns true end it "should use a global shared mutex" do Puppet::Util.expects(:synchronize_on).with('/file',Sync::SH).once Puppet::Util::FileLocking.readlock '/file' end it "should use a shared lock on the file" do Puppet::Util.expects(:synchronize_on).with('/file',Sync::SH).yields fh = mock 'filehandle' File.expects(:open).with("/file").yields fh fh.expects(:lock_shared).yields "locked_fh" result = nil Puppet::Util::FileLocking.readlock('/file') { |l| result = l } result.should == "locked_fh" end it "should only work on regular files" do File.expects(:file?).with('/file').returns false proc { Puppet::Util::FileLocking.readlock('/file') }.should raise_error(ArgumentError) end it "should create missing files" do Puppet::Util.expects(:synchronize_on).with('/file',Sync::SH).yields File.expects(:exists?).with('/file').returns false File.expects(:open).with('/file').once Puppet::Util::FileLocking.readlock('/file') end end describe "when acquiring a write lock" do before do Puppet::Util.stubs(:synchronize_on).yields File.stubs(:file?).with('/file').returns true File.stubs(:exists?).with('/file').returns true end it "should fail if the parent directory does not exist" do FileTest.expects(:directory?).with("/my/dir").returns false File.stubs(:file?).with('/my/dir/file').returns true File.stubs(:exists?).with('/my/dir/file').returns true lambda { Puppet::Util::FileLocking.writelock('/my/dir/file') }.should raise_error(Puppet::DevError) end it "should use a global exclusive mutex" do Puppet::Util.expects(:synchronize_on).with("/file",Sync::EX) Puppet::Util::FileLocking.writelock '/file' end it "should use any specified mode when opening the file" do File.expects(:open).with("/file", File::Constants::CREAT | File::Constants::WRONLY , :mymode) Puppet::Util::FileLocking.writelock('/file', :mymode) end it "should use the mode of the existing file if no mode is specified" do File.expects(:stat).with("/file").returns(mock("stat", :mode => 0755)) File.expects(:open).with("/file", File::Constants::CREAT | File::Constants::WRONLY, 0755) Puppet::Util::FileLocking.writelock('/file') end it "should use 0600 as the mode if no mode is specified and the file does not exist" do File.expects(:stat).raises(Errno::ENOENT) File.expects(:open).with("/file", File::Constants::CREAT | File::Constants::WRONLY, 0600) Puppet::Util::FileLocking.writelock('/file') end it "should create an exclusive file lock" do fh = mock 'fh' File.expects(:open).yields fh fh.expects(:lock_exclusive) Puppet::Util::FileLocking.writelock('/file') end it "should allow the caller to write to the locked file" do fh = mock 'fh' File.expects(:open).yields fh lfh = mock 'locked_filehandle' fh.expects(:lock_exclusive).yields(lfh) lfh.stubs(:seek) lfh.stubs(:truncate) lfh.expects(:print).with "foo" Puppet::Util::FileLocking.writelock('/file') do |f| f.print "foo" end end it "should truncate the file under an exclusive lock" do fh = mock 'fh' File.expects(:open).yields fh lfh = mock 'locked_filehandle' fh.expects(:lock_exclusive).yields(lfh) lfh.expects(:seek).with(0, IO::SEEK_SET) lfh.expects(:truncate).with(0) lfh.stubs(:print) Puppet::Util::FileLocking.writelock('/file') do |f| f.print "foo" end end it "should only work on regular files" do File.expects(:file?).with('/file').returns false proc { Puppet::Util::FileLocking.writelock('/file') }.should raise_error(ArgumentError) end it "should create missing files" do Puppet::Util.expects(:synchronize_on).with('/file',Sync::EX).yields File.expects(:exists?).with('/file').returns false File.expects(:open).with('/file', File::Constants::CREAT | File::Constants::WRONLY, 0600).once Puppet::Util::FileLocking.writelock('/file') end end end diff --git a/spec/unit/util/filetype_spec.rb b/spec/unit/util/filetype_spec.rb old mode 100644 new mode 100755 index 012631b91..a2c0da660 --- a/spec/unit/util/filetype_spec.rb +++ b/spec/unit/util/filetype_spec.rb @@ -1,100 +1,99 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/filetype' # XXX Import all of the tests into this file. describe Puppet::Util::FileType do describe "when backing up a file" do before do @file = Puppet::Util::FileType.filetype(:flat).new("/my/file") end it "should do nothing if the file does not exist" do File.expects(:exists?).with("/my/file").returns false @file.expects(:bucket).never @file.backup end it "should use its filebucket to backup the file if it exists" do File.expects(:exists?).with("/my/file").returns true bucket = mock 'bucket' bucket.expects(:backup).with("/my/file") @file.expects(:bucket).returns bucket @file.backup end it "should use the default filebucket" do bucket = mock 'bucket' bucket.expects(:bucket).returns "mybucket" Puppet::Type.type(:filebucket).expects(:mkdefaultbucket).returns bucket @file.bucket.should == "mybucket" end end describe "the flat filetype" do before do @type = Puppet::Util::FileType.filetype(:flat) end it "should exist" do @type.should_not be_nil end describe "when the file already exists" do it "should return the file's contents when asked to read it" do file = @type.new("/my/file") File.expects(:exist?).with("/my/file").returns true File.expects(:read).with("/my/file").returns "my text" file.read.should == "my text" end it "should unlink the file when asked to remove it" do file = @type.new("/my/file") File.expects(:exist?).with("/my/file").returns true File.expects(:unlink).with("/my/file") file.remove end end describe "when the file does not exist" do it "should return an empty string when asked to read the file" do file = @type.new("/my/file") File.expects(:exist?).with("/my/file").returns false file.read.should == "" end end describe "when writing the file" do before do @file = @type.new("/my/file") FileUtils.stubs(:cp) @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" Tempfile.stubs(:new).returns @tempfile end it "should first create a temp file and copy its contents over to the file location" do Tempfile.expects(:new).with("puppet").returns @tempfile @tempfile.expects(:print).with("my text") @tempfile.expects(:flush) @tempfile.expects(:close) FileUtils.expects(:cp).with(@tempfile.path, "/my/file") @file.write "my text" end it "should set the selinux default context on the file" do @file.expects(:set_selinux_default_context).with("/my/file") @file.write "eh" end end end end diff --git a/spec/unit/util/inline_docs_spec.rb b/spec/unit/util/inline_docs_spec.rb index 75afb57cb..1d88180b3 100755 --- a/spec/unit/util/inline_docs_spec.rb +++ b/spec/unit/util/inline_docs_spec.rb @@ -1,32 +1,31 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/inline_docs' class InlineDoccer include Puppet::Util::InlineDocs end describe Puppet::Util::InlineDocs do describe "when included" do it "should create a class method for specifying that docs should be associated" do InlineDoccer.expects(:use_docs=).with true InlineDoccer.associates_doc end it "should default to not associating docs" do (!! InlineDoccer.use_docs).should be_false end it "should create an instance method for setting documentation" do instance = InlineDoccer.new instance.doc = "foo" instance.doc.should == "foo" end it "should default to an empty string for docs" do InlineDoccer.new.doc.should == "" end end end diff --git a/spec/unit/util/ldap/connection_spec.rb b/spec/unit/util/ldap/connection_spec.rb index 91f008a2e..f97c72d77 100755 --- a/spec/unit/util/ldap/connection_spec.rb +++ b/spec/unit/util/ldap/connection_spec.rb @@ -1,169 +1,169 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-19. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/util/ldap/connection' # So our mocks and such all work, even when ldap isn't available. unless Puppet.features.ldap? class LDAP class Conn def initialize(*args) end end class SSLConn < Conn; end LDAP_OPT_PROTOCOL_VERSION = 1 LDAP_OPT_REFERRALS = 2 LDAP_OPT_ON = 3 end end describe Puppet::Util::Ldap::Connection do before do Puppet.features.stubs(:ldap?).returns true @ldapconn = mock 'ldap' LDAP::Conn.stubs(:new).returns(@ldapconn) LDAP::SSLConn.stubs(:new).returns(@ldapconn) @ldapconn.stub_everything @connection = Puppet::Util::Ldap::Connection.new("host", "port") end describe "when creating connections" do it "should require the host and port" do lambda { Puppet::Util::Ldap::Connection.new("myhost") }.should raise_error(ArgumentError) end it "should allow specification of a user and password" do lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :user => "blah", :password => "boo") }.should_not raise_error end it "should allow specification of ssl" do lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :ssl => :tsl) }.should_not raise_error end it "should support requiring a new connection" do lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :reset => true) }.should_not raise_error end it "should fail if ldap is unavailable" do Puppet.features.expects(:ldap?).returns(false) lambda { Puppet::Util::Ldap::Connection.new("host", "port") }.should raise_error(Puppet::Error) end it "should use neither ssl nor tls by default" do LDAP::Conn.expects(:new).with("host", "port").returns(@ldapconn) @connection.start end it "should use LDAP::SSLConn if ssl is requested" do LDAP::SSLConn.expects(:new).with("host", "port").returns(@ldapconn) @connection.ssl = true @connection.start end it "should use LDAP::SSLConn and tls if tls is requested" do LDAP::SSLConn.expects(:new).with("host", "port", true).returns(@ldapconn) @connection.ssl = :tls @connection.start end it "should set the protocol version to 3 and enable referrals" do @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) @connection.start end it "should bind with the provided user and password" do @connection.user = "myuser" @connection.password = "mypassword" @ldapconn.expects(:simple_bind).with("myuser", "mypassword") @connection.start end it "should bind with no user and password if none has been provided" do @ldapconn.expects(:simple_bind).with(nil, nil) @connection.start end end describe "when closing connections" do it "should not close connections that are not open" do @connection.stubs(:connection).returns(@ldapconn) @ldapconn.expects(:bound?).returns false @ldapconn.expects(:unbind).never @connection.close end end it "should have a class-level method for creating a default connection" do Puppet::Util::Ldap::Connection.should respond_to(:instance) end describe "when creating a default connection" do before do Puppet.settings.stubs(:value).returns "whatever" end it "should use the :ldapserver setting to determine the host" do Puppet.settings.expects(:value).with(:ldapserver).returns "myserv" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| host == "myserv" } Puppet::Util::Ldap::Connection.instance end it "should use the :ldapport setting to determine the port" do Puppet.settings.expects(:value).with(:ldapport).returns "456" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| port == "456" } Puppet::Util::Ldap::Connection.instance end it "should set ssl to :tls if tls is enabled" do Puppet.settings.expects(:value).with(:ldaptls).returns true Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == :tls } Puppet::Util::Ldap::Connection.instance end it "should set ssl to 'true' if ssl is enabled and tls is not" do Puppet.settings.expects(:value).with(:ldaptls).returns false Puppet.settings.expects(:value).with(:ldapssl).returns true Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == true } Puppet::Util::Ldap::Connection.instance end it "should set ssl to false if neither ssl nor tls are enabled" do Puppet.settings.expects(:value).with(:ldaptls).returns false Puppet.settings.expects(:value).with(:ldapssl).returns false Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == false } Puppet::Util::Ldap::Connection.instance end it "should set the ldapuser if one is set" do Puppet.settings.expects(:value).with(:ldapuser).returns "foo" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" } Puppet::Util::Ldap::Connection.instance end it "should set the ldapuser and ldappassword if both is set" do Puppet.settings.expects(:value).with(:ldapuser).returns "foo" Puppet.settings.expects(:value).with(:ldappassword).returns "bar" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" and options[:password] == "bar" } Puppet::Util::Ldap::Connection.instance end end end diff --git a/spec/unit/util/ldap/generator_spec.rb b/spec/unit/util/ldap/generator_spec.rb index 59c05b7c7..b3e664d6b 100755 --- a/spec/unit/util/ldap/generator_spec.rb +++ b/spec/unit/util/ldap/generator_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-28. # Copyright (c) 2008. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/util/ldap/generator' describe Puppet::Util::Ldap::Generator do before do @generator = Puppet::Util::Ldap::Generator.new(:uno) end it "should require a parameter name at initialization" do lambda { Puppet::Util::Ldap::Generator.new }.should raise_error end it "should always return its name as a string" do g = Puppet::Util::Ldap::Generator.new(:myname) g.name.should == "myname" end it "should provide a method for declaring the source parameter" do @generator.from(:dos) end it "should always return a set source as a string" do @generator.from(:dos) @generator.source.should == "dos" end it "should return the source as nil if there is no source" do @generator.source.should be_nil end it "should return itself when declaring the source" do @generator.from(:dos).should equal(@generator) end it "should run the provided block when asked to generate the value" do @generator.with { "yayness" } @generator.generate.should == "yayness" end it "should pass in any provided value to the block" do @generator.with { |value| value.upcase } @generator.generate("myval").should == "MYVAL" end it "should return itself when declaring the code used for generating" do @generator.with { |value| value.upcase }.should equal(@generator) end end diff --git a/spec/unit/util/ldap/manager_spec.rb b/spec/unit/util/ldap/manager_spec.rb index e91582c8d..5cce626b5 100755 --- a/spec/unit/util/ldap/manager_spec.rb +++ b/spec/unit/util/ldap/manager_spec.rb @@ -1,654 +1,654 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-19. # Copyright (c) 2006. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +require 'spec_helper' require 'puppet/util/ldap/manager' # If the ldap classes aren't available, go ahead and # create some, so our tests will pass. unless defined?(LDAP::Mod) class LDAP LDAP_MOD_ADD = :adding LDAP_MOD_REPLACE = :replacing LDAP_MOD_DELETE = :deleting class ResultError < RuntimeError; end class Mod def initialize(*args) end end end end describe Puppet::Util::Ldap::Manager do before do @manager = Puppet::Util::Ldap::Manager.new end it "should return self when specifying objectclasses" do @manager.manages(:one, :two).should equal(@manager) end it "should allow specification of what objectclasses are managed" do @manager.manages(:one, :two).objectclasses.should == [:one, :two] end it "should return self when specifying the relative base" do @manager.at("yay").should equal(@manager) end it "should allow specification of the relative base" do @manager.at("yay").location.should == "yay" end it "should return self when specifying the attribute map" do @manager.maps(:one => :two).should equal(@manager) end it "should allow specification of the rdn attribute" do @manager.named_by(:uid).rdn.should == :uid end it "should allow specification of the attribute map" do @manager.maps(:one => :two).puppet2ldap.should == {:one => :two} end it "should have a no-op 'and' method that just returns self" do @manager.and.should equal(@manager) end it "should allow specification of generated attributes" do @manager.generates(:thing).should be_instance_of(Puppet::Util::Ldap::Generator) end describe "when generating attributes" do before do @generator = stub 'generator', :source => "one", :name => "myparam" Puppet::Util::Ldap::Generator.stubs(:new).with(:myparam).returns @generator end it "should create a generator to do the parameter generation" do Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns @generator @manager.generates(:myparam) end it "should return the generator from the :generates method" do @manager.generates(:myparam).should equal(@generator) end it "should not replace already present values" do @manager.generates(:myparam) attrs = {"myparam" => "testing"} @generator.expects(:generate).never @manager.generate attrs attrs["myparam"].should == "testing" end it "should look for the parameter as a string, not a symbol" do @manager.generates(:myparam) @generator.expects(:generate).with("yay").returns %w{double yay} attrs = {"one" => "yay"} @manager.generate attrs attrs["myparam"].should == %w{double yay} end it "should fail if a source is specified and no source value is not defined" do @manager.generates(:myparam) lambda { @manager.generate "two" => "yay" }.should raise_error(ArgumentError) end it "should use the source value to generate the new value if a source attribute is specified" do @manager.generates(:myparam) @generator.expects(:generate).with("yay").returns %w{double yay} @manager.generate "one" => "yay" end it "should not pass in any value if no source attribute is specified" do @generator.stubs(:source).returns nil @manager.generates(:myparam) @generator.expects(:generate).with.returns %w{double yay} @manager.generate "one" => "yay" end it "should convert any results to arrays of strings if necessary" do @generator.expects(:generate).returns :test @manager.generates(:myparam) attrs = {"one" => "two"} @manager.generate(attrs) attrs["myparam"].should == ["test"] end it "should add the result to the passed-in attribute hash" do @generator.expects(:generate).returns %w{test} @manager.generates(:myparam) attrs = {"one" => "two"} @manager.generate(attrs) attrs["myparam"].should == %w{test} end end it "should be considered invalid if it is missing a location" do @manager.manages :me @manager.maps :me => :you @manager.should_not be_valid end it "should be considered invalid if it is missing an objectclass list" do @manager.maps :me => :you @manager.at "ou=yayness" @manager.should_not be_valid end it "should be considered invalid if it is missing an attribute map" do @manager.manages :me @manager.at "ou=yayness" @manager.should_not be_valid end it "should be considered valid if it has an attribute map, location, and objectclass list" do @manager.maps :me => :you @manager.manages :me @manager.at "ou=yayness" @manager.should be_valid end it "should calculate an instance's dn using the :ldapbase setting and the relative base" do Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" @manager.at "ou=mybase" @manager.dn("me").should == "cn=me,ou=mybase,dc=testing" end it "should use the specified rdn when calculating an instance's dn" do Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" @manager.named_by :uid @manager.at "ou=mybase" @manager.dn("me").should =~ /^uid=me/ end it "should calculate its base using the :ldapbase setting and the relative base" do Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" @manager.at "ou=mybase" @manager.base.should == "ou=mybase,dc=testing" end describe "when generating its search filter" do it "should using a single 'objectclass=' filter if a single objectclass is specified" do @manager.manages("testing") @manager.filter.should == "objectclass=testing" end it "should create an LDAP AND filter if multiple objectclasses are specified" do @manager.manages "testing", "okay", "done" @manager.filter.should == "(&(objectclass=testing)(objectclass=okay)(objectclass=done))" end end it "should have a method for converting a Puppet attribute name to an LDAP attribute name as a string" do @manager.maps :puppet_attr => :ldap_attr @manager.ldap_name(:puppet_attr).should == "ldap_attr" end it "should have a method for converting an LDAP attribute name to a Puppet attribute name" do @manager.maps :puppet_attr => :ldap_attr @manager.puppet_name(:ldap_attr).should == :puppet_attr end it "should have a :create method for creating ldap entries" do @manager.should respond_to(:create) end it "should have a :delete method for deleting ldap entries" do @manager.should respond_to(:delete) end it "should have a :modify method for modifying ldap entries" do @manager.should respond_to(:modify) end it "should have a method for finding an entry by name in ldap" do @manager.should respond_to(:find) end describe "when converting ldap entries to hashes for providers" do before do @manager.maps :uno => :one, :dos => :two @result = @manager.entry2provider("dn" => ["cn=one,ou=people,dc=madstop"], "one" => ["two"], "three" => %w{four}, "objectclass" => %w{yay ness}) end it "should set the name to the short portion of the dn" do @result[:name].should == "one" end it "should remove the objectclasses" do @result["objectclass"].should be_nil end it "should remove any attributes that are not mentioned in the map" do @result["three"].should be_nil end it "should rename convert to symbols all attributes to their puppet names" do @result[:uno].should == %w{two} end it "should set the value of all unset puppet attributes as :absent" do @result[:dos].should == :absent end end describe "when using an ldap connection" do before do @ldapconn = mock 'ldapconn' @conn = stub 'connection', :connection => @ldapconn, :start => nil, :close => nil Puppet::Util::Ldap::Connection.stubs(:new).returns(@conn) end it "should fail unless a block is given" do lambda { @manager.connect }.should raise_error(ArgumentError) end it "should open the connection with its server set to :ldapserver" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapserver).returns("myserver") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[0] == "myserver" }.returns @conn @manager.connect { |c| } end it "should open the connection with its port set to the :ldapport" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapport).returns("28") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[1] == "28" }.returns @conn @manager.connect { |c| } end it "should open the connection with no user if :ldapuser is not set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapuser).returns("") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user].nil? }.returns @conn @manager.connect { |c| } end it "should open the connection with its user set to the :ldapuser if it is set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapuser).returns("mypass") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user] == "mypass" }.returns @conn @manager.connect { |c| } end it "should open the connection with no password if :ldappassword is not set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldappassword).returns("") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password].nil? }.returns @conn @manager.connect { |c| } end it "should open the connection with its password set to the :ldappassword if it is set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldappassword).returns("mypass") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password] == "mypass" }.returns @conn @manager.connect { |c| } end it "should set ssl to :tls if ldaptls is enabled" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldaptls).returns(true) Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == :tls }.returns @conn @manager.connect { |c| } end it "should set ssl to true if ldapssl is enabled" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapssl).returns(true) Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == true }.returns @conn @manager.connect { |c| } end it "should set ssl to false if neither ldaptls nor ldapssl is enabled" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapssl).returns(false) Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == false }.returns @conn @manager.connect { |c| } end it "should open, yield, and then close the connection" do @conn.expects(:start) @conn.expects(:close) Puppet::Util::Ldap::Connection.expects(:new).returns(@conn) @ldapconn.expects(:test) @manager.connect { |c| c.test } end it "should close the connection even if there's an exception in the passed block" do @conn.expects(:close) lambda { @manager.connect { |c| raise ArgumentError } }.should raise_error(ArgumentError) end end describe "when using ldap" do before do @conn = mock 'connection' @manager.stubs(:connect).yields @conn @manager.stubs(:objectclasses).returns [:oc1, :oc2] @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro end describe "to create entries" do it "should convert the first argument to its :create method to a full dn and pass the resulting argument list to its connection" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| name == "mydn" } @manager.create("myname", {"attr" => "myattrs"}) end it "should add the objectclasses to the attributes" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("oc1") and attrs["objectClass"].include?("oc2") } @manager.create("myname", {:one => :testing}) end it "should add the rdn to the attributes" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| attrs["cn"] == %w{myname} } @manager.create("myname", {:one => :testing}) end it "should add 'top' to the objectclasses if it is not listed" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("top") } @manager.create("myname", {:one => :testing}) end it "should add any generated values that are defined" do generator = stub 'generator', :source => :one, :name => "myparam" Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator @manager.generates(:myparam) @manager.stubs(:dn).with("myname").returns "mydn" generator.expects(:generate).with(:testing).returns ["generated value"] @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated value"] } @manager.create("myname", {:one => :testing}) end it "should convert any generated values to arrays of strings if necessary" do generator = stub 'generator', :source => :one, :name => "myparam" Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator @manager.generates(:myparam) @manager.stubs(:dn).returns "mydn" generator.expects(:generate).returns :generated @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated"] } @manager.create("myname", {:one => :testing}) end end describe "do delete entries" do it "should convert the first argument to its :delete method to a full dn and pass the resulting argument list to its connection" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:delete).with("mydn") @manager.delete("myname") end end describe "to modify entries" do it "should convert the first argument to its :modify method to a full dn and pass the resulting argument list to its connection" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:modify).with("mydn", :mymods) @manager.modify("myname", :mymods) end end describe "to find a single entry" do it "should use the dn of the provided name as the search base, a scope of 0, and 'objectclass=*' as the filter for a search2 call" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:search2).with("mydn", 0, "objectclass=*") @manager.find("myname") end it "should return nil if an exception is thrown because no result is found" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:search2).raises LDAP::ResultError @manager.find("myname").should be_nil end it "should return a converted provider hash if the result is found" do @manager.expects(:dn).with("myname").returns "mydn" result = {"one" => "two"} @conn.expects(:search2).yields result @manager.expects(:entry2provider).with(result).returns "myprovider" @manager.find("myname").should == "myprovider" end end describe "to search for multiple entries" do before do @manager.stubs(:filter).returns "myfilter" end it "should use the manager's search base as the dn of the provided name as the search base" do @manager.expects(:base).returns "mybase" @conn.expects(:search2).with { |base, scope, filter| base == "mybase" } @manager.search end it "should use a scope of 1" do @conn.expects(:search2).with { |base, scope, filter| scope == 1 } @manager.search end it "should use any specified search filter" do @manager.expects(:filter).never @conn.expects(:search2).with { |base, scope, filter| filter == "boo" } @manager.search("boo") end it "should turn its objectclass list into its search filter if one is not specified" do @manager.expects(:filter).returns "yay" @conn.expects(:search2).with { |base, scope, filter| filter == "yay" } @manager.search end it "should return nil if no result is found" do @conn.expects(:search2) @manager.search.should be_nil end it "should return an array of the found results converted to provider hashes" do # LAK: AFAICT, it's impossible to yield multiple times in an expectation. one = {"dn" => "cn=one,dc=madstop,dc=com", "one" => "two"} @conn.expects(:search2).yields(one) @manager.expects(:entry2provider).with(one).returns "myprov" @manager.search.should == ["myprov"] end end end describe "when an instance" do before do @name = "myname" @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro end describe "is being updated" do it "should get created if the current attribute list is empty and the desired attribute list has :ensure == :present" do @manager.expects(:create) @manager.update(@name, {}, {:ensure => :present}) end it "should get created if the current attribute list has :ensure == :absent and the desired attribute list has :ensure == :present" do @manager.expects(:create) @manager.update(@name, {:ensure => :absent}, {:ensure => :present}) end it "should get deleted if the current attribute list has :ensure == :present and the desired attribute list has :ensure == :absent" do @manager.expects(:delete) @manager.update(@name, {:ensure => :present}, {:ensure => :absent}) end it "should get modified if both attribute lists have :ensure == :present" do @manager.expects(:modify) @manager.update(@name, {:ensure => :present, :one => :two}, {:ensure => :present, :one => :three}) end end describe "is being deleted" do it "should call the :delete method with its name and manager" do @manager.expects(:delete).with(@name) @manager.update(@name, {}, {:ensure => :absent}) end end describe "is being created" do before do @is = {} @should = {:ensure => :present, :one => :yay, :two => :absent} end it "should call the :create method with its name" do @manager.expects(:create).with { |name, attrs| name == @name } @manager.update(@name, @is, @should) end it "should call the :create method with its property hash converted to ldap attribute names" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should convert the property names to strings" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should convert the property values to arrays if necessary" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should convert the property values to strings if necessary" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should not include :ensure in the properties sent" do @manager.expects(:create).with { |*args| args[1][:ensure].nil? } @manager.update(@name, @is, @should) end it "should not include attributes set to :absent in the properties sent" do @manager.expects(:create).with { |*args| args[1][:dos].nil? } @manager.update(@name, @is, @should) end end describe "is being modified" do it "should call the :modify method with its name and an array of LDAP::Mod instances" do LDAP::Mod.stubs(:new).returns "whatever" @is = {:one => :yay} @should = {:one => :yay, :two => :foo} @manager.expects(:modify).with { |name, mods| name == @name } @manager.update(@name, @is, @should) end it "should create the LDAP::Mod with the property name converted to the ldap name as a string" do @is = {:one => :yay} @should = {:one => :yay, :two => :foo} mod = mock 'module' LDAP::Mod.expects(:new).with { |form, name, value| name == "dos" }.returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should create an LDAP::Mod instance of type LDAP_MOD_ADD for each attribute being added, with the attribute value converted to a string of arrays" do @is = {:one => :yay} @should = {:one => :yay, :two => :foo} mod = mock 'module' LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_ADD, "dos", ["foo"]).returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should create an LDAP::Mod instance of type LDAP_MOD_DELETE for each attribute being deleted" do @is = {:one => :yay, :two => :foo} @should = {:one => :yay, :two => :absent} mod = mock 'module' LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_DELETE, "dos", []).returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should create an LDAP::Mod instance of type LDAP_MOD_REPLACE for each attribute being modified, with the attribute converted to a string of arrays" do @is = {:one => :yay, :two => :four} @should = {:one => :yay, :two => :five} mod = mock 'module' LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_REPLACE, "dos", ["five"]).returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should pass all created Mod instances to the modify method" do @is = {:one => :yay, :two => :foo, :three => :absent} @should = {:one => :yay, :two => :foe, :three => :fee, :four => :fie} LDAP::Mod.expects(:new).times(3).returns("mod1").then.returns("mod2").then.returns("mod3") @manager.expects(:modify).with do |name, mods| mods.sort == %w{mod1 mod2 mod3}.sort end @manager.update(@name, @is, @should) end end end end diff --git a/spec/unit/util/loadedfile_spec.rb b/spec/unit/util/loadedfile_spec.rb index 92daeb953..dcea53e45 100755 --- a/spec/unit/util/loadedfile_spec.rb +++ b/spec/unit/util/loadedfile_spec.rb @@ -1,72 +1,71 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'tempfile' require 'puppet/util/loadedfile' describe Puppet::Util::LoadedFile do include PuppetSpec::Files before(:each) do @f = Tempfile.new('loadedfile_test') @f.puts "yayness" @f.flush @loaded = Puppet::Util::LoadedFile.new(@f.path) fake_ctime = Time.now - (2 * Puppet[:filetimeout]) @stat = stub('stat', :ctime => fake_ctime) @fake_now = Time.now + (2 * Puppet[:filetimeout]) end it "should accept files that don't exist" do nofile = tmpfile('testfile') File.exists?(nofile).should == false lambda{ Puppet::Util::LoadedFile.new(nofile) }.should_not raise_error end it "should recognize when the file has not changed" do # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should == false end it "should recognize when the file has changed" do # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() # just didn't do the job. File.stubs(:stat).returns(@stat) # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should be_an_instance_of(Time) end it "should not catch a change until the timeout has elapsed" do # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() # just didn't do the job. File.stubs(:stat).returns(@stat) @loaded.changed?.should be(false) # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should_not be(false) end it "should consider a file changed when that file is missing" do @f.close! # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should_not be(false) end it "should disable checking if Puppet[:filetimeout] is negative" do Puppet[:filetimeout] = -1 @loaded.changed?.should_not be(false) end after(:each) do @f.close end end diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb index b84c2ab44..873c5f0c7 100755 --- a/spec/unit/util/log/destinations_spec.rb +++ b/spec/unit/util/log/destinations_spec.rb @@ -1,37 +1,36 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/log' describe Puppet::Util::Log.desttypes[:report] do before do @dest = Puppet::Util::Log.desttypes[:report] end it "should require a report at initialization" do @dest.new("foo").report.should == "foo" end it "should send new messages to the report" do report = mock 'report' dest = @dest.new(report) report.expects(:<<).with("my log") dest.handle "my log" end end describe Puppet::Util::Log.desttypes[:file] do before do File.stubs(:open) # prevent actually creating the file @class = Puppet::Util::Log.desttypes[:file] end it "should default to autoflush false" do @class.new('/tmp/log').autoflush.should == false end end diff --git a/spec/unit/util/log_spec.rb b/spec/unit/util/log_spec.rb index 3c8577493..78411d187 100755 --- a/spec/unit/util/log_spec.rb +++ b/spec/unit/util/log_spec.rb @@ -1,227 +1,226 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/log' describe Puppet::Util::Log do it "should write a given message to the specified destination" do arraydest = [] Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(arraydest)) Puppet::Util::Log.new(:level => :notice, :message => "foo") message = arraydest.last.message message.should == "foo" end describe Puppet::Util::Log::DestConsole do before do @console = Puppet::Util::Log::DestConsole.new end it "should colorize if Puppet[:color] is :ansi" do Puppet[:color] = :ansi @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" end it "should colorize if Puppet[:color] is 'yes'" do Puppet[:color] = "yes" @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" end it "should htmlize if Puppet[:color] is :html" do Puppet[:color] = :html @console.colorize(:alert, "abc").should == "abc" end it "should do nothing if Puppet[:color] is false" do Puppet[:color] = false @console.colorize(:alert, "abc").should == "abc" end it "should do nothing if Puppet[:color] is invalid" do Puppet[:color] = "invalid option" @console.colorize(:alert, "abc").should == "abc" end end describe "instances" do before do Puppet::Util::Log.stubs(:newmessage) end [:level, :message, :time, :remote].each do |attr| it "should have a #{attr} attribute" do log = Puppet::Util::Log.new :level => :notice, :message => "A test message" log.should respond_to(attr) log.should respond_to(attr.to_s + "=") end end it "should fail if created without a level" do lambda { Puppet::Util::Log.new(:message => "A test message") }.should raise_error(ArgumentError) end it "should fail if created without a message" do lambda { Puppet::Util::Log.new(:level => :notice) }.should raise_error(ArgumentError) end it "should make available the level passed in at initialization" do Puppet::Util::Log.new(:level => :notice, :message => "A test message").level.should == :notice end it "should make available the message passed in at initialization" do Puppet::Util::Log.new(:level => :notice, :message => "A test message").message.should == "A test message" end # LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code, # at least at first. it "should always convert messages to strings" do Puppet::Util::Log.new(:level => :notice, :message => :foo).message.should == "foo" end it "should flush the log queue when the first destination is specified" do Puppet::Util::Log.close_all Puppet::Util::Log.expects(:flushqueue) Puppet::Util::Log.newdestination(:console) end it "should convert the level to a symbol if it's passed in as a string" do Puppet::Util::Log.new(:level => "notice", :message => :foo).level.should == :notice end it "should fail if the level is not a symbol or string" do lambda { Puppet::Util::Log.new(:level => 50, :message => :foo) }.should raise_error(ArgumentError) end it "should fail if the provided level is not valid" do Puppet::Util::Log.expects(:validlevel?).with(:notice).returns false lambda { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.should raise_error(ArgumentError) end it "should set its time to the initialization time" do time = mock 'time' Time.expects(:now).returns time Puppet::Util::Log.new(:level => "notice", :message => :foo).time.should equal(time) end it "should make available any passed-in tags" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{foo bar}) log.tags.should be_include("foo") log.tags.should be_include("bar") end it "should use an passed-in source" do Puppet::Util::Log.any_instance.expects(:source=).with "foo" Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "foo") end [:file, :line].each do |attr| it "should use #{attr} if provided" do Puppet::Util::Log.any_instance.expects(attr.to_s + "=").with "foo" Puppet::Util::Log.new(:level => "notice", :message => :foo, attr => "foo") end end it "should default to 'Puppet' as its source" do Puppet::Util::Log.new(:level => "notice", :message => :foo).source.should == "Puppet" end it "should register itself with Log" do Puppet::Util::Log.expects(:newmessage) Puppet::Util::Log.new(:level => "notice", :message => :foo) end it "should update Log autoflush when Puppet[:autoflush] is set" do Puppet::Util::Log.expects(:autoflush=).once.with(true) Puppet[:autoflush] = true end it "should have a method for determining if a tag is present" do Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:tagged?) end it "should match a tag if any of the tags are equivalent to the passed tag as a string" do Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{one two}).should be_tagged(:one) end it "should tag itself with its log level" do Puppet::Util::Log.new(:level => "notice", :message => :foo).should be_tagged(:notice) end it "should return its message when converted to a string" do Puppet::Util::Log.new(:level => "notice", :message => :foo).to_s.should == "foo" end it "should include its time, source, level, and message when prepared for reporting" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo) report = log.to_report report.should be_include("notice") report.should be_include("foo") report.should be_include(log.source) report.should be_include(log.time.to_s) end describe "when setting the source as a RAL object" do it "should tag itself with any tags the source has" do source = Puppet::Type.type(:file).new :path => "/foo/bar" log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) source.tags.each do |tag| log.tags.should be_include(tag) end end it "should use the source_descriptors" do source = stub "source" source.stubs(:source_descriptors).returns(:tags => ["tag","tag2"], :path => "path", :version => 100) log = Puppet::Util::Log.new(:level => "notice", :message => :foo) log.expects(:tag).with("tag") log.expects(:tag).with("tag2") log.source = source log.source.should == "path" end it "should copy over any file and line information" do source = Puppet::Type.type(:file).new :path => "/foo/bar" source.file = "/my/file" source.line = 50 log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) log.file.should == "/my/file" log.line.should == 50 end end describe "when setting the source as a non-RAL object" do it "should not try to copy over file, version, line, or tag information" do source = Puppet::Module.new("foo") source.expects(:file).never log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) end end end describe "to_yaml" do it "should not include the @version attribute" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :version => 100) log.to_yaml_properties.should_not include('@version') end it "should include attributes @level, @message, @source, @tags, and @time" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :version => 100) log.to_yaml_properties.should == %w{@level @message @source @tags @time} end it "should include attributes @file and @line if specified" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :file => "foo", :line => 35) log.to_yaml_properties.should include('@file') log.to_yaml_properties.should include('@line') end end end diff --git a/spec/unit/util/logging_spec.rb b/spec/unit/util/logging_spec.rb index 97ffc0b25..6a77e70ef 100755 --- a/spec/unit/util/logging_spec.rb +++ b/spec/unit/util/logging_spec.rb @@ -1,95 +1,119 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/logging' class LoggingTester include Puppet::Util::Logging end describe Puppet::Util::Logging do before do @logger = LoggingTester.new end Puppet::Util::Log.eachlevel do |level| it "should have a method for sending '#{level}' logs" do @logger.should respond_to(level) end end it "should have a method for sending a log with a specified log level" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" and args[:level] == "loglevel" and args[:message] == "mymessage" } @logger.send_log "loglevel", "mymessage" end describe "when sending a log" do it "should use the Log's 'create' entrance method" do Puppet::Util::Log.expects(:create) @logger.notice "foo" end it "should send itself converted to a string as the log source" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" } @logger.notice "foo" end it "should queue logs sent without a specified destination" do Puppet::Util::Log.close_all Puppet::Util::Log.expects(:queuemessage) @logger.notice "foo" end it "should use the path of any provided resource type" do resource = Puppet::Type.type(:mount).new :name => "foo" resource.expects(:path).returns "/path/to/mount".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/mount" } resource.notice "foo" end it "should use the path of any provided resource parameter" do resource = Puppet::Type.type(:mount).new :name => "foo" param = resource.parameter(:name) param.expects(:path).returns "/path/to/param".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/param" } param.notice "foo" end it "should send the provided argument as the log message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo" } @logger.notice "foo" end it "should join any provided arguments into a single string for the message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo bar baz" } @logger.notice ["foo", "bar", "baz"] end [:file, :line, :tags].each do |attr| it "should include #{attr} if available" do @logger.singleton_class.send(:attr_accessor, attr) @logger.send(attr.to_s + "=", "myval") Puppet::Util::Log.expects(:create).with { |args| args[attr] == "myval" } @logger.notice "foo" end end end + + describe "when sending a deprecation warning" do + before do + @logger.clear_deprecation_warnings + end + + it "should the message with warn" do + @logger.expects(:warning).with('foo') + @logger.deprecation_warning 'foo' + end + + it "should only log each unique message once" do + @logger.expects(:warning).with('foo').once + 5.times { @logger.deprecation_warning 'foo' } + end + + it "should only log the first 100 messages" do + (1..100).each { |i| + @logger.expects(:warning).with(i).once + @logger.deprecation_warning i + } + @logger.expects(:warning).with(101).never + @logger.deprecation_warning 101 + end + end end diff --git a/spec/unit/util/metric_spec.rb b/spec/unit/util/metric_spec.rb index 425812297..07a9e4945 100755 --- a/spec/unit/util/metric_spec.rb +++ b/spec/unit/util/metric_spec.rb @@ -1,95 +1,94 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/metric' describe Puppet::Util::Metric do before do @metric = Puppet::Util::Metric.new("foo") end it "should be aliased to Puppet::Metric" do Puppet::Util::Metric.should equal(Puppet::Metric) end [:type, :name, :value, :label, :basedir].each do |name| it "should have a #{name} attribute" do @metric.should respond_to(name) @metric.should respond_to(name.to_s + "=") end end it "should default to the :rrdir as the basedir "do Puppet.settings.expects(:value).with(:rrddir).returns "myrrd" @metric.basedir.should == "myrrd" end it "should use any provided basedir" do @metric.basedir = "foo" @metric.basedir.should == "foo" end it "should require a name at initialization" do lambda { Puppet::Util::Metric.new }.should raise_error(ArgumentError) end it "should always convert its name to a string" do Puppet::Util::Metric.new(:foo).name.should == "foo" end it "should support a label" do Puppet::Util::Metric.new("foo", "mylabel").label.should == "mylabel" end it "should autogenerate a label if none is provided" do Puppet::Util::Metric.new("foo_bar").label.should == "Foo bar" end it "should have a method for adding values" do @metric.should respond_to(:newvalue) end it "should have a method for returning values" do @metric.should respond_to(:values) end it "should require a name and value for its values" do lambda { @metric.newvalue }.should raise_error(ArgumentError) end it "should support a label for values" do @metric.newvalue("foo", 10, "label") @metric.values[0][1].should == "label" end it "should autogenerate value labels if none is provided" do @metric.newvalue("foo_bar", 10) @metric.values[0][1].should == "Foo bar" end it "should return its values sorted by label" do @metric.newvalue("foo", 10, "b") @metric.newvalue("bar", 10, "a") @metric.values.should == [["bar", "a", 10], ["foo", "b", 10]] end it "should use an array indexer method to retrieve individual values" do @metric.newvalue("foo", 10) @metric["foo"].should == 10 end it "should return nil if the named value cannot be found" do @metric["foo"].should == 0 end # LAK: I'm not taking the time to develop these tests right now. # I expect they should actually be extracted into a separate class # anyway. it "should be able to graph metrics using RRDTool" it "should be able to create a new RRDTool database" it "should be able to store metrics into an RRDTool database" end diff --git a/spec/unit/util/monkey_patches_spec.rb b/spec/unit/util/monkey_patches_spec.rb old mode 100644 new mode 100755 index 8bfcd900e..4b609ad09 --- a/spec/unit/util/monkey_patches_spec.rb +++ b/spec/unit/util/monkey_patches_spec.rb @@ -1,33 +1,55 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/monkey_patches' describe "yaml deserialization" do it "should call yaml_initialize when deserializing objects that have that method defined" do class Puppet::TestYamlInitializeClass attr_reader :foo def yaml_initialize(tag, var) var.should == {'foo' => 100} instance_variables.should == [] @foo = 200 end end obj = YAML.load("--- !ruby/object:Puppet::TestYamlInitializeClass\n foo: 100") obj.foo.should == 200 end it "should not call yaml_initialize if not defined" do class Puppet::TestYamlNonInitializeClass attr_reader :foo end obj = YAML.load("--- !ruby/object:Puppet::TestYamlNonInitializeClass\n foo: 100") obj.foo.should == 100 end end + +# In Ruby > 1.8.7 this is a builtin, otherwise we monkey patch the method in +describe "Array#combination" do + it "should fail if wrong number of arguments given" do + lambda { [1,2,3].combination() }.should raise_error(ArgumentError, /wrong number/) + lambda { [1,2,3].combination(1,2) }.should raise_error(ArgumentError, /wrong number/) + end + + it "should return an empty array if combo size than array size or negative" do + [1,2,3].combination(4).to_a.should == [] + [1,2,3].combination(-1).to_a.should == [] + end + + it "should return an empty array with an empty array if combo size == 0" do + [1,2,3].combination(0).to_a.should == [[]] + end + + it "should all provide all combinations of size passed in" do + [1,2,3,4].combination(1).to_a.should == [[1], [2], [3], [4]] + [1,2,3,4].combination(2).to_a.should == [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] + [1,2,3,4].combination(3).to_a.should == [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] + end +end diff --git a/spec/unit/util/nagios_maker_spec.rb b/spec/unit/util/nagios_maker_spec.rb index 20582525d..b61f4fe9d 100755 --- a/spec/unit/util/nagios_maker_spec.rb +++ b/spec/unit/util/nagios_maker_spec.rb @@ -1,126 +1,126 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-18. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/util/nagios_maker' describe Puppet::Util::NagiosMaker do before do @module = Puppet::Util::NagiosMaker @nagtype = stub 'nagios type', :parameters => [], :namevar => :name Nagios::Base.stubs(:type).with(:test).returns(@nagtype) @provider = stub 'provider', :nagios_type => nil @type = stub 'type', :newparam => nil, :newproperty => nil, :provide => @provider, :desc => nil, :ensurable => nil end it "should be able to create a new nagios type" do @module.should respond_to(:create_nagios_type) end it "should fail if it cannot find the named Naginator type" do Nagios::Base.stubs(:type).returns(nil) lambda { @module.create_nagios_type(:no_such_type) }.should raise_error(Puppet::DevError) end it "should create a new RAL type with the provided name prefixed with 'nagios_'" do Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should mark the created type as ensurable" do @type.expects(:ensurable) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should create a namevar parameter for the nagios type's name parameter" do @type.expects(:newparam).with(:name, :namevar => true) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should create a property for all non-namevar parameters" do @nagtype.stubs(:parameters).returns([:one, :two]) @type.expects(:newproperty).with(:one) @type.expects(:newproperty).with(:two) @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should skip parameters that start with integers" do @nagtype.stubs(:parameters).returns(["2dcoords".to_sym, :other]) @type.expects(:newproperty).with(:other) @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should deduplicate the parameter list" do @nagtype.stubs(:parameters).returns([:one, :one]) @type.expects(:newproperty).with(:one) @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should create a target property" do @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end end describe Puppet::Util::NagiosMaker, " when creating the naginator provider" do before do @module = Puppet::Util::NagiosMaker @provider = stub 'provider', :nagios_type => nil @nagtype = stub 'nagios type', :parameters => [], :namevar => :name Nagios::Base.stubs(:type).with(:test).returns(@nagtype) @type = stub 'type', :newparam => nil, :ensurable => nil, :newproperty => nil, :desc => nil Puppet::Type.stubs(:newtype).with(:nagios_test).returns(@type) end it "should add a naginator provider" do @type.expects(:provide).with { |name, options| name == :naginator }.returns @provider @module.create_nagios_type(:test) end it "should set Puppet::Provider::Naginator as the parent class of the provider" do @type.expects(:provide).with { |name, options| options[:parent] == Puppet::Provider::Naginator }.returns @provider @module.create_nagios_type(:test) end it "should use /etc/nagios/$name.cfg as the default target" do @type.expects(:provide).with { |name, options| options[:default_target] == "/etc/nagios/nagios_test.cfg" }.returns @provider @module.create_nagios_type(:test) end it "should trigger the lookup of the Nagios class" do @type.expects(:provide).returns @provider @provider.expects(:nagios_type) @module.create_nagios_type(:test) end end diff --git a/spec/unit/util/network_device/cisco/device_spec.rb b/spec/unit/util/network_device/cisco/device_spec.rb new file mode 100755 index 000000000..82b0666e6 --- /dev/null +++ b/spec/unit/util/network_device/cisco/device_spec.rb @@ -0,0 +1,521 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/util/network_device/cisco/device' + +describe Puppet::Util::NetworkDevice::Cisco::Device do + before(:each) do + @transport = stub_everything 'transport', :is_a? => true, :command => "" + @cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/") + @cisco.transport = @transport + end + + describe "when creating the device" do + it "should find the enable password from the url" do + cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password") + cisco.enable_password.should == "enable_password" + end + + it "should find the enable password from the options" do + cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password", :enable_password => "mypass") + cisco.enable_password.should == "mypass" + end + end + + describe "when connecting to the physical device" do + it "should connect to the transport" do + @transport.expects(:connect) + @cisco.command + end + + it "should attempt to login" do + @cisco.expects(:login) + @cisco.command + end + + it "should tell the device to not page" do + @transport.expects(:command).with("terminal length 0") + @cisco.command + end + + it "should enter the enable password if returned prompt is not privileged" do + @transport.stubs(:command).yields("Switch>").returns("") + @cisco.expects(:enable) + @cisco.command + end + + it "should find device capabilities" do + @cisco.expects(:find_capabilities) + @cisco.command + end + + it "should execute given command" do + @transport.expects(:command).with("mycommand") + @cisco.command("mycommand") + end + + it "should yield to the command block if one is provided" do + @transport.expects(:command).with("mycommand") + @cisco.command do |c| + c.command("mycommand") + end + end + + it "should close the device transport" do + @transport.expects(:close) + @cisco.command + end + + describe "when login in" do + it "should not login if transport handles login" do + @transport.expects(:handles_login?).returns(true) + @transport.expects(:command).never + @transport.expects(:expect).never + @cisco.login + end + + it "should send username if one has been provided" do + @transport.expects(:command).with("user", :prompt => /^Password:/) + @cisco.login + end + + it "should send password after the username" do + @transport.expects(:command).with("user", :prompt => /^Password:/) + @transport.expects(:command).with("password") + @cisco.login + end + + it "should expect the Password: prompt if no user was sent" do + @cisco.url.user = '' + @transport.expects(:expect).with(/^Password:/) + @transport.expects(:command).with("password") + @cisco.login + end + end + + describe "when entering enable password" do + it "should raise an error if no enable password has been set" do + @cisco.enable_password = nil + lambda{ @cisco.enable }.should raise_error + end + + it "should send the enable command and expect an enable prompt" do + @cisco.enable_password = 'mypass' + @transport.expects(:command).with("enable", :prompt => /^Password:/) + @cisco.enable + end + + it "should send the enable password" do + @cisco.enable_password = 'mypass' + @transport.stubs(:command).with("enable", :prompt => /^Password:/) + @transport.expects(:command).with("mypass") + @cisco.enable + end + end + end + + describe "when finding network device capabilities" do + it "should try to execute sh vlan brief" do + @transport.expects(:command).with("sh vlan brief").returns("") + @cisco.find_capabilities + end + + it "should detect errors" do + @transport.stubs(:command).with("sh vlan brief").returns(< "FastEthernet0/1", + "Fa0/1" => "FastEthernet0/1", + "FastEth 0/1" => "FastEthernet0/1", + "Gi1" => "GigEthernet1", + "Di9" => "Dialer9", + "Ethernet 0/0/1" => "Ethernet0/0/1", + "E0" => "Ethernet0", + "ATM 0/1.1" => "ATM0/1.1", + "VLAN99" => "VLAN99" + }.each do |input,expected| + it "should canonicalize #{input} to #{expected}" do + @cisco.canonalize_ifname(input).should == expected + end + end + + describe "when updating device vlans" do + describe "when removing a vlan" do + it "should issue the no vlan command" do + @transport.expects(:command).with("no vlan 200") + @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :absent}) + end + end + + describe "when updating a vlan" do + it "should issue the vlan command to enter global vlan modifications" do + @transport.expects(:command).with("vlan 200") + @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200"}) + end + + it "should issue the name command to modify the vlan description" do + @transport.expects(:command).with("name myvlan") + @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200", :description => "myvlan"}) + end + end + end + + describe "when parsing interface" do + + it "should parse interface output" do + @cisco.expects(:parse_interface).returns({ :ensure => :present }) + + @cisco.interface("FastEthernet0/1").should == { :ensure => :present } + end + + it "should parse trunking and merge results" do + @cisco.stubs(:parse_interface).returns({ :ensure => :present }) + @cisco.expects(:parse_trunking).returns({ :native_vlan => "100" }) + + @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :native_vlan => "100" } + end + + it "should return an absent interface if parse_interface returns nothing" do + @cisco.stubs(:parse_interface).returns({}) + + @cisco.interface("FastEthernet0/1").should == { :ensure => :absent } + end + + it "should parse ip address information and merge results" do + @cisco.stubs(:parse_interface).returns({ :ensure => :present }) + @cisco.expects(:parse_interface_config).returns({ :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] }) + + @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] } + end + + it "should parse the sh interface command" do + @transport.stubs(:command).with("sh interface FastEthernet0/1").returns(< :absent, :duplex => :auto, :speed => :auto } + end + + it "should be able to parse the sh vlan brief command output" do + @cisco.stubs(:support_vlan_brief?).returns(true) + @transport.stubs(:command).with("sh vlan brief").returns(<{:status=>"active", :interfaces=>["FastEthernet0/1", "FastEthernet0/2"], :description=>"management", :name=>"100"}, "1"=>{:status=>"active", :interfaces=>["FastEthernet0/3", "FastEthernet0/4", "FastEthernet0/5", "FastEthernet0/6", "FastEthernet0/7", "FastEthernet0/8", "FastEthernet0/9", "FastEthernet0/10", "FastEthernet0/11", "FastEthernet0/12", "FastEthernet0/13", "FastEthernet0/14", "FastEthernet0/15", "FastEthernet0/16", "FastEthernet0/17", "FastEthernet0/18", "FastEthernet0/23", "FastEthernet0/24"], :description=>"default", :name=>"1"}, "10"=>{:status=>"active", :interfaces=>[], :description=>"VLAN0010", :name=>"10"}} + end + + it "should parse trunk switchport information" do + @transport.stubs(:command).with("sh interface FastEthernet0/21 switchport").returns(< :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>:all, } + end + + it "should parse trunk switchport information with allowed vlans" do + @transport.stubs(:command).with("sh interface GigabitEthernet 0/1 switchport").returns(< :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>"1,99", } + end + + it "should parse access switchport information" do + @transport.stubs(:command).with("sh interface FastEthernet0/1 switchport").returns(< :access, :native_vlan => "100" } + end + + it "should parse ip addresses" do + @transport.stubs(:command).with("sh running-config interface Vlan 1 | begin interface").returns(<[[24, IPAddr.new('192.168.0.24'), 'secondary'], + [24, IPAddr.new('192.168.0.1'), nil], + [64, IPAddr.new('2001:07a8:71c1::'), "eui-64"]]} + end + + it "should parse etherchannel membership" do + @transport.stubs(:command).with("sh running-config interface Gi0/17 | begin interface").returns(<"1"} + end + end +end + +# static access +# Switch#sh interfaces FastEthernet 0/1 switchport +# Name: Fa0/1 +# Switchport: Enabled +# Administrative mode: static access +# Operational Mode: static access +# Administrative Trunking Encapsulation: isl +# Operational Trunking Encapsulation: isl +# Negotiation of Trunking: Disabled +# Access Mode VLAN: 100 (SHDSL) +# Trunking Native Mode VLAN: 1 (default) +# Trunking VLANs Enabled: NONE +# Pruning VLANs Enabled: NONE +# +# Priority for untagged frames: 0 +# Override vlan tag priority: FALSE +# Voice VLAN: none +# Appliance trust: none +# Self Loopback: No +# Switch# + +# c2960#sh interfaces GigabitEthernet 0/1 switchport +# Name: Gi0/1 +# Switchport: Enabled +# Administrative Mode: trunk +# Operational Mode: trunk +# Administrative Trunking Encapsulation: dot1q +# Operational Trunking Encapsulation: dot1q +# Negotiation of Trunking: On +# Access Mode VLAN: 1 (default) +# Trunking Native Mode VLAN: 1 (default) +# Administrative Native VLAN tagging: enabled +# Voice VLAN: none +# Administrative private-vlan host-association: none +# Administrative private-vlan mapping: none +# Administrative private-vlan trunk native VLAN: none +# Administrative private-vlan trunk Native VLAN tagging: enabled +# Administrative private-vlan trunk encapsulation: dot1q +# Administrative private-vlan trunk normal VLANs: none +# Administrative private-vlan trunk associations: none +# Administrative private-vlan trunk mappings: none +# Operational private-vlan: none +# Trunking VLANs Enabled: 1,99 +# Pruning VLANs Enabled: 2-1001 +# Capture Mode Disabled +# Capture VLANs Allowed: ALL +# +# Protected: false +# Unknown unicast blocked: disabled +# Unknown multicast blocked: disabled +# Appliance trust: none +# c2960# + +# c2960#sh interfaces GigabitEthernet 0/2 switchport +# Name: Gi0/2 +# Switchport: Enabled +# Administrative Mode: static access +# Operational Mode: static access +# Administrative Trunking Encapsulation: dot1q +# Operational Trunking Encapsulation: native +# Negotiation of Trunking: Off +# Access Mode VLAN: 99 (MGMT) +# Trunking Native Mode VLAN: 1 (default) +# Administrative Native VLAN tagging: enabled +# Voice VLAN: none +# Administrative private-vlan host-association: none +# Administrative private-vlan mapping: none +# Administrative private-vlan trunk native VLAN: none +# Administrative private-vlan trunk Native VLAN tagging: enabled +# Administrative private-vlan trunk encapsulation: dot1q +# Administrative private-vlan trunk normal VLANs: none +# Administrative private-vlan trunk associations: none +# Administrative private-vlan trunk mappings: none +# Operational private-vlan: none +# Trunking VLANs Enabled: ALL +# Pruning VLANs Enabled: 2-1001 +# Capture Mode Disabled +# Capture VLANs Allowed: ALL +# +# Protected: false +# Unknown unicast blocked: disabled +# Unknown multicast blocked: disabled +# Appliance trust: none +# c2960# + +# c877#sh interfaces FastEthernet 1 switchport +# Name: Fa1 +# Switchport: Enabled +# Administrative Mode: trunk +# Operational Mode: trunk +# Administrative Trunking Encapsulation: dot1q +# Operational Trunking Encapsulation: dot1q +# Negotiation of Trunking: Disabled +# Access Mode VLAN: 0 ((Inactive)) +# Trunking Native Mode VLAN: 1 (default) +# Trunking VLANs Enabled: ALL +# Trunking VLANs Active: 1 +# Protected: false +# Priority for untagged frames: 0 +# Override vlan tag priority: FALSE +# Voice VLAN: none +# Appliance trust: none + + +# c2960#sh etherchannel summary +# Flags: D - down P - bundled in port-channel +# I - stand-alone s - suspended +# H - Hot-standby (LACP only) +# R - Layer3 S - Layer2 +# U - in use f - failed to allocate aggregator +# +# M - not in use, minimum links not met +# u - unsuitable for bundling +# w - waiting to be aggregated +# d - default port +# +# +# Number of channel-groups in use: 1 +# Number of aggregators: 1 +# +# Group Port-channel Protocol Ports +# ------+-------------+-----------+----------------------------------------------- +# 1 Po1(SU) LACP Gi0/17(P) Gi0/18(P) +# +# c2960# diff --git a/spec/unit/util/network_device/cisco/interface_spec.rb b/spec/unit/util/network_device/cisco/interface_spec.rb new file mode 100755 index 000000000..24217750c --- /dev/null +++ b/spec/unit/util/network_device/cisco/interface_spec.rb @@ -0,0 +1,89 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/util/network_device' +require 'puppet/util/network_device/cisco/interface' + +describe Puppet::Util::NetworkDevice::Cisco::Interface do + before(:each) do + @transport = stub_everything 'transport' + @interface = Puppet::Util::NetworkDevice::Cisco::Interface.new("FastEthernet0/1",@transport) + end + + it "should include IPCalc" do + @interface.class.include?(Puppet::Util::NetworkDevice::IPCalc) + end + + describe "when updating the physical device" do + it "should enter global configuration mode" do + @transport.expects(:command).with("conf t") + @interface.update + end + + it "should enter interface configuration mode" do + @transport.expects(:command).with("interface FastEthernet0/1") + @interface.update + end + + it "should 'execute' all differing properties" do + @interface.expects(:execute).with(:description, "b") + @interface.expects(:execute).with(:mode, :access).never + @interface.update({ :description => "a", :mode => :access }, { :description => "b", :mode => :access }) + end + + it "should execute in cisco ios defined order" do + speed = states('speed').starts_as('notset') + @interface.expects(:execute).with(:speed, :auto).then(speed.is('set')) + @interface.expects(:execute).with(:duplex, :auto).when(speed.is('set')) + @interface.update({ :duplex => :half, :speed => "10" }, { :duplex => :auto, :speed => :auto }) + end + + it "should execute absent properties with a no prefix" do + @interface.expects(:execute).with(:description, "a", "no ") + @interface.update({ :description => "a"}, { }) + end + + it "should exit twice" do + @transport.expects(:command).with("exit").twice + @interface.update + end + end + + describe "when executing commands" do + it "should execute string commands directly" do + @transport.expects(:command).with("speed auto") + @interface.execute(:speed, :auto) + end + + it "should execute string commands with the given prefix" do + @transport.expects(:command).with("no speed auto") + @interface.execute(:speed, :auto, "no ") + end + + it "should stop at executing the first command that works for array" do + @transport.expects(:command).with("channel-group 1").yields("% Invalid command") + @transport.expects(:command).with("port group 1") + @interface.execute(:etherchannel, "1") + end + + it "should execute the block for block commands" do + @transport.expects(:command).with("ip address 192.168.0.1 255.255.255.0") + @interface.execute(:ipaddress, [[24, IPAddr.new('192.168.0.1'), nil]]) + end + + it "should execute the block for block commands" do + @transport.expects(:command).with("ipv6 address fe08::/76 link-local") + @interface.execute(:ipaddress, [[76, IPAddr.new('fe08::'), 'link-local']]) + end + + end + + describe "when sending commands to the device" do + it "should detect errors" do + Puppet.expects(:err) + @transport.stubs(:command).yields("% Invalid Command") + @interface.command("sh ver") + end + end +end diff --git a/spec/unit/util/network_device/ipcalc_spec.rb b/spec/unit/util/network_device/ipcalc_spec.rb new file mode 100755 index 000000000..0418c6a84 --- /dev/null +++ b/spec/unit/util/network_device/ipcalc_spec.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/util/network_device/ipcalc' + +describe Puppet::Util::NetworkDevice::IPCalc do + class TestIPCalc + include Puppet::Util::NetworkDevice::IPCalc + end + + before(:each) do + @ipcalc = TestIPCalc.new + end + + describe "when parsing ip/prefix" do + it "should parse ipv4 without prefixes" do + @ipcalc.parse('127.0.0.1').should == [32,IPAddr.new('127.0.0.1')] + end + + it "should parse ipv4 with prefixes" do + @ipcalc.parse('127.0.1.2/8').should == [8,IPAddr.new('127.0.1.2')] + end + + it "should parse ipv6 without prefixes" do + @ipcalc.parse('FE80::21A:2FFF:FE30:ECF0').should == [128,IPAddr.new('FE80::21A:2FFF:FE30:ECF0')] + end + + it "should parse ipv6 with prefixes" do + @ipcalc.parse('FE80::21A:2FFF:FE30:ECF0/56').should == [56,IPAddr.new('FE80::21A:2FFF:FE30:ECF0')] + end + end + + describe "when building netmask" do + it "should produce the correct ipv4 netmask from prefix length" do + @ipcalc.netmask(Socket::AF_INET, 27).should == IPAddr.new('255.255.255.224') + end + + it "should produce the correct ipv6 netmask from prefix length" do + @ipcalc.netmask(Socket::AF_INET6, 56).should == IPAddr.new('ffff:ffff:ffff:ff00::0') + end + end + + describe "when building wildmask" do + it "should produce the correct ipv4 wildmask from prefix length" do + @ipcalc.wildmask(Socket::AF_INET, 27).should == IPAddr.new('0.0.0.31') + end + + it "should produce the correct ipv6 wildmask from prefix length" do + @ipcalc.wildmask(Socket::AF_INET6, 126).should == IPAddr.new('::3') + end + end + + describe "when computing prefix length from netmask" do + it "should produce the correct ipv4 prefix length" do + @ipcalc.prefix_length(IPAddr.new('255.255.255.224')).should == 27 + end + + it "should produce the correct ipv6 prefix length" do + @ipcalc.prefix_length(IPAddr.new('fffe::0')).should == 15 + end + end +end diff --git a/spec/unit/util/network_device/transport/base_spec.rb b/spec/unit/util/network_device/transport/base_spec.rb new file mode 100755 index 000000000..c186d72e5 --- /dev/null +++ b/spec/unit/util/network_device/transport/base_spec.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/util/network_device/transport/base' + +describe Puppet::Util::NetworkDevice::Transport::Base do + class TestTransport < Puppet::Util::NetworkDevice::Transport::Base + end + + before(:each) do + @transport = TestTransport.new + end + + describe "when sending commands" do + it "should send the command to the telnet session" do + @transport.expects(:send).with("line") + @transport.command("line") + end + + it "should expect an output matching the given prompt" do + @transport.expects(:expect).with(/prompt/) + @transport.command("line", :prompt => /prompt/) + end + + it "should expect an output matching the default prompt" do + @transport.default_prompt = /defprompt/ + @transport.expects(:expect).with(/defprompt/) + @transport.command("line") + end + + it "should yield telnet output to the given block" do + @transport.expects(:expect).yields("output") + @transport.command("line") { |out| out.should == "output" } + end + + it "should return telnet output to the caller" do + @transport.expects(:expect).returns("output") + @transport.command("line").should == "output" + end + end +end diff --git a/spec/unit/util/network_device/transport/ssh_spec.rb b/spec/unit/util/network_device/transport/ssh_spec.rb new file mode 100755 index 000000000..0e91ed9f9 --- /dev/null +++ b/spec/unit/util/network_device/transport/ssh_spec.rb @@ -0,0 +1,211 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/util/network_device/transport/ssh' + +describe Puppet::Util::NetworkDevice::Transport::Ssh, :if => Puppet.features.ssh? do + + before(:each) do + @transport = Puppet::Util::NetworkDevice::Transport::Ssh.new() + end + + it "should handle login through the transport" do + @transport.should be_handles_login + end + + it "should connect to the given host and port" do + Net::SSH.expects(:start).with { |host, user, args| host == "localhost" && args[:port] == 22 }.returns stub_everything + @transport.host = "localhost" + @transport.port = 22 + + @transport.connect + end + + it "should connect using the given username and password" do + Net::SSH.expects(:start).with { |host, user, args| user == "user" && args[:password] == "pass" }.returns stub_everything + @transport.user = "user" + @transport.password = "pass" + + @transport.connect + end + + describe "when connected" do + before(:each) do + @ssh = stub_everything 'ssh' + @channel = stub_everything 'channel' + Net::SSH.stubs(:start).returns @ssh + @ssh.stubs(:open_channel).yields(@channel) + @transport.stubs(:expect) + end + + it "should open a channel" do + @ssh.expects(:open_channel) + + @transport.connect + end + + it "should request a pty" do + @channel.expects(:request_pty) + + @transport.connect + end + + it "should create a shell channel" do + @channel.expects(:send_channel_request).with("shell") + @transport.connect + end + + it "should raise an error if shell channel creation fails" do + @channel.expects(:send_channel_request).with("shell").yields(@channel, false) + lambda { @transport.connect }.should raise_error + end + + it "should register an on_data and on_extended_data callback" do + @channel.expects(:send_channel_request).with("shell").yields(@channel, true) + @channel.expects(:on_data) + @channel.expects(:on_extended_data) + @transport.connect + end + + it "should accumulate data to the buffer on data" do + @channel.expects(:send_channel_request).with("shell").yields(@channel, true) + @channel.expects(:on_data).yields(@channel, "data") + + @transport.connect + @transport.buf.should == "data" + end + + it "should accumulate data to the buffer on extended data" do + @channel.expects(:send_channel_request).with("shell").yields(@channel, true) + @channel.expects(:on_extended_data).yields(@channel, 1, "data") + + @transport.connect + @transport.buf.should == "data" + end + + it "should mark eof on close" do + @channel.expects(:send_channel_request).with("shell").yields(@channel, true) + @channel.expects(:on_close).yields(@channel) + + @transport.connect + @transport.should be_eof + end + + it "should expect output to conform to the default prompt" do + @channel.expects(:send_channel_request).with("shell").yields(@channel, true) + @transport.expects(:default_prompt).returns("prompt") + @transport.expects(:expect).with("prompt") + @transport.connect + end + + it "should start the ssh loop" do + @ssh.expects(:loop) + @transport.connect + end + end + + describe "when closing" do + before(:each) do + @ssh = stub_everything 'ssh' + @channel = stub_everything 'channel' + Net::SSH.stubs(:start).returns @ssh + @ssh.stubs(:open_channel).yields(@channel) + @channel.stubs(:send_channel_request).with("shell").yields(@channel, true) + @transport.stubs(:expect) + @transport.connect + end + + it "should close the channel" do + @channel.expects(:close) + @transport.close + end + + it "should close the ssh session" do + @ssh.expects(:close) + @transport.close + end + end + + describe "when sending commands" do + before(:each) do + @ssh = stub_everything 'ssh' + @channel = stub_everything 'channel' + Net::SSH.stubs(:start).returns @ssh + @ssh.stubs(:open_channel).yields(@channel) + @channel.stubs(:send_channel_request).with("shell").yields(@channel, true) + @transport.stubs(:expect) + @transport.connect + end + + it "should send data to the ssh channel" do + @channel.expects(:send_data).with("data\n") + @transport.command("data") + end + + it "should expect the default prompt afterward" do + @transport.expects(:default_prompt).returns("prompt") + @transport.expects(:expect).with("prompt") + @transport.command("data") + end + + it "should expect the given prompt" do + @transport.expects(:expect).with("myprompt") + @transport.command("data", :prompt => "myprompt") + end + + it "should yield the buffer output to given block" do + @transport.expects(:expect).yields("output") + @transport.command("data") do |out| + out.should == "output" + end + end + + it "should return buffer output" do + @transport.expects(:expect).returns("output") + @transport.command("data").should == "output" + end + end + + describe "when expecting output" do + before(:each) do + @connection = stub_everything 'connection' + @socket = stub_everything 'socket' + transport = stub 'transport', :socket => @socket + @ssh = stub_everything 'ssh', :transport => transport + @channel = stub_everything 'channel', :connection => @connection + @transport.ssh = @ssh + @transport.channel = @channel + end + + it "should process the ssh event loop" do + IO.stubs(:select) + @transport.buf = "output" + @transport.expects(:process_ssh) + @transport.expect(/output/) + end + + it "should return the output" do + IO.stubs(:select) + @transport.buf = "output" + @transport.stubs(:process_ssh) + @transport.expect(/output/).should == "output" + end + + it "should return the output" do + IO.stubs(:select) + @transport.buf = "output" + @transport.stubs(:process_ssh) + @transport.expect(/output/).should == "output" + end + + describe "when processing the ssh loop" do + it "should advance one tick in the ssh event loop and exit on eof" do + @transport.buf = '' + @connection.expects(:process).then.raises(EOFError) + @transport.process_ssh + end + end + end + +end diff --git a/spec/unit/util/network_device/transport/telnet_spec.rb b/spec/unit/util/network_device/transport/telnet_spec.rb new file mode 100755 index 000000000..7528e0740 --- /dev/null +++ b/spec/unit/util/network_device/transport/telnet_spec.rb @@ -0,0 +1,76 @@ +#!/usr/bin/env rspec + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/util/network_device/transport/telnet' + +describe Puppet::Util::NetworkDevice::Transport::Telnet do + + before(:each) do + @transport = Puppet::Util::NetworkDevice::Transport::Telnet.new() + end + + it "should not handle login through the transport" do + @transport.should_not be_handles_login + end + + it "should connect to the given host and port" do + Net::Telnet.expects(:new).with { |args| args["Host"] == "localhost" && args["Port"] == 23 }.returns stub_everything + @transport.host = "localhost" + @transport.port = 23 + + @transport.connect + end + + it "should connect and specify the default prompt" do + @transport.default_prompt = "prompt" + Net::Telnet.expects(:new).with { |args| args["Prompt"] == "prompt" }.returns stub_everything + @transport.host = "localhost" + @transport.port = 23 + + @transport.connect + end + + describe "when connected" do + before(:each) do + @telnet = stub_everything 'telnet' + Net::Telnet.stubs(:new).returns(@telnet) + @transport.connect + end + + it "should send line to the telnet session" do + @telnet.expects(:puts).with("line") + @transport.send("line") + end + + describe "when expecting output" do + it "should waitfor output on the telnet session" do + @telnet.expects(:waitfor).with(/regex/) + @transport.expect(/regex/) + end + + it "should return telnet session output" do + @telnet.expects(:waitfor).returns("output") + @transport.expect(/regex/).should == "output" + end + + it "should yield telnet session output to the given block" do + @telnet.expects(:waitfor).yields("output") + @transport.expect(/regex/) { |out| out.should == "output" } + end + end + end + + describe "when closing" do + before(:each) do + @telnet = stub_everything 'telnet' + Net::Telnet.stubs(:new).returns(@telnet) + @transport.connect + end + + it "should close the telnet session" do + @telnet.expects(:close) + @transport.close + end + end +end diff --git a/spec/unit/util/package_spec.rb b/spec/unit/util/package_spec.rb old mode 100644 new mode 100755 index e72a7ae91..78c114afc --- a/spec/unit/util/package_spec.rb +++ b/spec/unit/util/package_spec.rb @@ -1,21 +1,20 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/package' describe Puppet::Util::Package, " versioncmp" do it "should be able to be used as a module function" do Puppet::Util::Package.should respond_to(:versioncmp) end it "should be able to sort a long set of various unordered versions" do ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06} newary = ary.sort { |a, b| Puppet::Util::Package.versioncmp(a,b) } newary.should == ["0002", "1", "1.06", "1.1-3", "1.1-4", "1.1-5", "1.1.6", "1.1.a", "1.1a", "1.2", "1.5", "2.3", "2.3.0", "2.3.1", "2.3a.1", "2.4", "2.4", "2.4b", "2.40.2", "3.0", "3.1"] end end diff --git a/spec/unit/util/posix_spec.rb b/spec/unit/util/posix_spec.rb index 6d7351220..db7c5c3a7 100755 --- a/spec/unit/util/posix_spec.rb +++ b/spec/unit/util/posix_spec.rb @@ -1,256 +1,255 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/posix' class PosixTest include Puppet::Util::POSIX end describe Puppet::Util::POSIX do before do @posix = PosixTest.new end [:group, :gr].each do |name| it "should return :gid as the field for #{name}" do @posix.idfield(name).should == :gid end it "should return :getgrgid as the id method for #{name}" do @posix.methodbyid(name).should == :getgrgid end it "should return :getgrnam as the name method for #{name}" do @posix.methodbyname(name).should == :getgrnam end end [:user, :pw, :passwd].each do |name| it "should return :uid as the field for #{name}" do @posix.idfield(name).should == :uid end it "should return :getpwuid as the id method for #{name}" do @posix.methodbyid(name).should == :getpwuid end it "should return :getpwnam as the name method for #{name}" do @posix.methodbyname(name).should == :getpwnam end end describe "when retrieving a posix field" do before do @thing = stub 'thing', :field => "asdf" end it "should fail if no id was passed" do lambda { @posix.get_posix_field("asdf", "bar", nil) }.should raise_error(Puppet::DevError) end describe "and the id is an integer" do it "should log an error and return nil if the specified id is greater than the maximum allowed ID" do Puppet.settings.expects(:value).with(:maximum_uid).returns 100 Puppet.expects(:err) @posix.get_posix_field("asdf", "bar", 200).should be_nil end it "should use the method return by :methodbyid and return the specified field" do Etc.expects(:getgrgid).returns @thing @thing.expects(:field).returns "myval" @posix.get_posix_field(:gr, :field, 200).should == "myval" end it "should return nil if the method throws an exception" do Etc.expects(:getgrgid).raises ArgumentError @thing.expects(:field).never @posix.get_posix_field(:gr, :field, 200).should be_nil end end describe "and the id is not an integer" do it "should use the method return by :methodbyid and return the specified field" do Etc.expects(:getgrnam).returns @thing @thing.expects(:field).returns "myval" @posix.get_posix_field(:gr, :field, "asdf").should == "myval" end it "should return nil if the method throws an exception" do Etc.expects(:getgrnam).raises ArgumentError @thing.expects(:field).never @posix.get_posix_field(:gr, :field, "asdf").should be_nil end end end describe "when returning the gid" do before do @posix.stubs(:get_posix_field) end describe "and the group is an integer" do it "should convert integers specified as a string into an integer" do @posix.expects(:get_posix_field).with(:group, :name, 100) @posix.gid("100") end it "should look up the name for the group" do @posix.expects(:get_posix_field).with(:group, :name, 100) @posix.gid(100) end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.gid(100).should be_nil end it "should use the found name to look up the id" do @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 @posix.gid(100).should == 100 end # LAK: This is because some platforms have a broken Etc module that always return # the same group. it "should use :search_posix_field if the discovered id does not match the passed-in id" do @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 50 @posix.expects(:search_posix_field).with(:group, :gid, 100).returns "asdf" @posix.gid(100).should == "asdf" end end describe "and the group is a string" do it "should look up the gid for the group" do @posix.expects(:get_posix_field).with(:group, :gid, "asdf") @posix.gid("asdf") end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.gid("asdf").should be_nil end it "should use the found gid to look up the nam" do @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" @posix.gid("asdf").should == 100 end it "should use :search_posix_field if the discovered name does not match the passed-in name" do @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:group, :name, 100).returns "boo" @posix.expects(:search_posix_field).with(:group, :gid, "asdf").returns "asdf" @posix.gid("asdf").should == "asdf" end end end describe "when returning the uid" do before do @posix.stubs(:get_posix_field) end describe "and the group is an integer" do it "should convert integers specified as a string into an integer" do @posix.expects(:get_posix_field).with(:passwd, :name, 100) @posix.uid("100") end it "should look up the name for the group" do @posix.expects(:get_posix_field).with(:passwd, :name, 100) @posix.uid(100) end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.uid(100).should be_nil end it "should use the found name to look up the id" do @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 @posix.uid(100).should == 100 end # LAK: This is because some platforms have a broken Etc module that always return # the same group. it "should use :search_posix_field if the discovered id does not match the passed-in id" do @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 50 @posix.expects(:search_posix_field).with(:passwd, :uid, 100).returns "asdf" @posix.uid(100).should == "asdf" end end describe "and the group is a string" do it "should look up the uid for the group" do @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf") @posix.uid("asdf") end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.uid("asdf").should be_nil end it "should use the found uid to look up the nam" do @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" @posix.uid("asdf").should == 100 end it "should use :search_posix_field if the discovered name does not match the passed-in name" do @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "boo" @posix.expects(:search_posix_field).with(:passwd, :uid, "asdf").returns "asdf" @posix.uid("asdf").should == "asdf" end end end it "should be able to iteratively search for posix values" do @posix.should respond_to(:search_posix_field) end describe "when searching for posix values iteratively" do it "should iterate across all of the structs returned by Etc and return the appropriate field from the first matching value" end end diff --git a/spec/unit/util/pson_spec.rb b/spec/unit/util/pson_spec.rb index 08758ee38..63d085a66 100755 --- a/spec/unit/util/pson_spec.rb +++ b/spec/unit/util/pson_spec.rb @@ -1,53 +1,52 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/pson' class PsonUtil include Puppet::Util::Pson end describe Puppet::Util::Pson do it "should fail if no data is provided" do lambda { PsonUtil.new.pson_create("type" => "foo") }.should raise_error(ArgumentError) end it "should call 'from_pson' with the provided data" do pson = PsonUtil.new pson.expects(:from_pson).with("mydata") pson.pson_create("type" => "foo", "data" => "mydata") end { 'foo' => '"foo"', 1 => '1', "\x80" => "\"\x80\"", [] => '[]' }.each { |str,pson| it "should be able to encode #{str.inspect}" do str.to_pson.should == pson end } it "should be able to handle arbitrary binary data" do bin_string = (1..20000).collect { |i| ((17*i+13*i*i) % 255).chr }.join PSON.parse(%Q{{ "type": "foo", "data": #{bin_string.to_pson} }})["data"].should == bin_string end it "should be able to handle UTF8 that isn't a real unicode character" do s = ["\355\274\267"] PSON.parse( [s].to_pson ).should == [s] end it "should be able to handle UTF8 for \\xFF" do s = ["\xc3\xbf"] PSON.parse( [s].to_pson ).should == [s] end it "should be able to handle invalid UTF8 bytes" do s = ["\xc3\xc3"] PSON.parse( [s].to_pson ).should == [s] end end diff --git a/spec/unit/util/queue/stomp_spec.rb b/spec/unit/util/queue/stomp_spec.rb index a11d5324e..f67189cf5 100755 --- a/spec/unit/util/queue/stomp_spec.rb +++ b/spec/unit/util/queue/stomp_spec.rb @@ -1,136 +1,135 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/queue' describe Puppet::Util::Queue, :if => Puppet.features.stomp? do it 'should load :stomp client appropriately' do Puppet.settings.stubs(:value).returns 'faux_queue_source' Puppet::Util::Queue.queue_type_to_class(:stomp).name.should == 'Puppet::Util::Queue::Stomp' end end describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp? do before do # So we make sure we never create a real client instance. # Otherwise we'll try to connect, and that's bad. Stomp::Client.stubs(:new).returns stub("client") end it 'should be registered with Puppet::Util::Queue as :stomp type' do Puppet::Util::Queue.queue_type_to_class(:stomp).should == Puppet::Util::Queue::Stomp end describe "when initializing" do it "should create a Stomp client instance" do Stomp::Client.expects(:new).returns stub("stomp_client") Puppet::Util::Queue::Stomp.new end it "should provide helpful failures when the queue source is not a valid source" do # Stub rather than expect, so we can include the source in the error Puppet.settings.stubs(:value).with(:queue_source).returns "-----" lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) end it "should fail unless the queue source is a stomp URL" do # Stub rather than expect, so we can include the source in the error Puppet.settings.stubs(:value).with(:queue_source).returns "http://foo/bar" lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) end it "should fail somewhat helpfully if the Stomp client cannot be created" do Stomp::Client.expects(:new).raises RuntimeError lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) end list = %w{user password host port} {"user" => "myuser", "password" => "mypass", "host" => "foohost", "port" => 42}.each do |name, value| it "should use the #{name} from the queue source as the queueing #{name}" do Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser:mypass@foohost:42/" Stomp::Client.expects(:new).with { |*args| args[list.index(name)] == value } Puppet::Util::Queue::Stomp.new end end it "should create a reliable client instance" do Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser@foohost:42/" Stomp::Client.expects(:new).with { |*args| args[4] == true } Puppet::Util::Queue::Stomp.new end end describe "when sending a message" do before do @client = stub 'client' Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end it "should send it to the queue client instance" do @client.expects(:send).with { |queue, msg, options| msg == "Smite!" } @queue.send_message('fooqueue', 'Smite!') end it "should send it to the transformed queue name" do @client.expects(:send).with { |queue, msg, options| queue == "/queue/fooqueue" } @queue.send_message('fooqueue', 'Smite!') end it "should send it as a persistent message" do @client.expects(:send).with { |queue, msg, options| options[:persistent] == true } @queue.send_message('fooqueue', 'Smite!') end end describe "when subscribing to a queue" do before do @client = stub 'client', :acknowledge => true Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end it "should subscribe via the queue client instance" do @client.expects(:subscribe) @queue.subscribe('fooqueue') end it "should subscribe to the transformed queue name" do @client.expects(:subscribe).with { |queue, options| queue == "/queue/fooqueue" } @queue.subscribe('fooqueue') end it "should specify that its messages should be acknowledged" do @client.expects(:subscribe).with { |queue, options| options[:ack] == :client } @queue.subscribe('fooqueue') end it "should yield the body of any received message" do message = mock 'message' message.expects(:body).returns "mybody" @client.expects(:subscribe).yields(message) body = nil @queue.subscribe('fooqueue') { |b| body = b } body.should == "mybody" end it "should acknowledge all successfully processed messages" do message = stub 'message', :body => "mybode" @client.stubs(:subscribe).yields(message) @client.expects(:acknowledge).with(message) @queue.subscribe('fooqueue') { |b| "eh" } end end it 'should transform the simple queue name to "/queue/"' do Puppet::Util::Queue::Stomp.new.stompify_target('blah').should == '/queue/blah' end end diff --git a/spec/unit/util/queue_spec.rb b/spec/unit/util/queue_spec.rb index d356fd7bb..59ea57d9b 100755 --- a/spec/unit/util/queue_spec.rb +++ b/spec/unit/util/queue_spec.rb @@ -1,96 +1,95 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/queue' require 'spec/mocks' def make_test_client_class(n) c = Class.new do class < make_test_client_class('Bogus::Default'), :setup => make_test_client_class('Bogus::Setup') } describe Puppet::Util::Queue do before :all do mod.register_queue_type(client_classes[:default], :default) mod.register_queue_type(client_classes[:setup], :setup) end before :each do @class = Class.new do extend mod end end after :all do instances = mod.instance_hash(:queue_clients) [:default, :setup, :bogus, :aardvark, :conflict, :test_a, :test_b].each{ |x| instances.delete(x) } end context 'when determining a type name from a class' do it 'should handle a simple one-word class name' do mod.queue_type_from_class(make_test_client_class('Foo')).should == :foo end it 'should handle a simple two-word class name' do mod.queue_type_from_class(make_test_client_class('FooBar')).should == :foo_bar end it 'should handle a two-part class name with one terminating word' do mod.queue_type_from_class(make_test_client_class('Foo::Bar')).should == :bar end it 'should handle a two-part class name with two terminating words' do mod.queue_type_from_class(make_test_client_class('Foo::BarBah')).should == :bar_bah end end context 'when registering a queue client class' do c = make_test_client_class('Foo::Bogus') it 'uses the proper default name logic when type is unspecified' do mod.register_queue_type(c) mod.queue_type_to_class(:bogus).should == c end it 'uses an explicit type name when provided' do mod.register_queue_type(c, :aardvark) mod.queue_type_to_class(:aardvark).should == c end it 'throws an exception when type names conflict' do mod.register_queue_type( make_test_client_class('Conflict') ) lambda { mod.register_queue_type( c, :conflict) }.should raise_error end it 'handle multiple, non-conflicting registrations' do a = make_test_client_class('TestA') b = make_test_client_class('TestB') mod.register_queue_type(a) mod.register_queue_type(b) mod.queue_type_to_class(:test_a).should == a mod.queue_type_to_class(:test_b).should == b end it 'throws an exception when type name is unknown' do lambda { mod.queue_type_to_class(:nope) }.should raise_error end end context 'when determining client type' do it 'returns client class based on the :queue_type setting' do Puppet.settings.expects(:value).with(:queue_type).returns(:myqueue) Puppet::Util::Queue.expects(:queue_type_to_class).with(:myqueue).returns "eh" @class.client_class.should == "eh" end end end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index f118dc99b..c7f99051f 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -1,563 +1,562 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' describe RDoc::Parser do before :each do File.stubs(:stat).with("init.pp") @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) end describe "when scanning files" do it "should parse puppet files with the puppet parser" do @parser.stubs(:scan_top_level) parser = stub 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once parser.expects(:file=).with("module/manifests/init.pp") parser.expects(:file=).with("/dev/null/manifests/site.pp") @parser.scan end it "should scan the ast for Puppet files" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan end it "should return a PuppetTopLevel to RDoc" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan.should be_a(RDoc::PuppetTopLevel) end it "should scan the top level even if the file has already parsed" do known_type = stub 'known_types' env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(env) env.stubs(:known_resource_types).returns(known_type) known_type.expects(:watching_file?).with("module/manifests/init.pp").returns(true) @parser.expects(:scan_top_level) @parser.scan end end describe "when scanning top level entities" do before :each do @resource_type_collection = resource_type_collection = stub_everything('resource_type_collection') @parser.instance_eval { @known_resource_types = resource_type_collection } @parser.stubs(:split_module).returns("module") @topcontainer = stub_everything 'topcontainer' @container = stub_everything 'container' @module = stub_everything 'module' @container.stubs(:add_module).returns(@module) @parser.stubs(:get_class_or_module).returns([@container, "module"]) end it "should read any present README as module documentation" do FileTest.stubs(:readable?).returns(true) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:comment=).with("readme") @parser.scan_top_level(@topcontainer) end it "should tell the container its module name" do @parser.stubs(:parse_elements) @topcontainer.expects(:module_name=).with("module") @parser.scan_top_level(@topcontainer) end it "should not document our toplevel if it isn't a valid module" do @parser.stubs(:split_module).returns(nil) @topcontainer.expects(:document_self=).with(false) @parser.expects(:parse_elements).never @parser.scan_top_level(@topcontainer) end it "should set the module as global if we parse the global manifests (ie __site__ module)" do @parser.stubs(:split_module).returns(RDoc::Parser::SITE) @parser.stubs(:parse_elements) @topcontainer.expects(:global=).with(true) @parser.scan_top_level(@topcontainer) end it "should attach this module container to the toplevel container" do @parser.stubs(:parse_elements) @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) @parser.scan_top_level(@topcontainer) end it "should defer ast parsing to parse_elements for this module" do @parser.expects(:parse_elements).with(@module) @parser.scan_top_level(@topcontainer) end it "should defer plugins parsing to parse_plugins for this module" do @parser.input_file_name = "module/lib/puppet/parser/function.rb" @parser.expects(:parse_plugins).with(@module) @parser.scan_top_level(@topcontainer) end end describe "when finding modules from filepath" do before :each do Puppet::Module.stubs(:modulepath).returns("/path/to/modules") end it "should return the module name for modulized puppet manifests" do File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" end it "should return for manifests not under module path" do File.stubs(:expand_path).returns("/path/to/manifests/init.pp") File.stubs(:identical?).returns(false) @parser.split_module("/path/to/manifests/init.pp").should == RDoc::Parser::SITE end end describe "when parsing AST elements" do before :each do @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef" @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode" @resource_type_collection = resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser.instance_eval { @known_resource_types = resource_type_collection } @container = stub_everything 'container' end it "should document classes in the parsed file" do @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container) @parser.parse_elements(@container) end it "should not document class parsed in an other file" do @klass.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container).never @parser.parse_elements(@container) end it "should document vardefs for the main class" do @klass.stubs(:name).returns :main @resource_type_collection.add_hostclass(@klass) code = stub 'code', :is_a? => false @klass.stubs(:name).returns("") @klass.stubs(:code).returns(code) @parser.expects(:scan_for_vardef).with(@container, code) @parser.parse_elements(@container) end it "should document definitions in the parsed file" do @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container) @parser.parse_elements(@container) end it "should not document definitions parsed in an other file" do @definition.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container).never @parser.parse_elements(@container) end it "should document nodes in the parsed file" do @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container) @parser.parse_elements(@container) end it "should not document node parsed in an other file" do @node.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container).never @parser.parse_elements(@container) end end describe "when documenting definition" do before(:each) do @define = stub_everything 'define', :arguments => [], :doc => "mydoc", :file => "file", :line => 42 @class = stub_everything 'class' @parser.stubs(:get_class_or_module).returns([@class, "mydef"]) end it "should register a RDoc method to the current container" do @class.expects(:add_method).with { |m| m.name == "mydef"} @parser.document_define("mydef", @define, @class) end it "should attach the documentation to this method" do @class.expects(:add_method).with { |m| m.comment = "mydoc" } @parser.document_define("mydef", @define, @class) end it "should produce a better error message on unhandled exception" do @class.expects(:add_method).raises(ArgumentError) lambda { @parser.document_define("mydef", @define, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end it "should convert all definition parameter to string" do arg = stub 'arg' val = stub 'val' @define.stubs(:arguments).returns({arg => val}) arg.expects(:to_s).returns("arg") val.expects(:to_s).returns("val") @parser.document_define("mydef", @define, @class) end end describe "when documenting nodes" do before :each do @code = stub_everything 'code' @node = stub_everything 'node', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_node = stub_everything 'rdocnode' @class = stub_everything 'class' @class.stubs(:add_node).returns(@rdoc_node) end it "should add a node to the current container" do @class.expects(:add_node).with("mynode", "parent").returns(@rdoc_node) @parser.document_node("mynode", @node, @class) end it "should associate the node documentation to the rdoc node" do @rdoc_node.expects(:comment=).with("mydoc") @parser.document_node("mynode", @node, @class) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for variable definition" do @parser.expects(:scan_for_vardef).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should produce a better error message on unhandled exception" do @class.stubs(:add_node).raises(ArgumentError) lambda { @parser.document_node("mynode", @node, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when documenting classes" do before :each do @code = stub_everything 'code' @class = stub_everything 'class', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_class = stub_everything 'rdoc-class' @module = stub_everything 'class' @module.stubs(:add_class).returns(@rdoc_class) @parser.stubs(:get_class_or_module).returns([@module, "myclass"]) end it "should add a class to the current container" do @module.expects(:add_class).with(RDoc::PuppetClass, "myclass", "parent").returns(@rdoc_class) @parser.document_class("mynode", @class, @module) end it "should set the superclass" do @rdoc_class.expects(:superclass=).with("parent") @parser.document_class("mynode", @class, @module) end it "should associate the node documentation to the rdoc class" do @rdoc_class.expects(:comment=).with("mydoc") @parser.document_class("mynode", @class, @module) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should produce a better error message on unhandled exception" do @module.stubs(:add_class).raises(ArgumentError) lambda { @parser.document_class("mynode", @class, @module) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when scanning for includes and requires" do def create_stmt(name) stmt_value = stub "#{name}_value", :to_s => "myclass" Puppet::Parser::AST::Function.new( :name => name, :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, create_stmt("include")) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt("include") ]) @class.expects(:add_include)#.with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end it "should register requires to the current container" do @code.stubs(:children).returns([ create_stmt("require") ]) @class.expects(:add_require).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end end describe "when scanning for realized virtual resources" do def create_stmt stmt_value = stub "resource_ref", :to_s => "File[\"/tmp/a\"]" Puppet::Parser::AST::Function.new( :name => 'realize', :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class,create_stmt) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt ]) @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class, [@code]) end end describe "when scanning for variable definition" do before :each do @class = stub_everything 'class' @stmt = stub_everything 'stmt', :name => "myvar", :value => "myvalue", :doc => "mydoc" @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) @stmt.stubs(:is_a?).with(Puppet::Parser::AST::VarDef).returns(true) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should recursively register variables to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, @stmt) end end describe "when scanning for resources" do before :each do @class = stub_everything 'class' @stmt = Puppet::Parser::AST::Resource.new( :type => "File", :instances => Puppet::Parser::AST::ASTArray.new(:children => [ Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::Name.new(:value => "myfile"), :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) ) ]), :doc => 'mydoc' ) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should register a PuppetResource to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, @stmt) end end describe "when parsing plugins" do before :each do @container = stub 'container' end it "should delegate parsing custom facts to parse_facts" do @parser = RDoc::Parser.new(@top_level, "module/manifests/lib/puppet/facter/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_fact).with(@container) @parser.parse_plugins(@container) end it "should delegate parsing plugins to parse_plugins" do @parser = RDoc::Parser.new(@top_level, "module/manifests/lib/puppet/functions/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_puppet_plugin).with(@container) @parser.parse_plugins(@container) end end describe "when parsing plugins" do before :each do @container = stub_everything 'container' end it "should add custom functions to the container" do File.stubs(:open).yields("# documentation module Puppet::Parser::Functions newfunction(:myfunc, :type => :rvalue) do |args| File.dirname(args[0]) end end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "myfunc" end @parser.parse_puppet_plugin(@container) end it "should add custom types to the container" do File.stubs(:open).yields("# documentation Puppet::Type.newtype(:mytype) do end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "mytype" end @parser.parse_puppet_plugin(@container) end end describe "when parsing facts" do before :each do @container = stub_everything 'container' File.stubs(:open).yields(["# documentation", "Facter.add('myfact') do", "confine :kernel => :linux", "end"]) end it "should add facts to the container" do @container.expects(:add_fact).with do |fact| fact.comment == "documentation\n" and fact.name == "myfact" end @parser.parse_fact(@container) end it "should add confine to the parsed facts" do ourfact = nil @container.expects(:add_fact).with do |fact| ourfact = fact true end @parser.parse_fact(@container) ourfact.confine.should == { :type => "kernel", :value => ":linux" } end end end diff --git a/spec/unit/util/rdoc_spec.rb b/spec/unit/util/rdoc_spec.rb index 3b5248528..067b5b8a7 100755 --- a/spec/unit/util/rdoc_spec.rb +++ b/spec/unit/util/rdoc_spec.rb @@ -1,148 +1,147 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/rdoc' require 'rdoc/rdoc' describe Puppet::Util::RDoc do describe "when generating RDoc HTML documentation" do before :each do @rdoc = stub_everything 'rdoc' RDoc::RDoc.stubs(:new).returns(@rdoc) end it "should tell the parser to ignore import" do Puppet.expects(:[]=).with(:ignoreimport, true) Puppet::Util::RDoc.rdoc("output", []) end it "should install the Puppet HTML Generator into RDoc generators" do Puppet::Util::RDoc.rdoc("output", []) RDoc::RDoc::GENERATORS["puppet"].file_name.should == "puppet/util/rdoc/generators/puppet_generator.rb" end it "should tell RDoc to generate documentation using the Puppet generator" do @rdoc.expects(:document).with { |args| args.include?("--fmt") and args.include?("puppet") } Puppet::Util::RDoc.rdoc("output", []) end it "should tell RDoc to be quiet" do @rdoc.expects(:document).with { |args| args.include?("--quiet") } Puppet::Util::RDoc.rdoc("output", []) end it "should pass charset to RDoc" do @rdoc.expects(:document).with { |args| args.include?("--charset") and args.include?("utf-8") } Puppet::Util::RDoc.rdoc("output", [], "utf-8") end it "should tell RDoc to force updates of indices when RDoc supports it" do Options::OptionList.stubs(:options).returns([["--force-update", "-U", 0 ]]) @rdoc.expects(:document).with { |args| args.include?("--force-update") } Puppet::Util::RDoc.rdoc("output", []) end it "should not tell RDoc to force updates of indices when RDoc doesn't support it" do Options::OptionList.stubs(:options).returns([]) @rdoc.expects(:document).never.with { |args| args.include?("--force-update") } Puppet::Util::RDoc.rdoc("output", []) end it "should tell RDoc to use the given outputdir" do @rdoc.expects(:document).with { |args| args.include?("--op") and args.include?("myoutputdir") } Puppet::Util::RDoc.rdoc("myoutputdir", []) end it "should tell RDoc to exclude .pp files under any modules//files section" do @rdoc.expects(:document).with { |args| args.include?("--exclude") and args.include?("/modules/[^/]*/files/.*\.pp$") } Puppet::Util::RDoc.rdoc("myoutputdir", []) end it "should give all the source directories to RDoc" do @rdoc.expects(:document).with { |args| args.include?("sourcedir") } Puppet::Util::RDoc.rdoc("output", ["sourcedir"]) end end describe "when running a manifest documentation" do it "should tell the parser to ignore import" do Puppet.expects(:[]=).with(:ignoreimport, true) Puppet::Util::RDoc.manifestdoc([]) end it "should use a parser with the correct environment" do FileTest.stubs(:file?).returns(true) Puppet::Util::RDoc.stubs(:output) parser = stub_everything Puppet::Parser::Parser.stubs(:new).with{ |env| env.is_a?(Puppet::Node::Environment) }.returns(parser) parser.expects(:file=).with("file") parser.expects(:parse) Puppet::Util::RDoc.manifestdoc(["file"]) end it "should puppet parse all given files" do FileTest.stubs(:file?).returns(true) Puppet::Util::RDoc.stubs(:output) parser = stub_everything Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:file=).with("file") parser.expects(:parse) Puppet::Util::RDoc.manifestdoc(["file"]) end it "should call output for each parsed file" do FileTest.stubs(:file?).returns(true) ast = stub_everything parser = stub_everything Puppet::Parser::Parser.stubs(:new).returns(parser) parser.stubs(:parse).returns(ast) Puppet::Util::RDoc.expects(:output).with("file", ast) Puppet::Util::RDoc.manifestdoc(["file"]) end describe "when outputing documentation" do it "should output doc for ast classes, nodes and definitions in order of increasing line number" do byline = sequence('documentation outputs in line order') Puppet::Util::RDoc.expects(:puts).with("im a class\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a node\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a define\n").in_sequence(byline) # any other output must fail Puppet::Util::RDoc.manifestdoc([my_fixture('basic.pp')]) end it "should output resource documentation if needed" do pending "#6634 being fixed" Puppet.settings[:document_all] = true byline = sequence('documentation outputs in line order') Puppet::Util::RDoc.expects(:puts).with("im a class\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a node\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a define\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a resource\n").in_sequence(byline) # any other output must fail Puppet::Util::RDoc.manifestdoc([my_fixture('basic.pp')]) end end end end diff --git a/spec/unit/util/reference_serializer_spec.rb b/spec/unit/util/reference_serializer_spec.rb old mode 100644 new mode 100755 index f72715ed6..de53ab9bb --- a/spec/unit/util/reference_serializer_spec.rb +++ b/spec/unit/util/reference_serializer_spec.rb @@ -1,52 +1,51 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/rails/reference_serializer' class SerializeTester include Puppet::Util::ReferenceSerializer end describe Puppet::Util::ReferenceSerializer do before do @tester = SerializeTester.new end describe "when serializing" do it "should yaml-dump resource references" do ref = Puppet::Resource.new("file", "/foo") @tester.serialize_value(ref).should =~ /^---/ end it "should convert the boolean 'true' into the string 'true'" do @tester.serialize_value(true).should == "true" end it "should convert the boolean 'false' into the string 'false'" do @tester.serialize_value(false).should == "false" end it "should return all other values" do @tester.serialize_value("foo").should == "foo" end end describe "when unserializing" do it "should yaml-load values that look like yaml" do yaml = YAML.dump(%w{a b c}) @tester.unserialize_value(yaml).should == %w{a b c} end it "should convert the string 'true' into the boolean 'true'" do @tester.unserialize_value("true").should == true end it "should convert the string 'false' into the boolean 'false'" do @tester.unserialize_value("false").should == false end it "should return all other values" do @tester.unserialize_value("foo").should == "foo" end end end diff --git a/spec/unit/util/resource_template_spec.rb b/spec/unit/util/resource_template_spec.rb index e823a9ff4..4f7cafdb7 100755 --- a/spec/unit/util/resource_template_spec.rb +++ b/spec/unit/util/resource_template_spec.rb @@ -1,58 +1,57 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/resource_template' describe Puppet::Util::ResourceTemplate do describe "when initializing" do it "should fail if the template does not exist" do FileTest.expects(:exist?).with("/my/template").returns false lambda { Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) }.should raise_error(ArgumentError) end it "should not create the ERB template" do ERB.expects(:new).never FileTest.expects(:exist?).with("/my/template").returns true Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) end end describe "when evaluating" do before do FileTest.stubs(:exist?).returns true File.stubs(:read).returns "eh" @template = stub 'template', :result => nil ERB.stubs(:new).returns @template @resource = mock 'resource' @wrapper = Puppet::Util::ResourceTemplate.new("/my/template", @resource) end it "should set all of the resource's parameters as instance variables" do @resource.expects(:to_hash).returns(:one => "uno", :two => "dos") @template.expects(:result).with do |bind| eval("@one", bind) == "uno" and eval("@two", bind) == "dos" end @wrapper.evaluate end it "should create a template instance with the contents of the file" do File.expects(:read).with("/my/template").returns "yay" ERB.expects(:new).with("yay", 0, "-").returns(@template) @wrapper.stubs :set_resource_variables @wrapper.evaluate end it "should return the result of the template" do @wrapper.stubs :set_resource_variables @wrapper.expects(:binding).returns "mybinding" @template.expects(:result).with("mybinding").returns "myresult" @wrapper.evaluate.should == "myresult" end end end diff --git a/spec/unit/util/run_mode_spec.rb b/spec/unit/util/run_mode_spec.rb old mode 100644 new mode 100755 index 1956973ea..c8d2b31f6 --- a/spec/unit/util/run_mode_spec.rb +++ b/spec/unit/util/run_mode_spec.rb @@ -1,51 +1,50 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Util::RunMode do before do @run_mode = Puppet::Util::RunMode.new('fake') end it "should have confdir /etc/puppet when run as root" do Puppet.features.stubs(:root?).returns(true) @run_mode.conf_dir.should == '/etc/puppet' end it "should have confdir ~/.puppet when run as non-root" do Puppet.features.stubs(:root?).returns(false) @run_mode.expects(:expand_path).with("~/.puppet").returns("~/.puppet") @run_mode.conf_dir.should == "~/.puppet" end it "should have vardir /var/lib/puppet when run as root" do Puppet.features.stubs(:root?).returns(true) @run_mode.var_dir.should == '/var/lib/puppet' end it "should have vardir ~/.puppet/var when run as non-root" do Puppet.features.stubs(:root?).returns(false) @run_mode.expects(:expand_path).with("~/.puppet/var").returns("~/.puppet/var") @run_mode.var_dir.should == "~/.puppet/var" end it "should have rundir depend on vardir" do @run_mode.run_dir.should == '$vardir/run' end it "should have logopts return an array with $vardir/log if runmode is not master" do @run_mode.expects(:master?).returns false @run_mode.logopts.should == ["$vardir/log", "The Puppet log directory."] end it "should have logopts return a hash with $vardir/log and other metadata if runmode is master" do @run_mode.expects(:master?).returns true @run_mode.logopts.should == { :default => "$vardir/log", :mode => 0750, :owner => "service", :group => "service", :desc => "The Puppet log directory.", } end end diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb index c18be0c2f..0eaf43cbb 100755 --- a/spec/unit/util/selinux_spec.rb +++ b/spec/unit/util/selinux_spec.rb @@ -1,278 +1,277 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/selinux' include Puppet::Util::SELinux unless defined?(Selinux) module Selinux def self.is_selinux_enabled false end end end describe Puppet::Util::SELinux do describe "selinux_support?" do before do end it "should return :true if this system has SELinux enabled" do Selinux.expects(:is_selinux_enabled).returns 1 selinux_support?.should be_true end it "should return :false if this system lacks SELinux" do Selinux.expects(:is_selinux_enabled).returns 0 selinux_support?.should be_false end it "should return nil if /proc/mounts does not exist" do File.stubs(:open).with("/proc/mounts").raises("No such file or directory - /proc/mounts") read_mounts.should == nil end end describe "filesystem detection" do before :each do fh = stub 'fh', :close => nil File.stubs(:open).with("/proc/mounts").returns fh fh.expects(:read_nonblock).times(2).returns("rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n/sys /sys sysfs rw,relatime 0 0\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\n").then.raises EOFError end it "should parse the contents of /proc/mounts" do read_mounts.should == { '/' => 'ext3', '/sys' => 'sysfs', '/mnt/nfs' => 'nfs', '/proc' => 'proc', '/dev' => 'tmpfs' } end it "should match a path on / to ext3" do find_fs('/etc/puppet/testfile').should == "ext3" end it "should match a path on /mnt/nfs to nfs" do find_fs('/mnt/nfs/testfile/foobar').should == "nfs" end it "should reture true for a capable filesystem" do selinux_label_support?('/etc/puppet/testfile').should be_true end it "should return false for a noncapable filesystem" do selinux_label_support?('/mnt/nfs/testfile').should be_false end it "should follow symlinks when determining file systems" do self.stubs(:realpath).with('/mnt/symlink/testfile').returns('/mnt/nfs/dest/testfile') selinux_label_support?('/mnt/symlink/testfile').should be_false end end describe "realpath" do it "should handle files that don't exist" do # Since I'm stubbing Pathname.new for this test, # I need to also stub the internal calls to Pathname.new, # which happen in Pathname.dirname and Parthname.basename # I want those to return real Pathname objects, # so I'm creating them before the stub is in place. realpaths = Hash.new {|hash, path| hash[path] = Pathname.new(path) } paths = ['symlink', '/mnt'] paths.each { |path| realpaths[path] } realpaths['/mnt/symlink'] = stubs "Pathname" realpaths['/mnt/symlink'].stubs(:realpath).returns(realpaths['/mnt/nfs/dest']) realpaths['/mnt/symlink'].stubs(:exist?).returns(true) realpaths['/mnt/symlink/nonexistant'] = stubs "Pathname" realpaths['/mnt/symlink/nonexistant'].stubs(:realpath).raises(Errno::ENOENT) realpaths['/mnt/symlink/nonexistant'].stubs(:exist?).returns(false) realpaths['/mnt/symlink/nonexistant'].stubs(:dirname).returns(realpaths['/mnt/symlink']) realpaths['/mnt/symlink/nonexistant'].stubs(:basename).returns(realpaths['nonexistant']) realpaths.each do |path, value| Pathname.stubs(:new).with(path).returns(value) end realpath('/mnt/symlink/nonexistant').should == '/mnt/nfs/dest/nonexistant' end end describe "get_selinux_current_context" do it "should return nil if no SELinux support" do self.expects(:selinux_support?).returns false get_selinux_current_context("/foo").should be_nil end it "should return a context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0" end it "should return nil if lgetfilecon fails" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns -1 get_selinux_current_context("/foo").should be_nil end end describe "get_selinux_default_context" do it "should return nil if no SELinux support" do self.expects(:selinux_support?).returns false get_selinux_default_context("/foo").should be_nil end it "should return a context if a default context exists" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 File.expects(:lstat).with("/foo").returns fstat self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"] get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" end it "should return nil if matchpathcon returns failure" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 File.expects(:lstat).with("/foo").returns fstat self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns -1 get_selinux_default_context("/foo").should be_nil end it "should return nil if selinux_label_support returns false" do self.expects(:selinux_support?).returns true self.expects(:find_fs).with("/foo").returns "nfs" get_selinux_default_context("/foo").should be_nil end end describe "parse_selinux_context" do it "should return nil if no context is passed" do parse_selinux_context(:seluser, nil).should be_nil end it "should return nil if the context is 'unlabeled'" do parse_selinux_context(:seluser, "unlabeled").should be_nil end it "should return the user type when called with :seluser" do parse_selinux_context(:seluser, "user_u:role_r:type_t:s0").should == "user_u" end it "should return the role type when called with :selrole" do parse_selinux_context(:selrole, "user_u:role_r:type_t:s0").should == "role_r" end it "should return the type type when called with :seltype" do parse_selinux_context(:seltype, "user_u:role_r:type_t:s0").should == "type_t" end it "should return nil for :selrange when no range is returned" do parse_selinux_context(:selrange, "user_u:role_r:type_t").should be_nil end it "should return the range type when called with :selrange" do parse_selinux_context(:selrange, "user_u:role_r:type_t:s0").should == "s0" end describe "with a variety of SELinux range formats" do ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range| it "should parse range '#{range}'" do parse_selinux_context(:selrange, "user_u:role_r:type_t:#{range}").should == range end end end end describe "set_selinux_context" do before :each do fh = stub 'fh', :close => nil File.stubs(:open).with("/proc/mounts").returns fh fh.stubs(:read_nonblock).returns( "rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n"+ "/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n"+ "/sys /sys sysfs rw,relatime 0 0\n" ).then.raises EOFError end it "should return nil if there is no SELinux support" do self.expects(:selinux_support?).returns false set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil end it "should return nil if selinux_label_support returns false" do self.expects(:selinux_support?).returns true self.expects(:selinux_label_support?).with("/foo").returns false set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil end it "should use lsetfilecon to set a context" do self.expects(:selinux_support?).returns true Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true end it "should use lsetfilecon to set user_u user context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "foo:role_r:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "user_u", :seluser).should be_true end it "should use lsetfilecon to set role_r role context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:foo:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "role_r", :selrole).should be_true end it "should use lsetfilecon to set type_t type context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:foo:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "type_t", :seltype).should be_true end it "should use lsetfilecon to set s0:c3,c5 range context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0:c3,c5").returns 0 set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true end end describe "set_selinux_default_context" do it "should return nil if there is no SELinux support" do self.expects(:selinux_support?).returns false set_selinux_default_context("/foo").should be_nil end it "should return nil if no default context exists" do self.expects(:get_selinux_default_context).with("/foo").returns nil set_selinux_default_context("/foo").should be_nil end it "should do nothing and return nil if the current context matches the default context" do self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" self.expects(:get_selinux_current_context).with("/foo").returns "user_u:role_r:type_t" set_selinux_default_context("/foo").should be_nil end it "should set and return the default context if current and default do not match" do self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" self.expects(:get_selinux_current_context).with("/foo").returns "olduser_u:role_r:type_t" self.expects(:set_selinux_context).with("/foo", "user_u:role_r:type_t").returns true set_selinux_default_context("/foo").should == "user_u:role_r:type_t" end end end diff --git a/spec/unit/util/settings/file_setting_spec.rb b/spec/unit/util/settings/file_setting_spec.rb index a2049e2ad..734b41f3a 100755 --- a/spec/unit/util/settings/file_setting_spec.rb +++ b/spec/unit/util/settings/file_setting_spec.rb @@ -1,254 +1,253 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/settings' require 'puppet/util/settings/file_setting' describe Puppet::Util::Settings::FileSetting do FileSetting = Puppet::Util::Settings::FileSetting before do @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" end describe "when determining whether the service user should be used" do before do @settings = mock 'settings' @settings.stubs(:[]).with(:mkusers).returns false @settings.stubs(:service_user_available?).returns true end it "should be true if the service user is available" do @settings.expects(:service_user_available?).returns true setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end it "should be true if 'mkusers' is set" do @settings.expects(:[]).with(:mkusers).returns true setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end it "should be false if the service user is not available and 'mkusers' is unset" do setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end end describe "when setting the owner" do it "should allow the file to be owned by root" do root_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") } root_owner.should_not raise_error end it "should allow the file to be owned by the service user" do service_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "service", :desc => "a setting") } service_owner.should_not raise_error end it "should allow the ownership of the file to be unspecified" do no_owner = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } no_owner.should_not raise_error end it "should not allow other owners" do invalid_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "invalid", :desc => "a setting") } invalid_owner.should raise_error(FileSetting::SettingError) end end describe "when reading the owner" do it "should be root when the setting specifies root" do setting = FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") setting.owner.should == "root" end it "should be the owner of the service when the setting specifies service and the service user should be used" do settings = mock("settings") settings.stubs(:[]).returns "the_service" setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") setting.expects(:use_service_user?).returns true setting.owner.should == "the_service" end it "should be the root when the setting specifies service and the service user should not be used" do settings = mock("settings") settings.stubs(:[]).returns "the_service" setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") setting.expects(:use_service_user?).returns false setting.owner.should == "root" end it "should be nil when the owner is unspecified" do FileSetting.new(:settings => mock("settings"), :desc => "a setting").owner.should be_nil end end describe "when setting the group" do it "should allow the group to be service" do service_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "service", :desc => "a setting") } service_group.should_not raise_error end it "should allow the group to be unspecified" do no_group = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } no_group.should_not raise_error end it "should not allow invalid groups" do invalid_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "invalid", :desc => "a setting") } invalid_group.should raise_error(FileSetting::SettingError) end end describe "when reading the group" do it "should be service when the setting specifies service" do setting = FileSetting.new(:settings => mock("settings", :[] => "the_service"), :group => "service", :desc => "a setting") setting.group.should == "the_service" end it "should be nil when the group is unspecified" do FileSetting.new(:settings => mock("settings"), :desc => "a setting").group.should be_nil end end it "should be able to be converted into a resource" do FileSetting.new(:settings => mock("settings"), :desc => "eh").should respond_to(:to_resource) end describe "when being converted to a resource" do before do @settings = mock 'settings' @file = Puppet::Util::Settings::FileSetting.new(:settings => @settings, :desc => "eh", :name => :mydir, :section => "mysect") @settings.stubs(:value).with(:mydir).returns @basepath end it "should skip files that cannot determine their types" do @file.expects(:type).returns nil @file.to_resource.should be_nil end it "should skip non-existent files if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file File.expects(:exist?).with(@basepath).returns false @file.to_resource.should be_nil end it "should manage existent files even if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file File.expects(:exist?).with(@basepath).returns true @file.to_resource.should be_instance_of(Puppet::Resource) end describe "on POSIX systems", :if => Puppet.features.posix? do it "should skip files in /dev" do @settings.stubs(:value).with(:mydir).returns "/dev/file" @file.to_resource.should be_nil end end it "should skip files whose paths are not strings" do @settings.stubs(:value).with(:mydir).returns :foo @file.to_resource.should be_nil end it "should return a file resource with the path set appropriately" do resource = @file.to_resource resource.type.should == "File" resource.title.should == @basepath end it "should fully qualified returned files if necessary (#795)" do @settings.stubs(:value).with(:mydir).returns "myfile" @file.to_resource.title.should == File.join(Dir.getwd, "myfile") end it "should set the mode on the file if a mode is provided" do @file.mode = 0755 @file.to_resource[:mode].should == 0755 end it "should not set the mode on a the file if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false @file.stubs(:mode).returns(0755) @file.to_resource[:mode].should == nil end it "should set the owner if running as root and the owner is provided" do Puppet.features.expects(:root?).returns true @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == "foo" end it "should not set the owner if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false Puppet.features.stubs(:root?).returns true @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == nil end it "should set the group if running as root and the group is provided" do Puppet.features.expects(:root?).returns true @file.stubs(:group).returns "foo" @file.to_resource[:group].should == "foo" end it "should not set the group if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false Puppet.features.stubs(:root?).returns true @file.stubs(:group).returns "foo" @file.to_resource[:group].should == nil end it "should not set owner if not running as root" do Puppet.features.expects(:root?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should be_nil end it "should not set group if not running as root" do Puppet.features.expects(:root?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should be_nil end it "should set :ensure to the file type" do @file.expects(:type).returns :directory @file.to_resource[:ensure].should == :directory end it "should set the loglevel to :debug" do @file.to_resource[:loglevel].should == :debug end it "should set the backup to false" do @file.to_resource[:backup].should be_false end it "should tag the resource with the settings section" do @file.expects(:section).returns "mysect" @file.to_resource.should be_tagged("mysect") end it "should tag the resource with the setting name" do @file.to_resource.should be_tagged("mydir") end it "should tag the resource with 'settings'" do @file.to_resource.should be_tagged("settings") end end end diff --git a/spec/unit/util/settings_spec.rb b/spec/unit/util/settings_spec.rb index 2ab31c294..aa50c8f3a 100755 --- a/spec/unit/util/settings_spec.rb +++ b/spec/unit/util/settings_spec.rb @@ -1,1108 +1,1107 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Util::Settings do describe "when specifying defaults" do before do @settings = Puppet::Util::Settings.new end it "should start with no defined parameters" do @settings.params.length.should == 0 end it "should allow specification of default values associated with a section as an array" do @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) end it "should not allow duplicate parameter specifications" do @settings.setdefaults(:section, :myvalue => ["a", "b"]) lambda { @settings.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError) end it "should allow specification of default values associated with a section as a hash" do @settings.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"}) end it "should consider defined parameters to be valid" do @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) @settings.valid?(:myvalue).should be_true end it "should require a description when defaults are specified with an array" do lambda { @settings.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError) end it "should require a description when defaults are specified with a hash" do lambda { @settings.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError) end it "should raise an error if we can't guess the type" do lambda { @settings.setdefaults(:section, :myvalue => {:default => Object.new, :desc => "An impossible object"}) }.should raise_error(ArgumentError) end it "should support specifying owner, group, and mode when specifying files" do @settings.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "service", :mode => "boo", :group => "service", :desc => "whatever"}) end it "should support specifying a short name" do @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) end it "should support specifying the setting type" do @settings.setdefaults(:section, :myvalue => {:default => "/w", :desc => "b", :type => :setting}) @settings.setting(:myvalue).should be_instance_of(Puppet::Util::Settings::Setting) end it "should fail if an invalid setting type is specified" do lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :type => :foo}) }.should raise_error(ArgumentError) end it "should fail when short names conflict" do @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError) end end describe "when setting values" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :main, :myval => ["val", "desc"] @settings.setdefaults :main, :bool => [true, "desc"] end it "should provide a method for setting values from other objects" do @settings[:myval] = "something else" @settings[:myval].should == "something else" end it "should support a getopt-specific mechanism for setting values" do @settings.handlearg("--myval", "newval") @settings[:myval].should == "newval" end it "should support a getopt-specific mechanism for turning booleans off" do @settings[:bool] = true @settings.handlearg("--no-bool", "") @settings[:bool].should == false end it "should support a getopt-specific mechanism for turning booleans on" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with no argument to be a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be a boolean, if the setting itself is a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do @settings[:myval] = "bob" @settings.handlearg("--myval", "") @settings[:myval].should == "" end it "should consider a cli setting with a boolean as an argument to be a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "true") @settings[:bool].should == true end it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do # Turn it off first @settings[:myval] = "bob" @settings.handlearg("--no-myval", "") @settings[:myval].should == "" end it "should clear the cache when setting getopt-specific values" do @settings.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"] @settings[:two].should == "whah yay" @settings.handlearg("--one", "else") @settings[:two].should == "else yay" end it "should not clear other values when setting getopt-specific values" do @settings[:myval] = "yay" @settings.handlearg("--no-bool", "") @settings[:myval].should == "yay" end it "should clear the list of used sections" do @settings.expects(:clearused) @settings[:myval] = "yay" end it "should call passed blocks when values are set" do values = [] @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings[:hooker] = "something" values.should == %w{something} end it "should call passed blocks when values are set via the command line" do values = [] @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings.handlearg("--hooker", "yay") values.should == %w{yay} end it "should provide an option to call passed blocks during definition" do values = [] @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) values.should == %w{yay} end it "should pass the fully interpolated value to the hook when called on definition" do values = [] @settings.setdefaults(:section, :one => ["test", "a"]) @settings.setdefaults(:section, :hooker => {:default => "$one/yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) values.should == %w{test/yay} end it "should munge values using the setting-specific methods" do @settings[:bool] = "false" @settings[:bool].should == false end it "should prefer cli values to values set in Ruby code" do @settings.handlearg("--myval", "cliarg") @settings[:myval] = "memarg" @settings[:myval].should == "cliarg" end it "should clear the list of environments" do Puppet::Node::Environment.expects(:clear).at_least(1) @settings[:myval] = "memarg" end it "should raise an error if we try to set 'name'" do lambda{ @settings[:name] = "foo" }.should raise_error(ArgumentError) end it "should raise an error if we try to set 'run_mode'" do lambda{ @settings[:run_mode] = "foo" }.should raise_error(ArgumentError) end it "should warn and use [master] if we ask for [puppetmasterd]" do Puppet.expects(:warning) @settings.set_value(:myval, "foo", :puppetmasterd) @settings.stubs(:run_mode).returns(:master) @settings.value(:myval).should == "foo" end it "should warn and use [agent] if we ask for [puppetd]" do Puppet.expects(:warning) @settings.set_value(:myval, "foo", :puppetd) @settings.stubs(:run_mode).returns(:agent) @settings.value(:myval).should == "foo" end end describe "when returning values" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/my/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"] FileTest.stubs(:exist?).returns true end it "should provide a mechanism for returning set values" do @settings[:one] = "other" @settings[:one].should == "other" end it "should interpolate default values for other parameters into returned parameter values" do @settings[:one].should == "ONE" @settings[:two].should == "ONE TWO" @settings[:three].should == "ONE ONE TWO THREE" end it "should interpolate default values that themselves need to be interpolated" do @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR" end it "should provide a method for returning uninterpolated values" do @settings[:two] = "$one tw0" @settings.uninterpolated_value(:two).should == "$one tw0" @settings.uninterpolated_value(:four).should == "$two $three FOUR" end it "should interpolate set values for other parameters into returned parameter values" do @settings[:one] = "on3" @settings[:two] = "$one tw0" @settings[:three] = "$one $two thr33" @settings[:four] = "$one $two $three f0ur" @settings[:one].should == "on3" @settings[:two].should == "on3 tw0" @settings[:three].should == "on3 on3 tw0 thr33" @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur" end it "should not cache interpolated values such that stale information is returned" do @settings[:two].should == "ONE TWO" @settings[:one] = "one" @settings[:two].should == "one TWO" end it "should not cache values such that information from one environment is returned for another environment" do text = "[env1]\none = oneval\n[env2]\none = twoval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:one, "env1").should == "oneval" @settings.value(:one, "env2").should == "twoval" end it "should have a run_mode that defaults to user" do @settings.run_mode.should == :user end end describe "when choosing which value to return" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/my/file", "a"], :one => ["ONE", "a"], :two => ["TWO", "b"] FileTest.stubs(:exist?).returns true Puppet.stubs(:run_mode).returns stub('run_mode', :name => :mymode) end it "should return default values if no values have been set" do @settings[:one].should == "ONE" end it "should return values set on the cli before values set in the configuration file" do text = "[main]\none = fileval\n" @settings.stubs(:read_file).returns(text) @settings.handlearg("--one", "clival") @settings.parse @settings[:one].should == "clival" end it "should return values set on the cli before values set in Ruby" do @settings[:one] = "rubyval" @settings.handlearg("--one", "clival") @settings[:one].should == "clival" end it "should return values set in the mode-specific section before values set in the main section" do text = "[main]\none = mainval\n[mymode]\none = modeval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings[:one].should == "modeval" end it "should not return values outside of its search path" do text = "[other]\none = oval\n" file = "/some/file" @settings.stubs(:read_file).returns(text) @settings.parse @settings[:one].should == "ONE" end it "should return values in a specified environment" do text = "[env]\none = envval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:one, "env").should == "envval" end it 'should use the current environment for $environment' do @settings.setdefaults :main, :myval => ["$environment/foo", "mydocs"] @settings.value(:myval, "myenv").should == "myenv/foo" end it "should interpolate found values using the current environment" do text = "[main]\none = mainval\n[myname]\none = nameval\ntwo = $one/two\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:two, "myname").should == "nameval/two" end it "should return values in a specified environment before values in the main or name sections" do text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:one, "env").should == "envval" end end describe "when parsing its configuration" do before do @settings = Puppet::Util::Settings.new @settings.stubs(:service_user_available?).returns true @file = "/some/file" @settings.setdefaults :section, :user => ["suser", "doc"], :group => ["sgroup", "doc"] @settings.setdefaults :section, :config => ["/some/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] FileTest.stubs(:exist?).returns true end it "should not ignore the report setting" do @settings.setdefaults :section, :report => ["false", "a"] myfile = stub "myfile" @settings[:config] = myfile text = <<-CONF [puppetd] report=true CONF @settings.expects(:read_file).returns(text) @settings.parse @settings[:report].should be_true end it "should use its current ':config' value for the file to parse" do myfile = Puppet.features.posix? ? "/my/file" : "C:/myfile" # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it @settings[:config] = myfile File.expects(:read).with(myfile).returns "[main]" @settings.parse end it "should fail if no configuration setting is defined" do @settings = Puppet::Util::Settings.new lambda { @settings.parse }.should raise_error(RuntimeError) end it "should not try to parse non-existent files" do FileTest.expects(:exist?).with("/some/file").returns false File.expects(:read).with("/some/file").never @settings.parse end it "should set a timer that triggers reparsing, even if the file does not exist" do FileTest.expects(:exist?).returns false @settings.expects(:set_filetimeout_timer) @settings.parse end it "should return values set in the configuration file" do text = "[main] one = fileval " @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == "fileval" end #484 - this should probably be in the regression area it "should not throw an exception on unknown parameters" do text = "[main]\nnosuchparam = mval\n" @settings.expects(:read_file).returns(text) lambda { @settings.parse }.should_not raise_error end it "should convert booleans in the configuration file into Ruby booleans" do text = "[main] one = true two = false " @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == true @settings[:two].should == false end it "should convert integers in the configuration file into Ruby Integers" do text = "[main] one = 65 " @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == 65 end it "should support specifying all metadata (owner, group, mode) in the configuration file" do @settings.setdefaults :section, :myfile => ["/myfile", "a"] text = "[main] myfile = /other/file {owner = service, group = service, mode = 644} " @settings.expects(:read_file).returns(text) @settings.parse @settings[:myfile].should == "/other/file" @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} end it "should support specifying a single piece of metadata (owner, group, or mode) in the configuration file" do @settings.setdefaults :section, :myfile => ["/myfile", "a"] text = "[main] myfile = /other/file {owner = service} " file = "/some/file" @settings.expects(:read_file).returns(text) @settings.parse @settings[:myfile].should == "/other/file" @settings.metadata(:myfile).should == {:owner => "suser"} end it "should call hooks associated with values set in the configuration file" do values = [] @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = setval " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["setval"] end it "should not call the same hook for values set multiple times in the configuration file" do values = [] @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[user] mysetting = setval [main] mysetting = other " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["setval"] end it "should pass the environment-specific value to the hook when one is available" do values = [] @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} @settings.setdefaults :section, :environment => ["yay", "a"] @settings.setdefaults :section, :environments => ["yay,foo", "a"] text = "[main] mysetting = setval [yay] mysetting = other " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["other"] end it "should pass the interpolated value to the hook when one is available" do values = [] @settings.setdefaults :section, :base => {:default => "yay", :desc => "a", :hook => proc { |v| values << v }} @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = $base/setval " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["yay/setval"] end it "should allow empty values" do @settings.setdefaults :section, :myarg => ["myfile", "a"] text = "[main] myarg = " @settings.stubs(:read_file).returns(text) @settings.parse @settings[:myarg].should == "" end describe "and when reading a non-positive filetimeout value from the config file" do before do @settings.setdefaults :foo, :filetimeout => [5, "eh"] somefile = "/some/file" text = "[main] filetimeout = -1 " File.expects(:read).with(somefile).returns(text) File.expects(:expand_path).with(somefile).returns somefile @settings[:config] = somefile end it "should not set a timer" do EventLoop::Timer.expects(:new).never @settings.parse end end end describe "when reparsing its configuration" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/test/file", "a"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] FileTest.stubs(:exist?).returns true end it "should use a LoadedFile instance to determine if the file has changed" do file = mock 'file' Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file file.expects(:changed?) @settings.stubs(:parse) @settings.reparse end it "should not create the LoadedFile instance and should not parse if the file does not exist" do FileTest.expects(:exist?).with("/test/file").returns false Puppet::Util::LoadedFile.expects(:new).never @settings.expects(:parse).never @settings.reparse end it "should not reparse if the file has not changed" do file = mock 'file' Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file file.expects(:changed?).returns false @settings.expects(:parse).never @settings.reparse end it "should reparse if the file has changed" do file = stub 'file', :file => "/test/file" Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file file.expects(:changed?).returns true @settings.expects(:parse) @settings.reparse end it "should use a cached LoadedFile instance" do first = mock 'first' second = mock 'second' Puppet::Util::LoadedFile.expects(:new).times(2).with("/test/file").returns(first).then.returns(second) @settings.file.should equal(first) Puppet::Util::Cacher.expire @settings.file.should equal(second) end it "should replace in-memory values with on-file values" do # Init the value text = "[main]\none = disk-init\n" file = mock 'file' file.stubs(:changed?).returns(true) file.stubs(:file).returns("/test/file") @settings[:one] = "init" @settings.file = file # Now replace the value text = "[main]\none = disk-replace\n" # This is kinda ridiculous - the reason it parses twice is that # it goes to parse again when we ask for the value, because the # mock always says it should get reparsed. @settings.stubs(:read_file).returns(text) @settings.reparse @settings[:one].should == "disk-replace" end it "should retain parameters set by cli when configuration files are reparsed" do @settings.handlearg("--one", "clival") text = "[main]\none = on-disk\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings[:one].should == "clival" end it "should remove in-memory values that are no longer set in the file" do # Init the value text = "[main]\none = disk-init\n" @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == "disk-init" # Now replace the value text = "[main]\ntwo = disk-replace\n" @settings.expects(:read_file).returns(text) @settings.parse #@settings.reparse # The originally-overridden value should be replaced with the default @settings[:one].should == "ONE" # and we should now have the new value in memory @settings[:two].should == "disk-replace" end it "should retain in-memory values if the file has a syntax error" do # Init the value text = "[main]\none = initial-value\n" @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == "initial-value" # Now replace the value with something bogus text = "[main]\nkenny = killed-by-what-follows\n1 is 2, blah blah florp\n" @settings.expects(:read_file).returns(text) @settings.parse # The originally-overridden value should not be replaced with the default @settings[:one].should == "initial-value" # and we should not have the new value in memory @settings[:kenny].should be_nil end end it "should provide a method for creating a catalog of resources from its configuration" do Puppet::Util::Settings.new.should respond_to(:to_catalog) end describe "when creating a catalog" do before do @settings = Puppet::Util::Settings.new @settings.stubs(:service_user_available?).returns true @prefix = Puppet.features.posix? ? "" : "C:" end it "should add all file resources to the catalog if no sections have been specified" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"], :seconddir => [@prefix+"/seconddir", "a"] @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] catalog = @settings.to_catalog [@prefix+"/maindir", @prefix+"/seconddir", @prefix+"/otherdir"].each do |path| catalog.resource(:file, path).should be_instance_of(Puppet::Resource) end end it "should add only files in the specified sections if section names are provided" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] catalog = @settings.to_catalog(:main) catalog.resource(:file, @prefix+"/otherdir").should be_nil catalog.resource(:file, @prefix+"/maindir").should be_instance_of(Puppet::Resource) end it "should not try to add the same file twice" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] @settings.setdefaults :other, :otherdir => [@prefix+"/maindir", "a"] lambda { @settings.to_catalog }.should_not raise_error end it "should ignore files whose :to_resource method returns nil" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] @settings.setting(:maindir).expects(:to_resource).returns nil Puppet::Resource::Catalog.any_instance.expects(:add_resource).never @settings.to_catalog end describe "when adding users and groups to the catalog" do before do Puppet.features.stubs(:root?).returns true @settings.setdefaults :foo, :mkusers => [true, "e"], :user => ["suser", "doc"], :group => ["sgroup", "doc"] @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} @catalog = @settings.to_catalog end it "should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root" do @catalog.resource(:user, "suser").should be_instance_of(Puppet::Resource) @catalog.resource(:group, "sgroup").should be_instance_of(Puppet::Resource) end it "should only add users and groups to the catalog from specified sections" do @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} catalog = @settings.to_catalog(:other) catalog.resource(:user, "jane").should be_nil catalog.resource(:group, "billy").should be_nil end it "should not add users or groups to the catalog if :mkusers not running as root" do Puppet.features.stubs(:root?).returns false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is not a valid setting" do Puppet.features.stubs(:root?).returns true settings = Puppet::Util::Settings.new settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} catalog = settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is a valid setting but is disabled" do @settings[:mkusers] = false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not try to add users or groups to the catalog twice" do @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} # This would fail if users/groups were added twice lambda { @settings.to_catalog }.should_not raise_error end it "should set :ensure to :present on each created user and group" do @catalog.resource(:user, "suser")[:ensure].should == :present @catalog.resource(:group, "sgroup")[:ensure].should == :present end it "should set each created user's :gid to the service group" do @settings.to_catalog.resource(:user, "suser")[:gid].should == "sgroup" end it "should not attempt to manage the root user" do Puppet.features.stubs(:root?).returns true @settings.setdefaults :foo, :foodir => {:default => "/foodir", :desc => "a", :owner => "root", :group => "service"} @settings.to_catalog.resource(:user, "root").should be_nil end end end it "should be able to be converted to a manifest" do Puppet::Util::Settings.new.should respond_to(:to_manifest) end describe "when being converted to a manifest" do it "should produce a string with the code for each resource joined by two carriage returns" do @settings = Puppet::Util::Settings.new @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] main = stub 'main_resource', :ref => "File[/maindir]" main.expects(:to_manifest).returns "maindir" second = stub 'second_resource', :ref => "File[/seconddir]" second.expects(:to_manifest).returns "seconddir" @settings.setting(:maindir).expects(:to_resource).returns main @settings.setting(:seconddir).expects(:to_resource).returns second @settings.to_manifest.split("\n\n").sort.should == %w{maindir seconddir} end end describe "when using sections of the configuration to manage the local host" do before do @settings = Puppet::Util::Settings.new @settings.stubs(:service_user_available?).returns true @settings.setdefaults :main, :noop => [false, ""] @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] @settings.setdefaults :main, :user => ["suser", "doc"], :group => ["sgroup", "doc"] @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service", :mode => 0755} @settings.setdefaults :third, :thirddir => ["/thirddir", "b"] @settings.setdefaults :files, :myfile => {:default => "/myfile", :desc => "a", :mode => 0755} end it "should provide a method that writes files with the correct modes" do @settings.should respond_to(:write) end it "should provide a method that creates directories with the correct modes" do Puppet::Util::SUIDManager.expects(:asuser).with("suser", "sgroup").yields Dir.expects(:mkdir).with("/otherdir", 0755) @settings.mkdir(:otherdir) end it "should create a catalog with the specified sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should canonicalize the sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use("main", "other") end it "should ignore sections that have already been used" do @settings.expects(:to_catalog).with(:main).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main) @settings.expects(:to_catalog).with(:other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should ignore tags and schedules when creating files and directories" it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do pending "Not converted from test/unit yet" end it "should convert the created catalog to a RAL catalog" do @catalog = Puppet::Resource::Catalog.new("foo") @settings.expects(:to_catalog).with(:main).returns @catalog @catalog.expects(:to_ral).returns @catalog @settings.use(:main) end it "should specify that it is not managing a host catalog" do catalog = Puppet::Resource::Catalog.new("foo") catalog.expects(:apply) @settings.expects(:to_catalog).returns catalog catalog.stubs(:to_ral).returns catalog catalog.expects(:host_config=).with false @settings.use(:main) end it "should support a method for re-using all currently used sections" do @settings.expects(:to_catalog).with(:main, :third).times(2).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :third) @settings.reuse end it "should fail with an appropriate message if any resources fail" do @catalog = Puppet::Resource::Catalog.new("foo") @catalog.stubs(:to_ral).returns @catalog @settings.expects(:to_catalog).returns @catalog @trans = mock("transaction") @catalog.expects(:apply).yields(@trans) @trans.expects(:any_failed?).returns(true) report = mock 'report' @trans.expects(:report).returns report log = mock 'log', :to_s => "My failure", :level => :err report.expects(:logs).returns [log] @settings.expects(:raise).with { |msg| msg.include?("My failure") } @settings.use(:whatever) end end describe "when dealing with printing configs" do before do @settings = Puppet::Util::Settings.new #these are the magic default values @settings.stubs(:value).with(:configprint).returns("") @settings.stubs(:value).with(:genconfig).returns(false) @settings.stubs(:value).with(:genmanifest).returns(false) @settings.stubs(:value).with(:environment).returns(nil) end describe "when checking print_config?" do it "should return false when the :configprint, :genconfig and :genmanifest are not set" do @settings.print_configs?.should be_false end it "should return true when :configprint has a value" do @settings.stubs(:value).with(:configprint).returns("something") @settings.print_configs?.should be_true end it "should return true when :genconfig has a value" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.print_configs?.should be_true end it "should return true when :genmanifest has a value" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.print_configs?.should be_true end end describe "when printing configs" do describe "when :configprint has a value" do it "should call print_config_options" do @settings.stubs(:value).with(:configprint).returns("something") @settings.expects(:print_config_options) @settings.print_configs end it "should get the value of the option using the environment" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.expects(:value).with(:environment).returns("env") @settings.expects(:value).with("something", "env").returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs end it "should print the value of the option" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.expects(:puts).with("foo") @settings.print_configs end it "should print the value pairs if there are multiple options" do @settings.stubs(:value).with(:configprint).returns("bar,baz") @settings.stubs(:include?).with("bar").returns(true) @settings.stubs(:include?).with("baz").returns(true) @settings.stubs(:value).with("bar", nil).returns("foo") @settings.stubs(:value).with("baz", nil).returns("fud") @settings.expects(:puts).with("bar = foo") @settings.expects(:puts).with("baz = fud") @settings.print_configs end it "should print a whole bunch of stuff if :configprint = all" it "should return true after printing" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs.should be_true end it "should return false if a config param is not found" do @settings.stubs :puts @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(false) @settings.print_configs.should be_false end end describe "when genconfig is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.expects(:to_config) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.stubs(:to_config) @settings.print_configs.should be_true end end describe "when genmanifest is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.expects(:to_manifest) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.stubs(:to_manifest) @settings.print_configs.should be_true end end end end describe "when setting a timer to trigger configuration file reparsing" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :foo, :filetimeout => [5, "eh"] end it "should do nothing if no filetimeout setting is available" do @settings.expects(:value).with(:filetimeout).returns nil EventLoop::Timer.expects(:new).never @settings.set_filetimeout_timer end it "should always convert the timer interval to an integer" do @settings.expects(:value).with(:filetimeout).returns "10" EventLoop::Timer.expects(:new).with(:interval => 10, :start? => true, :tolerance => 1) @settings.set_filetimeout_timer end it "should do nothing if the filetimeout setting is not greater than 0" do @settings.expects(:value).with(:filetimeout).returns -2 EventLoop::Timer.expects(:new).never @settings.set_filetimeout_timer end it "should create a timer with its interval set to the filetimeout, start? set to true, and a tolerance of 1" do @settings.expects(:value).with(:filetimeout).returns 5 EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1) @settings.set_filetimeout_timer end it "should reparse when the timer goes off" do EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).yields @settings.expects(:reparse) @settings.set_filetimeout_timer end end describe "when determining if the service user is available" do it "should return false if there is no user setting" do Puppet::Util::Settings.new.should_not be_service_user_available end it "should return false if the user provider says the user is missing" do settings = Puppet::Util::Settings.new settings.setdefaults :main, :user => ["foo", "doc"] user = mock 'user' user.expects(:exists?).returns false Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user settings.should_not be_service_user_available end it "should return true if the user provider says the user is present" do settings = Puppet::Util::Settings.new settings.setdefaults :main, :user => ["foo", "doc"] user = mock 'user' user.expects(:exists?).returns true Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user settings.should be_service_user_available end it "should cache the result" end end diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb index 5359f746f..90c11aa69 100755 --- a/spec/unit/util/storage_spec.rb +++ b/spec/unit/util/storage_spec.rb @@ -1,234 +1,233 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'yaml' require 'puppet/util/storage' describe Puppet::Util::Storage do include PuppetSpec::Files before(:all) do @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" Puppet[:statedir] = tmpdir("statedir") end after(:all) do Puppet.settings.clear end before(:each) do Puppet::Util::Storage.clear end describe "when caching a symbol" do it "should return an empty hash" do Puppet::Util::Storage.cache(:yayness).should == {} Puppet::Util::Storage.cache(:more_yayness).should == {} end it "should add the symbol to its internal state" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} end it "should not clobber existing state when caching additional objects" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet::Util::Storage.cache(:bubblyness) Puppet::Util::Storage.state.should == {:yayness=>{},:bubblyness=>{}} end end describe "when caching a Puppet::Type" do before(:all) do @file_test = Puppet::Type.type(:file).new(:name => @basepath+"/yayness", :check => %w{checksum type}) @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+"/bin/ls /yayness") end it "should return an empty hash" do Puppet::Util::Storage.cache(@file_test).should == {} Puppet::Util::Storage.cache(@exec_test).should == {} end it "should add the resource ref to its internal state" do Puppet::Util::Storage.state.should == {} Puppet::Util::Storage.cache(@file_test) Puppet::Util::Storage.state.should == {"File[#{@basepath}/yayness]"=>{}} Puppet::Util::Storage.cache(@exec_test) Puppet::Util::Storage.state.should == {"File[#{@basepath}/yayness]"=>{}, "Exec[#{@basepath}/bin/ls /yayness]"=>{}} end end describe "when caching something other than a resource or symbol" do it "should cache by converting to a string" do data = Puppet::Util::Storage.cache(42) data[:yay] = true Puppet::Util::Storage.cache("42")[:yay].should be_true end end it "should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet::Util::Storage.clear Puppet::Util::Storage.state.should == {} end describe "when loading from the state file" do before do Puppet.settings.stubs(:use).returns(true) end describe "when the state file/directory does not exist" do before(:each) do transient = Tempfile.new('storage_test') @path = transient.path() transient.close!() end it "should not fail to load()" do FileTest.exists?(@path).should be_false Puppet[:statedir] = @path proc { Puppet::Util::Storage.load }.should_not raise_error Puppet[:statefile] = @path proc { Puppet::Util::Storage.load }.should_not raise_error end it "should not lose its internal state when load() is called" do FileTest.exists?(@path).should be_false Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet[:statefile] = @path proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {:yayness=>{}} end end describe "when the state file/directory exists" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path end it "should overwrite its internal state if load() is called" do # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should restore its internal state if the state file contains valid YAML" do test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == test_yaml end it "should initialize with a clear internal state if the state file does not contain valid YAML" do @state_file.write(:booness) @state_file.flush proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do @state_file.write(:booness) @state_file.flush YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).raises(SystemCallError) proc { Puppet::Util::Storage.load }.should raise_error end it "should attempt to rename the state file if the file is corrupted" do # We fake corruption by causing YAML.load to raise an exception YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).at_least_once proc { Puppet::Util::Storage.load }.should_not raise_error end it "should fail gracefully on load() if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) proc { Puppet::Util::Storage.load }.should_not raise_error Dir.rmdir(Puppet[:statefile]) end it "should fail gracefully on load() if it cannot get a read lock on the state file" do Puppet::Util::FileLocking.expects(:readlock).yields(false) test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == test_yaml end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end describe "when storing to the state file" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path end it "should create the state file if it does not exist" do @state_file.close!() FileTest.exists?(Puppet[:statefile]).should be_false Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should_not raise_error FileTest.exists?(Puppet[:statefile]).should be_true end it "should raise an exception if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should raise_error Dir.rmdir(Puppet[:statefile]) end it "should raise an exception if it cannot get a write lock on the state file" do Puppet::Util::FileLocking.expects(:writelock).yields(false) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should raise_error end it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} proc { Puppet::Util::Storage.store }.should_not raise_error Puppet::Util::Storage.clear Puppet::Util::Storage.state.should == {} proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {:yayness=>{}} end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end diff --git a/spec/unit/util/tagging_spec.rb b/spec/unit/util/tagging_spec.rb index 5231be2eb..018871bef 100755 --- a/spec/unit/util/tagging_spec.rb +++ b/spec/unit/util/tagging_spec.rb @@ -1,102 +1,102 @@ -#!/usr/bin/env ruby +#!/usr/bin/env rspec # # Created by Luke Kanies on 2008-01-19. # Copyright (c) 2007. All rights reserved. -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'spec_helper' require 'puppet/util/tagging' describe Puppet::Util::Tagging, "when adding tags" do before do @tagger = Object.new @tagger.extend(Puppet::Util::Tagging) end it "should have a method for adding tags" do @tagger.should be_respond_to(:tag) end it "should have a method for returning all tags" do @tagger.should be_respond_to(:tags) end it "should add tags to the returned tag list" do @tagger.tag("one") @tagger.tags.should be_include("one") end it "should not add duplicate tags to the returned tag list" do @tagger.tag("one") @tagger.tag("one") @tagger.tags.should == ["one"] end it "should return a duplicate of the tag list, rather than the original" do @tagger.tag("one") tags = @tagger.tags tags << "two" @tagger.tags.should_not be_include("two") end it "should add all provided tags to the tag list" do @tagger.tag("one", "two") @tagger.tags.should be_include("one") @tagger.tags.should be_include("two") end it "should fail on tags containing '*' characters" do lambda { @tagger.tag("bad*tag") }.should raise_error(Puppet::ParseError) end it "should fail on tags starting with '-' characters" do lambda { @tagger.tag("-badtag") }.should raise_error(Puppet::ParseError) end it "should fail on tags containing ' ' characters" do lambda { @tagger.tag("bad tag") }.should raise_error(Puppet::ParseError) end it "should allow alpha tags" do lambda { @tagger.tag("good_tag") }.should_not raise_error(Puppet::ParseError) end it "should allow tags containing '.' characters" do lambda { @tagger.tag("good.tag") }.should_not raise_error(Puppet::ParseError) end it "should provide a method for testing tag validity" do @tagger.singleton_class.publicize_methods(:valid_tag?) { @tagger.should be_respond_to(:valid_tag?) } end it "should add qualified classes as tags" do @tagger.tag("one::two") @tagger.tags.should be_include("one::two") end it "should add each part of qualified classes as tags" do @tagger.tag("one::two::three") @tagger.tags.should be_include("one") @tagger.tags.should be_include("two") @tagger.tags.should be_include("three") end it "should indicate when the object is tagged with a provided tag" do @tagger.tag("one") @tagger.should be_tagged("one") end it "should indicate when the object is not tagged with a provided tag" do @tagger.should_not be_tagged("one") end it "should indicate when the object is tagged with any tag in an array" do @tagger.tag("one") @tagger.should be_tagged("one","two","three") end it "should indicate when the object is not tagged with any tag in an array" do @tagger.tag("one") @tagger.should_not be_tagged("two","three") end end diff --git a/spec/unit/util/user_attr_spec.rb b/spec/unit/util/user_attr_spec.rb old mode 100644 new mode 100755 index 5acdaee58..2d6ba8b5a --- a/spec/unit/util/user_attr_spec.rb +++ b/spec/unit/util/user_attr_spec.rb @@ -1,47 +1,46 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/user_attr' describe UserAttr do before do user_attr = ["foo::::type=role", "bar::::type=normal;profile=foobar"] File.stubs(:readlines).returns(user_attr) end describe "when getting attributes by name" do it "should return nil if there is no entry for that name" do UserAttr.get_attributes_by_name('baz').should == nil end it "should return a hash if there is an entry in /etc/user_attr" do UserAttr.get_attributes_by_name('foo').class.should == Hash end it "should return a hash with the name value from /etc/user_attr" do UserAttr.get_attributes_by_name('foo')[:name].should == 'foo' end #this test is contrived #there are a bunch of possible parameters that could be in the hash #the role/normal is just a the convention of the file describe "when the name is a role" do it "should contain :type = role" do UserAttr.get_attributes_by_name('foo')[:type].should == 'role' end end describe "when the name is not a role" do it "should contain :type = normal" do UserAttr.get_attributes_by_name('bar')[:type].should == 'normal' end end describe "when the name has more attributes" do it "should contain all the attributes" do UserAttr.get_attributes_by_name('bar')[:profile].should == 'foobar' end end end end diff --git a/spec/unit/util/warnings_spec.rb b/spec/unit/util/warnings_spec.rb index a3460bccf..cc2c44711 100755 --- a/spec/unit/util/warnings_spec.rb +++ b/spec/unit/util/warnings_spec.rb @@ -1,39 +1,38 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' describe Puppet::Util::Warnings do before(:all) do @msg1 = "booness" @msg2 = "more booness" end {:notice => "notice_once", :warning => "warnonce"}.each do |log, method| describe "when registring '#{log}' messages" do it "should always return nil" do Puppet::Util::Warnings.send(method, @msg1).should be(nil) end it "should issue a warning" do Puppet.expects(log).with(@msg1) Puppet::Util::Warnings.send(method, @msg1) end it "should issue a warning exactly once per unique message" do Puppet.expects(log).with(@msg1).once Puppet::Util::Warnings.send(method, @msg1) Puppet::Util::Warnings.send(method, @msg1) end it "should issue multiple warnings for multiple unique messages" do Puppet.expects(log).times(2) Puppet::Util::Warnings.send(method, @msg1) Puppet::Util::Warnings.send(method, @msg2) end end end after(:each) do Puppet::Util::Warnings.clear_warnings end end diff --git a/spec/unit/util/zaml_spec.rb b/spec/unit/util/zaml_spec.rb index 804aa8e58..d77cf99d2 100755 --- a/spec/unit/util/zaml_spec.rb +++ b/spec/unit/util/zaml_spec.rb @@ -1,63 +1,62 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +#!/usr/bin/env rspec +require 'spec_helper' require 'puppet/util/monkey_patches' describe "Pure ruby yaml implementation" do { 7 => "--- 7", 3.14159 => "--- 3.14159", 'test' => "--- test", [] => "--- []", :symbol => "--- !ruby/sym symbol", {:a => "A"} => "--- \n !ruby/sym a: A", {:a => "x\ny"} => "--- \n !ruby/sym a: |-\n x\n y" }.each { |o,y| it "should convert the #{o.class} #{o.inspect} to yaml" do o.to_yaml.should == y end it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do YAML.load(o.to_yaml).should == o end } # # Can't test for equality on raw objects { Object.new => "--- !ruby/object {}", [Object.new] => "--- \n - !ruby/object {}", {Object.new => Object.new} => "--- \n ? !ruby/object {}\n : !ruby/object {}" }.each { |o,y| it "should convert the #{o.class} #{o.inspect} to yaml" do o.to_yaml.should == y end it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do lambda { YAML.load(o.to_yaml) }.should_not raise_error end } it "should emit proper labels and backreferences for common objects" do # Note: this test makes assumptions about the names ZAML chooses # for labels. x = [1, 2] y = [3, 4] z = [x, y, x, y] z.to_yaml.should == "--- \n - &id001\n - 1\n - 2\n - &id002\n - 3\n - 4\n - *id001\n - *id002" z2 = YAML.load(z.to_yaml) z2.should == z z2[0].should equal(z2[2]) z2[1].should equal(z2[3]) end it "should emit proper labels and backreferences for recursive objects" do x = [1, 2] x << x x.to_yaml.should == "--- &id001\n \n - 1\n - 2\n - *id001" x2 = YAML.load(x.to_yaml) x2.should be_a(Array) x2.length.should == 3 x2[0].should == 1 x2[1].should == 2 x2[2].should equal(x2) end end diff --git a/autotest/watcher.rb b/spec/watchr.rb old mode 100644 new mode 100755 similarity index 85% rename from autotest/watcher.rb rename to spec/watchr.rb index 9f89a448c..26919d1a1 --- a/autotest/watcher.rb +++ b/spec/watchr.rb @@ -1,138 +1,142 @@ ENV["WATCHR"] = "1" ENV['AUTOTEST'] = 'true' def run_comp(cmd) puts cmd results = [] old_sync = $stdout.sync $stdout.sync = true line = [] begin open("| #{cmd}", "r") do |f| until f.eof? do c = f.getc putc c line << c if c == ?\n results << if RUBY_VERSION >= "1.9" then - line.join - else - line.pack "c*" - end + line.join + else + line.pack "c*" + end line.clear end end end ensure $stdout.sync = old_sync end results.join end def clear #system("clear") end def growl(message, status) # Strip the color codes message.gsub!(/\[\d+m/, '') growlnotify = `which growlnotify`.chomp return if growlnotify.empty? title = "Watchr Test Results" image = status == :pass ? "autotest/images/pass.png" : "autotest/images/fail.png" options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'" system %(#{growlnotify} #{options} &) end def file2specs(file) %w{spec/unit spec/integration}.collect { |d| - file.sub('lib/puppet', d) + file.sub('lib/puppet', d).sub(".rb", "_spec.rb") }.find_all { |f| File.exist?(f) } end def file2test(file) result = file.sub('lib/puppet', 'test') return nil unless File.exist?(result) result end def run_spec(command) clear result = run_comp(command).split("\n").last status = result.include?('0 failures') ? :pass : :fail growl result, status end def run_test(command) clear result = run_comp(command).split("\n").last status = result.include?('0 failures, 0 errors') ? :pass : :fail growl result.split("\n").last rescue nil end def run_test_file(file) run_test(%Q(#{file})) end def run_spec_files(files) files = Array(files) return if files.empty? - opts = File.readlines('spec/spec.opts').collect { |l| l.chomp }.join(" ") - run_spec("spec #{files.join(' ')}") + if File.exist?(File.expand_path("~/.rspec")) then + opts = '' # use the user defaults + else + opts = File.readlines('spec/spec.opts').collect { |l| l.chomp }.join(" ") + end + run_spec("rspec #{opts} --tty #{files.join(' ')}") end def run_all_tests run_test("rake unit") end def run_all_specs - run_test("rake spec") + run_spec_files "spec" end def run_suite - run_all_tests run_all_specs + run_all_tests end watch('spec/spec_helper.rb') { run_all_specs } watch(%r{^spec/(unit|integration)/.*\.rb$}) { |md| run_spec_files(md[0]) } watch(%r{^lib/puppet/(.*)\.rb$}) { |md| run_spec_files(file2specs(md[0])) if t = file2test(md[0]) run_test_file(t) end } watch(%r{^spec/lib/spec.*}) { |md| run_all_specs } watch(%r{^spec/lib/monkey_patches/.*}) { |md| run_all_specs } watch(%r{test/.+\.rb}) { |md| if md[0] =~ /\/lib\// run_all_tests else run_test_file(md[0]) end } # Ctrl-\ Signal.trap 'QUIT' do puts " --- Running all tests ---\n\n" run_suite end @interrupted = false # Ctrl-C Signal.trap 'INT' do if @interrupted @wants_to_quit = true abort("\n") else puts "Interrupt a second time to quit; wait for rerun of tests" @interrupted = true Kernel.sleep 1.5 # raise Interrupt, nil # let the run loop catch it run_suite end end diff --git a/test/language/functions.rb b/test/language/functions.rb index e882b68f3..84b1b3861 100755 --- a/test/language/functions.rb +++ b/test/language/functions.rb @@ -1,543 +1,541 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../lib/puppettest') require 'puppet' require 'puppet/parser/parser' require 'puppet/network/client' require 'puppettest' require 'puppettest/resourcetesting' class TestLangFunctions < Test::Unit::TestCase include PuppetTest::ParserTesting include PuppetTest::ResourceTesting def test_functions Puppet::Node::Environment.stubs(:current).returns nil assert_nothing_raised do Puppet::Parser::AST::Function.new( :name => "fakefunction", :arguments => AST::ASTArray.new( :children => [nameobj("avalue")] ) ) end assert_raise(Puppet::ParseError) do func = Puppet::Parser::AST::Function.new( :name => "fakefunction", :arguments => AST::ASTArray.new( :children => [nameobj("avalue")] ) ) func.evaluate(mkscope) end assert_nothing_raised do Puppet::Parser::Functions.newfunction(:fakefunction, :type => :rvalue) do |input| return "output #{input[0]}" end end func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "fakefunction", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [nameobj("avalue")] ) ) end scope = mkscope val = nil assert_nothing_raised do val = func.evaluate(scope) end assert_equal("output avalue", val) end def test_taggedfunction scope = mkscope scope.resource.tag("yayness") # Make sure the ast stuff does what it's supposed to {"yayness" => true, "booness" => false}.each do |tag, retval| func = taggedobj(tag, :rvalue) val = nil assert_nothing_raised do val = func.evaluate(scope) end assert_equal(retval, val, "'tagged' returned #{val} for #{tag}") end # Now make sure we correctly get tags. scope.resource.tag("resourcetag") assert(scope.function_tagged("resourcetag"), "tagged function did not catch resource tags") scope.compiler.catalog.tag("configtag") assert(scope.function_tagged("configtag"), "tagged function did not catch catalog tags") end def test_failfunction func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "fail", :ftype => :statement, :arguments => AST::ASTArray.new( :children => [stringobj("this is a failure"), stringobj("and another")] ) ) end scope = mkscope val = nil assert_raise(Puppet::ParseError) do val = func.evaluate(scope) end end def test_multipletemplates Dir.mkdir(Puppet[:templatedir]) onep = File.join(Puppet[:templatedir], "one") twop = File.join(Puppet[:templatedir], "two") File.open(onep, "w") do |f| f.puts "<%- if @one.nil? then raise '@one undefined' end -%>template <%= @one %>" end File.open(twop, "w") do |f| f.puts "template <%= @two %>" end func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "template", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [stringobj("one"), stringobj("two")] ) ) end ast = varobj("output", func) scope = mkscope # Test that our manual exception throw fails the parse assert_raise(Puppet::ParseError) do ast.evaluate(scope) end # Test that our use of an undefined instance variable does not throw # an exception, but only safely continues. scope.setvar("one", "One") assert_nothing_raised do ast.evaluate(scope) end # Ensure that we got the output we expected from that evaluation. assert_equal("template One\ntemplate \n", scope.lookupvar("output"), "Undefined template variables do not raise exceptions") # Now, fill in the last variable and make sure the whole thing # evaluates correctly. scope.setvar("two", "Two") scope.unsetvar("output") assert_nothing_raised do ast.evaluate(scope) end assert_equal( "template One\ntemplate Two\n", scope.lookupvar("output"), "Templates were not handled correctly") end # Now make sure we can fully qualify files, and specify just one def test_singletemplates template = tempfile File.open(template, "w") do |f| f.puts "template <%= @yay.nil?() ? raise('yay undefined') : @yay %>" end func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "template", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [stringobj(template)] ) ) end ast = varobj("output", func) scope = mkscope assert_raise(Puppet::ParseError) do ast.evaluate(scope) end scope.setvar("yay", "this is yay") assert_nothing_raised do ast.evaluate(scope) end assert_equal( "template this is yay\n", scope.lookupvar("output"), "Templates were not handled correctly") end # Make sure that legacy template variable access works as expected. def test_legacyvariables template = tempfile File.open(template, "w") do |f| f.puts "template <%= deprecated %>" end func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "template", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [stringobj(template)] ) ) end ast = varobj("output", func) # Verify that we get an exception using old-style accessors. scope = mkscope assert_raise(Puppet::ParseError) do ast.evaluate(scope) end # Verify that we evaluate and return their value correctly. scope.setvar("deprecated", "deprecated value") assert_nothing_raised do ast.evaluate(scope) end assert_equal( "template deprecated value\n", scope.lookupvar("output"), "Deprecated template variables were not handled correctly") end # Make sure that problems with kernel method visibility still exist. def test_kernel_module_shadows_deprecated_var_lookup template = tempfile File.open(template, "w").puts("<%= binding %>") func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "template", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [stringobj(template)] ) ) end ast = varobj("output", func) # Verify that Kernel methods still shadow deprecated variable lookups. scope = mkscope assert_nothing_raised("No exception for Kernel shadowed variable names") do ast.evaluate(scope) end end def test_tempatefunction_cannot_see_scopes template = tempfile File.open(template, "w") do |f| f.puts "<%= lookupvar('myvar') %>" end func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "template", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [stringobj(template)] ) ) end ast = varobj("output", func) scope = mkscope scope.setvar("myvar", "this is yayness") assert_raise(Puppet::ParseError) do ast.evaluate(scope) end end def test_template_reparses template = tempfile File.open(template, "w") do |f| f.puts "original text" end file = tempfile Puppet[:code] = %{file { "#{file}": content => template("#{template}") }} Puppet[:environment] = "yay" node = mknode node.stubs(:environment).returns Puppet::Node::Environment.new Puppet[:environment] = "yay" catalog = Puppet::Parser::Compiler.new(node).compile version = catalog.version fileobj = catalog.vertices.find { |r| r.title == file } assert(fileobj, "File was not in catalog") assert_equal( "original text\n", fileobj["content"], "Template did not work") Puppet[:filetimeout] = -5 # Have to sleep because one second is the fs's time granularity. sleep(1) # Now modify the template File.open(template, "w") do |f| f.puts "new text" end newversion = Puppet::Parser::Compiler.new(node).compile.version assert(version != newversion, "Parse date did not change") end def test_template_defined_vars template = tempfile File.open(template, "w") do |f| f.puts "template <%= @yayness %>" end func = nil assert_nothing_raised do func = Puppet::Parser::AST::Function.new( :name => "template", :ftype => :rvalue, :arguments => AST::ASTArray.new( :children => [stringobj(template)] ) ) end ast = varobj("output", func) { "" => "", false => "false", }.each do |string, value| scope = mkscope scope.setvar("yayness", string) - assert_equal(string, scope.lookupvar("yayness", false)) + assert_equal(string, scope.lookupvar("yayness")) assert_nothing_raised("An empty string was not a valid variable value") do ast.evaluate(scope) end - - assert_equal( - "template #{value}\n", scope.lookupvar("output"), - - "#{string.inspect} did not get evaluated correctly") + assert_equal( + "template #{value}\n", scope.lookupvar("output"), + "#{string.inspect} did not get evaluated correctly") end end def test_autoloading_functions #assert_equal(false, Puppet::Parser::Functions.function(:autofunc), # "Got told autofunc already exists") dir = tempfile $LOAD_PATH << dir newpath = File.join(dir, "puppet", "parser", "functions") FileUtils.mkdir_p(newpath) File.open(File.join(newpath, "autofunc.rb"), "w") { |f| f.puts %{ Puppet::Parser::Functions.newfunction(:autofunc, :type => :rvalue) do |vals| Puppet.wanring vals.inspect end } } Puppet::Node::Environment.stubs(:current).returns nil obj = nil assert_nothing_raised { obj = Puppet::Parser::Functions.function(:autofunc) } assert(obj, "Did not autoload function") assert(Puppet::Parser::Functions.environment_module.method_defined?(:function_autofunc), "Did not set function correctly") end def test_search scope = mkscope fun = scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yay::ness") foo = scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "foo::bar") search = Puppet::Parser::Functions.function(:search) scope.function_search(["foo", "yay"]) ffun = ffoo = nil assert_nothing_raised("Search path change did not work") do ffun = scope.find_definition("ness") ffoo = scope.find_definition('bar') end assert(ffun, "Could not find definition in 'fun' namespace") assert(ffoo, "Could not find definition in 'foo' namespace") end def test_include scope = mkscope parser = mkparser include = Puppet::Parser::Functions.function(:include) assert_raise(Puppet::Error, "did not throw error on missing class") do scope.function_include("nosuchclass") end scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "myclass", {}) scope.compiler.expects(:evaluate_classes).with(%w{myclass otherclass}, scope, false).returns(%w{myclass otherclass}) assert_nothing_raised do scope.function_include(["myclass", "otherclass"]) end end def test_file parser = mkparser scope = mkscope(:parser => parser) file = Puppet::Parser::Functions.function(:file) file1 = tempfile file2 = tempfile file3 = tempfile File.open(file2, "w") { |f| f.puts "yaytest" } val = nil assert_nothing_raised("Failed to call file with one arg") do val = scope.function_file([file2]) end assert_equal("yaytest\n", val, "file() failed") assert_nothing_raised("Failed to call file with two args") do val = scope.function_file([file1, file2]) end assert_equal("yaytest\n", val, "file() failed") assert_raise(Puppet::ParseError, "did not fail when files are missing") do val = scope.function_file([file1, file3]) end end def test_generate command = tempfile sh = %x{which sh} File.open(command, "w") do |f| f.puts %{#!#{sh} if [ -n "$1" ]; then echo "yay-$1" else echo yay fi } end File.chmod(0755, command) assert_equal("yay\n", %x{#{command}}, "command did not work") assert_equal("yay-foo\n", %x{#{command} foo}, "command did not work") Puppet::Node::Environment.stubs(:current).returns nil generate = Puppet::Parser::Functions.function(:generate) scope = mkscope parser = mkparser val = nil assert_nothing_raised("Could not call generator with no args") do val = scope.function_generate([command]) end assert_equal("yay\n", val, "generator returned wrong results") assert_nothing_raised("Could not call generator with args") do val = scope.function_generate([command, "foo"]) end assert_equal("yay-foo\n", val, "generator returned wrong results") assert_raise(Puppet::ParseError, "Did not fail with an unqualified path") do val = scope.function_generate([File.basename(command), "foo"]) end assert_raise(Puppet::ParseError, "Did not fail when command failed") do val = scope.function_generate([%x{which touch}.chomp, "/this/dir/does/not/exist"]) end fake = File.join(File.dirname(command), "..") dir = File.dirname(command) dirname = File.basename(dir) bad = File.join(dir, "..", dirname, File.basename(command)) assert_raise(Puppet::ParseError, "Did not fail when command failed") do val = scope.function_generate([bad]) end end end diff --git a/test/language/parser.rb b/test/language/parser.rb index 36a9c4787..f7e636add 100755 --- a/test/language/parser.rb +++ b/test/language/parser.rb @@ -1,739 +1,738 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../lib/puppettest') require 'mocha' require 'puppet' require 'puppet/parser/parser' require 'puppettest' require 'puppettest/support/utils' class TestParser < Test::Unit::TestCase include PuppetTest include PuppetTest::ParserTesting include PuppetTest::Support::Utils def setup super - Puppet[:parseonly] = true #@lexer = Puppet::Parser::Lexer.new end def teardown super Puppet::Node::Environment.clear end def test_each_file textfiles { |file| Puppet::Node::Environment.clear parser = mkparser Puppet.debug("parsing #{file}") if __FILE__ == $0 assert_nothing_raised { parser.file = file parser.parse } } end def test_failers failers { |file| parser = mkparser Puppet.debug("parsing failer #{file}") if __FILE__ == $0 assert_raise(Puppet::ParseError, Puppet::Error, "Did not fail while parsing #{file}") { Puppet[:manifest] = file config = mkcompiler(parser) config.compile #ast.hostclass("").evaluate config.topscope } } end def test_arrayrvalues parser = mkparser ret = nil file = tempfile assert_nothing_raised { parser.string = "file { \"#{file}\": mode => [755, 640] }" } assert_nothing_raised { ret = parser.parse } end def test_arrayrvalueswithtrailingcomma parser = mkparser ret = nil file = tempfile assert_nothing_raised { parser.string = "file { \"#{file}\": mode => [755, 640,] }" } assert_nothing_raised { ret = parser.parse } end def mkmanifest(file) name = File.join(tmpdir, "file#{rand(100)}") @@tmpfiles << name File.open(file, "w") { |f| f.puts "file { \"%s\": ensure => file, mode => 755 }\n" % name } end def test_importglobbing basedir = File.join(tmpdir, "importesting") @@tmpfiles << basedir Dir.mkdir(basedir) subdir = "subdir" Dir.mkdir(File.join(basedir, subdir)) manifest = File.join(basedir, "manifest") File.open(manifest, "w") { |f| f.puts "import \"%s/*\"" % subdir } 4.times { |i| path = File.join(basedir, subdir, "subfile#{i}.pp") mkmanifest(path) } assert_nothing_raised("Could not parse multiple files") { parser = mkparser parser.file = manifest parser.parse } end def test_nonexistent_import basedir = File.join(tmpdir, "importesting") @@tmpfiles << basedir Dir.mkdir(basedir) manifest = File.join(basedir, "manifest") File.open(manifest, "w") do |f| f.puts "import \" no such file \"" end assert_raise(Puppet::ParseError) { parser = mkparser parser.file = manifest parser.parse } end def test_trailingcomma path = tempfile str = %{file { "#{path}": ensure => file, } } parser = mkparser parser.string = str assert_nothing_raised("Could not parse trailing comma") { parser.parse } end def test_importedclasses imported = tempfile '.pp' importer = tempfile '.pp' made = tempfile File.open(imported, "w") do |f| f.puts %{class foo { file { "#{made}": ensure => file }}} end File.open(importer, "w") do |f| f.puts %{import "#{imported}"\ninclude foo} end parser = mkparser parser.file = importer # Make sure it parses fine assert_nothing_raised { parser.parse } # Now make sure it actually does the work assert_creates(importer, made) end # Make sure fully qualified and unqualified files can be imported def test_fqfilesandlocalfiles dir = tempfile Dir.mkdir(dir) importer = File.join(dir, "site.pp") fullfile = File.join(dir, "full.pp") localfile = File.join(dir, "local.pp") files = [] File.open(importer, "w") do |f| f.puts %{import "#{fullfile}"\ninclude full\nimport "local.pp"\ninclude local} end fullmaker = tempfile files << fullmaker File.open(fullfile, "w") do |f| f.puts %{class full { file { "#{fullmaker}": ensure => file }}} end localmaker = tempfile files << localmaker File.open(localfile, "w") do |f| f.puts %{class local { file { "#{localmaker}": ensure => file }}} end parser = mkparser parser.file = importer # Make sure it parses assert_nothing_raised { parser.parse } # Now make sure it actually does the work assert_creates(importer, *files) end # Make sure the parser adds '.pp' when necessary def test_addingpp dir = tempfile Dir.mkdir(dir) importer = File.join(dir, "site.pp") localfile = File.join(dir, "local.pp") files = [] File.open(importer, "w") do |f| f.puts %{import "local"\ninclude local} end file = tempfile files << file File.open(localfile, "w") do |f| f.puts %{class local { file { "#{file}": ensure => file }}} end parser = mkparser parser.file = importer assert_nothing_raised { parser.parse } end # Make sure that file importing changes file relative names. def test_changingrelativenames dir = tempfile Dir.mkdir(dir) Dir.mkdir(File.join(dir, "subdir")) top = File.join(dir, "site.pp") subone = File.join(dir, "subdir/subone") subtwo = File.join(dir, "subdir/subtwo") files = [] file = tempfile files << file File.open(subone + ".pp", "w") do |f| f.puts %{class one { file { "#{file}": ensure => file }}} end otherfile = tempfile files << otherfile File.open(subtwo + ".pp", "w") do |f| f.puts %{import "subone"\n class two inherits one { file { "#{otherfile}": ensure => file } }} end File.open(top, "w") do |f| f.puts %{import "subdir/subtwo"} end parser = mkparser parser.file = top assert_nothing_raised { parser.parse } end # Defaults are purely syntactical, so it doesn't make sense to be able to # collect them. def test_uncollectabledefaults string = "@Port { protocols => tcp }" assert_raise(Puppet::ParseError) { mkparser.parse(string) } end # Verify that we can parse collections def test_collecting text = "Port <| |>" parser = mkparser parser.string = text ret = nil assert_nothing_raised { ret = parser.parse } ret.code.each do |obj| assert_instance_of(AST::Collection, obj) end end def test_emptyfile file = tempfile File.open(file, "w") do |f| f.puts %{} end parser = mkparser parser.file = file assert_nothing_raised { parser.parse } end def test_multiple_nodes_named file = tempfile other = tempfile File.open(file, "w") do |f| f.puts %{ node nodeA, nodeB { file { "#{other}": ensure => file } } } end parser = mkparser parser.file = file ast = nil assert_nothing_raised { ast = parser.parse } end def test_emptyarrays str = %{$var = []\n} parser = mkparser parser.string = str # Make sure it parses fine assert_nothing_raised { parser.parse } end # Make sure function names aren't reserved words. def test_functionnamecollision str = %{tag yayness tag(rahness) file { "/tmp/yayness": tag => "rahness", ensure => exists } } parser = mkparser parser.string = str # Make sure it parses fine assert_nothing_raised { parser.parse } end def test_metaparams_in_definition_prototypes parser = mkparser assert_raise(Puppet::ParseError) { parser.known_resource_types.import_ast(parser.parse(%{define mydef($schedule) {}}), '') } assert_nothing_raised { parser.known_resource_types.import_ast(parser.parse(%{define adef($schedule = false) {}}), '') parser.known_resource_types.import_ast(parser.parse(%{define mydef($schedule = daily) {}}), '') } end def test_parsingif parser = mkparser exec = proc do |val| %{exec { "/bin/echo #{val}": logoutput => true }} end str1 = %{if true { #{exec.call("true")} }} ret = nil assert_nothing_raised { ret = parser.parse(str1).code[0] } assert_instance_of(Puppet::Parser::AST::IfStatement, ret) parser = mkparser str2 = %{if true { #{exec.call("true")} } else { #{exec.call("false")} }} ret = parser.parse(str2).code[0] assert_instance_of(Puppet::Parser::AST::IfStatement, ret) assert_instance_of(Puppet::Parser::AST::Else, ret.else) end def test_hostclass parser = mkparser assert_nothing_raised { parser.known_resource_types.import_ast(parser.parse(%{class myclass { class other {} }}), '') } assert(parser.hostclass("myclass"), "Could not find myclass") assert(parser.hostclass("myclass::other"), "Could not find myclass::other") assert_nothing_raised { parser.known_resource_types.import_ast(parser.parse("class base {} class container { class deep::sub inherits base {} }"), '') } sub = parser.hostclass("container::deep::sub") assert(sub, "Could not find sub") # Now try it with a parent class being a fq class assert_nothing_raised { parser.known_resource_types.import_ast(parser.parse("class container::one inherits container::deep::sub {}"), '') } sub = parser.hostclass("container::one") assert(sub, "Could not find one") assert_equal("container::deep::sub", sub.parent) # Finally, try including a qualified class assert_nothing_raised("Could not include fully qualified class") { parser.known_resource_types.import_ast(parser.parse("include container::deep::sub"), '') } end def test_topnamespace parser = mkparser # Make sure we put the top-level code into a class called "" in # the "" namespace parser.initvars assert_nothing_raised do parser.known_resource_types.import_ast(parser.parse("Exec { path => '/usr/bin:/usr/sbin' }"), '') assert_equal("", parser.known_resource_types.hostclass("").name) assert_equal("", parser.known_resource_types.hostclass("").namespace) end end # Make sure virtual and exported resources work appropriately. def test_virtualresources tests = [:virtual] if Puppet.features.rails? catalog_cache_class = Puppet::Resource::Catalog.indirection.cache_class facts_cache_class = Puppet::Node::Facts.indirection.cache_class node_cache_class = Puppet::Node.indirection.cache_class Puppet[:storeconfigs] = true tests << :exported end tests.each do |form| parser = mkparser if form == :virtual at = "@" else at = "@@" end check = proc do |res, msg| if res.is_a?(Puppet::Parser::Resource) txt = res.ref else txt = res.class end # Real resources get marked virtual when exported if form == :virtual or res.is_a?(Puppet::Parser::Resource) assert(res.virtual, "#{msg} #{at}#{txt} is not virtual") end if form == :virtual assert(! res.exported, "#{msg} #{at}#{txt} is exported") else assert(res.exported, "#{msg} #{at}#{txt} is not exported") end end ret = nil assert_nothing_raised do parser.known_resource_types.import_ast(parser.parse("#{at}file { '/tmp/testing': owner => root }"), '') ret = parser.known_resource_types end assert_instance_of(AST::ASTArray, ret.hostclass("").code) resdef = ret.hostclass("").code[0] assert_instance_of(AST::Resource, resdef) assert_instance_of(AST::ASTArray, resdef.instances) assert_equal(1, resdef.instances.children.length) assert_equal("/tmp/testing", resdef.instances[0].title.value) # We always get an astarray back, so... check.call(resdef, "simple resource") # Now let's try it with multiple resources in the same spec assert_nothing_raised do parser.known_resource_types.import_ast(parser.parse("#{at}file { ['/tmp/1', '/tmp/2']: owner => root }"), '') ret = parser.known_resource_types end ret.hostclass("").code[0].each do |res| assert_instance_of(AST::Resource, res) check.call(res, "multiresource") end end ensure if Puppet.features.rails? Puppet[:storeconfigs] = false Puppet::Resource::Catalog.indirection.cache_class = catalog_cache_class Puppet::Node::Facts.indirection.cache_class = facts_cache_class Puppet::Node.indirection.cache_class = node_cache_class end end def test_collections tests = [:virtual] if Puppet.features.rails? catalog_cache_class = Puppet::Resource::Catalog.indirection.cache_class facts_cache_class = Puppet::Node::Facts.indirection.cache_class node_cache_class = Puppet::Node.indirection.cache_class Puppet[:storeconfigs] = true tests << :exported end tests.each do |form| Puppet::Node::Environment.clear parser = mkparser if form == :virtual arrow = "<||>" else arrow = "<<||>>" end ret = nil assert_nothing_raised do ret = parser.parse("File #{arrow}") end coll = ret.code[0] assert_instance_of(AST::Collection, coll) assert_equal(form, coll.form) end ensure if Puppet.features.rails? Puppet[:storeconfigs] = false Puppet::Resource::Catalog.indirection.cache_class = catalog_cache_class Puppet::Node::Facts.indirection.cache_class = facts_cache_class Puppet::Node.indirection.cache_class = node_cache_class end end def test_collectionexpressions %w{== !=}.each do |oper| Puppet::Node::Environment.clear str = "File <| title #{oper} '/tmp/testing' |>" parser = mkparser res = nil assert_nothing_raised do res = parser.parse(str).code[0] end assert_instance_of(AST::Collection, res) query = res.query assert_instance_of(AST::CollExpr, query) assert_equal(:virtual, query.form) assert_equal("title", query.test1.value) assert_equal("/tmp/testing", query.test2.value) assert_equal(oper, query.oper) end end def test_collectionstatements %w{and or}.each do |joiner| str = "File <| title == '/tmp/testing' #{joiner} owner == root |>" parser = mkparser res = nil assert_nothing_raised do res = parser.parse(str).code[0] end assert_instance_of(AST::Collection, res) query = res.query assert_instance_of(AST::CollExpr, query) assert_equal(joiner, query.oper) assert_instance_of(AST::CollExpr, query.test1) assert_instance_of(AST::CollExpr, query.test2) end end def test_collectionstatements_with_parens [ "(title == '/tmp/testing' and owner == root) or owner == wheel", "(title == '/tmp/testing')" ].each do |test| str = "File <| #{test} |>" parser = mkparser res = nil assert_nothing_raised("Could not parse '#{test}'") do res = parser.parse(str).code[0] end assert_instance_of(AST::Collection, res) query = res.query assert_instance_of(AST::CollExpr, query) #assert_equal(joiner, query.oper) #assert_instance_of(AST::CollExpr, query.test1) #assert_instance_of(AST::CollExpr, query.test2) end end def test_fully_qualified_definitions parser = mkparser types = parser.known_resource_types assert_nothing_raised("Could not parse fully-qualified definition") { types.import_ast(parser.parse(%{define one::two { }}), '') } assert(parser.definition("one::two"), "Could not find one::two with no namespace") end # #524 def test_functions_with_no_arguments parser = mkparser assert_nothing_raised("Could not parse statement function with no args") { parser.parse %{tag()} } assert_nothing_raised("Could not parse rvalue function with no args") { parser.parse %{$testing = template()} } end # #774 def test_fully_qualified_collection_statement parser = mkparser assert_nothing_raised("Could not parse fully qualified collection statement") { parser.parse %{Foo::Bar <||>} } end def test_multiple_imports_on_one_line one = tempfile '.pp' two = tempfile '.pp' base = tempfile '.pp' File.open(one, "w") { |f| f.puts "$var = value" } File.open(two, "w") { |f| f.puts "$var = value" } File.open(base, "w") { |f| f.puts "import '#{one}', '#{two}'" } parser = mkparser parser.file = base # Importing is logged at debug time. Puppet::Util::Log.level = :debug assert_nothing_raised("Parser could not import multiple files at once") do parser.parse end [one, two].each do |file| assert(@logs.detect { |l| l.message =~ /importing '#{file}'/}, "did not import #{file}") end end def test_cannot_assign_qualified_variables parser = mkparser assert_raise(Puppet::ParseError, "successfully assigned a qualified variable") do parser.parse("$one::two = yay") end end # #629 - undef keyword def test_undef parser = mkparser result = nil assert_nothing_raised("Could not parse assignment to undef") { result = parser.parse %{$variable = undef} } main = result.code children = main.children assert_instance_of(AST::VarDef, main.children[0]) assert_instance_of(AST::Undef, main.children[0].value) end # Prompted by #729 -- parsing should not modify the interpreter. def test_parse parser = mkparser str = "file { '/tmp/yay': ensure => file }\nclass yay {}\nnode foo {}\ndefine bar {}\n" result = nil assert_nothing_raised("Could not parse") do parser.known_resource_types.import_ast(parser.parse(str), '') result = parser.known_resource_types end assert_instance_of(Puppet::Resource::TypeCollection, result, "Did not get a ASTSet back from parsing") assert_instance_of(Puppet::Resource::Type, result.hostclass("yay"), "Did not create 'yay' class") assert_instance_of(Puppet::Resource::Type, result.hostclass(""), "Did not create main class") assert_instance_of(Puppet::Resource::Type, result.definition("bar"), "Did not create 'bar' definition") assert_instance_of(Puppet::Resource::Type, result.node("foo"), "Did not create 'foo' node") end def test_namesplit parser = mkparser assert_nothing_raised do {"base::sub" => %w{base sub}, "main" => ["", "main"], "one::two::three::four" => ["one::two::three", "four"], }.each do |name, ary| result = parser.namesplit(name) assert_equal(ary, result, "#{name} split to #{result}") end end end # Make sure class, node, and define methods are case-insensitive def test_structure_case_insensitivity parser = mkparser result = nil assert_nothing_raised do parser.known_resource_types.import_ast(parser.parse("class yayness { }"), '') result = parser.known_resource_types.hostclass('yayness') end assert_equal(result, parser.find_hostclass("", "yayNess")) assert_nothing_raised do parser.known_resource_types.import_ast(parser.parse("define funtest { }"), '') result = parser.known_resource_types.definition('funtest') end assert_equal(result, parser.find_definition("", "fUntEst"), "#{"fUntEst"} was not matched") end end diff --git a/test/language/scope.rb b/test/language/scope.rb index c4154dc27..ccc359651 100755 --- a/test/language/scope.rb +++ b/test/language/scope.rb @@ -1,277 +1,266 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../lib/puppettest') require 'mocha' require 'puppettest' require 'puppettest/parsertesting' require 'puppettest/resourcetesting' # so, what kind of things do we want to test? # we don't need to test function, since we're confident in the # library tests. We do, however, need to test how things are actually # working in the language. # so really, we want to do things like test that our ast is correct # and test whether we've got things in the right scopes class TestScope < Test::Unit::TestCase include PuppetTest::ParserTesting include PuppetTest::ResourceTesting def setup Puppet::Node::Environment.clear super end def to_ary(hash) hash.collect { |key,value| [key,value] } end def test_variables config = mkcompiler topscope = config.topscope midscope = config.newscope(topscope) botscope = config.newscope(midscope) scopes = {:top => topscope, :mid => midscope, :bot => botscope} # Set a variable in the top and make sure all three can get it topscope.setvar("first", "topval") scopes.each do |name, scope| - assert_equal("topval", scope.lookupvar("first", false), "Could not find var in #{name}") + assert_equal("topval", scope.lookupvar("first"), "Could not find var in #{name}") end # Now set a var in the midscope and make sure the mid and bottom can see it but not the top midscope.setvar("second", "midval") - assert_equal(:undefined, scopes[:top].lookupvar("second", false), "Found child var in top scope") + assert_equal(:undefined, scopes[:top].lookupvar("second"), "Found child var in top scope") [:mid, :bot].each do |name| - assert_equal("midval", scopes[name].lookupvar("second", false), "Could not find var in #{name}") + assert_equal("midval", scopes[name].lookupvar("second"), "Could not find var in #{name}") end # And set something in the bottom, and make sure we only find it there. botscope.setvar("third", "botval") [:top, :mid].each do |name| - assert_equal(:undefined, scopes[name].lookupvar("third", false), "Found child var in top scope") + assert_equal(:undefined, scopes[name].lookupvar("third"), "Found child var in top scope") end - assert_equal("botval", scopes[:bot].lookupvar("third", false), "Could not find var in bottom scope") + assert_equal("botval", scopes[:bot].lookupvar("third"), "Could not find var in bottom scope") # Test that the scopes convert to hash structures correctly. # For topscope recursive vs non-recursive should be identical assert_equal(topscope.to_hash(false), topscope.to_hash(true), "Recursive and non-recursive hash is identical for topscope") # Check the variable we expect is present. assert_equal({"first" => "topval"}, topscope.to_hash, "topscope returns the expected hash of variables") # Now, check that midscope does the right thing in all cases. assert_equal( {"second" => "midval"}, midscope.to_hash(false), "midscope non-recursive hash contains only midscope variable") assert_equal( {"first" => "topval", "second" => "midval"}, midscope.to_hash(true), "midscope recursive hash contains topscope variable also") # Finally, check the ability to shadow symbols by adding a shadow to # bottomscope, then checking that we see the right stuff. botscope.setvar("first", "shadowval") assert_equal( {"third" => "botval", "first" => "shadowval"}, botscope.to_hash(false), "botscope has the right non-recursive hash") assert_equal( {"third" => "botval", "first" => "shadowval", "second" => "midval"}, botscope.to_hash(true), "botscope values shadow parent scope values") end def test_declarative # set to declarative top = mkscope sub = mkscope(:parent => top) assert_nothing_raised { top.setvar("test","value") } assert_raise(Puppet::ParseError) { top.setvar("test","other") } assert_nothing_raised { sub.setvar("test","later") } assert_raise(Puppet::ParseError) { top.setvar("test","yeehaw") } end def test_parent config = mkcompiler top = config.topscope # Make a subscope sub = config.newscope(top) assert_equal(top, sub.parent, "Did not find parent scope correctly") assert_equal(top, sub.parent, "Did not find parent scope on second call") end # Make sure we know what we consider to be truth. def test_truth assert_equal( true, Puppet::Parser::Scope.true?("a string"), "Strings not considered true") assert_equal( true, Puppet::Parser::Scope.true?(true), "True considered true") assert_equal( false, Puppet::Parser::Scope.true?(""), "Empty strings considered true") assert_equal( false, Puppet::Parser::Scope.true?(false), "false considered true") assert_equal( false, Puppet::Parser::Scope.true?(:undef), "undef considered true") end def test_virtual_definitions_do_not_get_evaluated parser = mkparser config = mkcompiler # Create a default source parser.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") config.topscope.source = parser.known_resource_types.hostclass("") # And a scope resource scope_res = Puppet::Parser::Resource.new(:file, "/file", :scope => config.topscope) config.topscope.resource = scope_res args = AST::ASTArray.new( :children => [nameobj("arg")] ) # Create a top-level define parser.known_resource_types.add Puppet::Resource::Type.new(:definition, "one", :arguments => [%w{arg}], :code => AST::ASTArray.new( :children => [ resourcedef("file", "/tmp", {"owner" => varref("arg")}) ] )) # create a resource that calls our third define obj = resourcedef("one", "boo", {"arg" => "parentfoo"}) # And mark it as virtual obj.virtual = true # And then evaluate it obj.evaluate config.topscope # And run the loop. config.send(:evaluate_generators) %w{File}.each do |type| objects = config.resources.find_all { |r| r.type == type and r.virtual } assert(objects.empty?, "Virtual define got evaluated") end end if defined? ::ActiveRecord # Verify that we can both store and collect an object in the same # run, whether it's in the same scope as a collection or a different # scope. def test_storeandcollect catalog_cache_class = Puppet::Resource::Catalog.indirection.cache_class facts_cache_class = Puppet::Node::Facts.indirection.cache_class node_cache_class = Puppet::Node.indirection.cache_class Puppet[:storeconfigs] = true Puppet::Rails.init sleep 1 children = [] Puppet[:code] = " class yay { @@host { myhost: ip => \"192.168.0.2\" } } include yay @@host { puppet: ip => \"192.168.0.3\" } Host <<||>>" config = nil # We run it twice because we want to make sure there's no conflict # if we pull it up from the database. node = mknode node.merge "hostname" => node.name 2.times { |i| catalog = Puppet::Parser::Compiler.new(node).compile assert_instance_of(Puppet::Parser::Resource, catalog.resource(:host, "puppet")) assert_instance_of(Puppet::Parser::Resource, catalog.resource(:host, "myhost")) } ensure Puppet[:storeconfigs] = false Puppet::Resource::Catalog.indirection.cache_class = catalog_cache_class Puppet::Node::Facts.indirection.cache_class = facts_cache_class Puppet::Node.indirection.cache_class = node_cache_class end else $stderr.puts "No ActiveRecord -- skipping collection tests" end def test_namespaces scope = mkscope assert_equal( [""], scope.namespaces, "Started out with incorrect namespaces") assert_nothing_raised { scope.add_namespace("fun::test") } assert_equal(["fun::test"], scope.namespaces, "Did not add namespace correctly") assert_nothing_raised { scope.add_namespace("yay::test") } assert_equal(["fun::test", "yay::test"], scope.namespaces, "Did not add extra namespace correctly") end # #629 - undef should be "" or :undef def test_lookupvar_with_undef scope = mkscope scope.setvar("testing", :undef) - - - assert_equal( - :undef, scope.lookupvar("testing", false), - - "undef was not returned as :undef when not string") - - - assert_equal( - "", scope.lookupvar("testing", true), - - "undef was not returned as '' when string") + assert_equal(:undef, scope.lookupvar("testing"), "undef was not returned as :undef") end end diff --git a/test/test b/test/test index c8b9b08ef..8d2659cb7 100755 --- a/test/test +++ b/test/test @@ -1,241 +1,241 @@ #!/usr/bin/env ruby # # = Synopsis # # Run unit tests, usually with the goal of resolving some conflict # between tests. # # = Usage # # test [-d|--debug] [-f|--files] [-h|--help] [-n method] [-v|--verbose] [file] ... # # = Description # # This script is useful for running a specific subset of unit tests, especially # when attempting to find a conflict between tests. By default, it will take # the first argument you pass it and run that test or test directory with each # test directory in turn, looking for a failure. If any tests fail, then # the script will drill into that test directory, looking for the specific conflict. # # In this way, when you have deduced you have two conflicting unit tests (tests which # pass in isolation but fail when run together), you can tell this script where # the failure is and leave it alone to find the conflict. # # This script is different from the Rakefile because it will run all tests in # a single process, whereas if you ask the Rakefile to run multiple tests, it will # run them in separate processes thus making it impossible to find conflicts. # # = Examples # # Attempt to resolve a conflict between a single test suite that could be anywhere: # # ./test ral/providers/user.rb # # Determine whether two individual files conflict: # # ./test --files language/functions.rb ral/providers/provider.rb # # Run the same test, but only run a specific unit test: # # ./test -d -n test_search --files language/functions.rb ral/providers/provider.rb # # = Options # # debug:: # Enable full debugging. # # files:: # Specify exactly which files to test. # # help:: # Print this help message # # n:: # Specify a single unit test to run. You can still specify as many files # as you want. # # verbose:: # Print extra information. # # = Example # # puppet -l /tmp/script.log script.pp # # = Author # # Luke Kanies # # = Copyright # -# Copyright (c) 2005 Puppet Labs, LLC -# Licensed under the GNU Public License +# Copyright (c) 2005-2011 Puppet Labs, LLC +# Licensed under the Apache 2.0 License require 'find' require 'getoptlong' include Find result = GetoptLong.new( [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "-n", GetoptLong::REQUIRED_ARGUMENT ], [ "--files", "-f", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ] ) usage = "USAGE: %s [--help] suite" % $0 $options = {} keep = [] result.each { |opt,arg| case opt when "--verbose" $options[:verbose] = true when "--files" $options[:files] = true when "--debug" $options[:debug] = true $options[:verbose] = true when "--help" puts usage exit else keep << opt keep << arg if arg end } def dirs Dir.glob("*").find_all { |d| FileTest.directory?(d) }.reject { |d| ["lib", "data"].include?(d) } end def rake(*args) print "trying %s..." % args.join(" ") output = %x{rake %s} % args.join(" ") if $?.exitstatus == 0 puts "succeeded" return true else puts "failed" return false end end def resolve(dir) dirs = dirs() # If the passed dir is a subdir or file, put the basedir last if dir.include?(File::SEPARATOR) basedir = dir.split(File::SEPARATOR)[0] if dirs.include?(basedir) dirs.delete(basedir) dirs << basedir end end failed = nil dirs.each do |d| next if d == dir unless run([d, dir]) failed = d break end end puts "%s failed" % failed files = ruby_files(failed) files.each do |file| unless run([file, dir]) puts file exit(0) end end exit(1) end def ruby_files(dir) files = [] # First collect the entire file list. begin find(dir) { |f| files << f if f =~ /\.rb$/ } rescue => detail puts "could not find on %s: %s" % [dir.inspect, detail] end files end def run(files, flags = nil) args = %w{ruby} args << "-Ilib:../lib" args << "lib/rake/puppet_test_loader.rb" if flags args += flags end args += ARGV print files.join(" ") + "... " $stdout.flush files.each do |file| case File.stat(file).ftype when "file"; args << file when "directory"; args += ruby_files(file) else $stderr.puts "Skipping %s; can't handle %s" % [file, File.stat(file).ftype] end end args = args.join(" ") if $options[:verbose] p args end output = %x{#{args} 2>&1} if $options[:debug] print output end if $?.exitstatus == 0 puts "succeeded" return true else puts "failed" puts output return false end end if $options[:files] run(ARGV, keep) else dir = ARGV.shift unless dir $stderr.puts usage exit(1) end resolve(dir) end # # #files = [] # #args.each do |test| # if FileTest.directory?(test) # files += ruby_files(test) # end #end ## Now load all of our files. #files.each do |file| # load file unless file =~ /^-/ #end # #runner = Test::Unit::AutoRunner.new(false) #runner.process_args #runner.run