summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Pittman <daniel@puppetlabs.com>2011-05-06 11:08:35 -0700
committerDaniel Pittman <daniel@puppetlabs.com>2011-05-06 11:08:35 -0700
commitf80afbe72b848fe4ed81d8116d4eeb494aa6f61e (patch)
tree73c7f27f2ea2fb1668f7067ce05638f5064f540d
parent1b12b55b6a2d3581f9643bf09d55727ba1213580 (diff)
parentb983386ece1b9816e6d3d59a316ad589e35773df (diff)
downloadpuppet-f80afbe72b848fe4ed81d8116d4eeb494aa6f61e.tar.gz
puppet-f80afbe72b848fe4ed81d8116d4eeb494aa6f61e.tar.xz
puppet-f80afbe72b848fe4ed81d8116d4eeb494aa6f61e.zip
Merge branch '2.7.x' into 2.7.next
Conflicts: * spec/unit/node/facts_spec.rb Updates: * spec/unit/interface/action{,_builder}_spec.rb => update for 'when_invoked' block being required.
-rw-r--r--CHANGELOG163
-rw-r--r--acceptance/tests/ticket_5477_master_not_dectect_sitepp.rb3
-rw-r--r--acceptance/tests/ticket_6928_puppet_master_parse_fails.rb11
-rw-r--r--acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb33
-rw-r--r--acceptance/tests/ticket_7139_puppet_resource_file_qualified_paths.rm11
-rw-r--r--conf/auth.conf5
-rw-r--r--lib/puppet/application/face_base.rb130
-rw-r--r--lib/puppet/application/faces.rb50
-rw-r--r--lib/puppet/application/help.rb1
-rw-r--r--lib/puppet/application/plugin.rb3
-rw-r--r--lib/puppet/application/secret_agent.rb (renamed from lib/puppet/application/configurer.rb)4
-rw-r--r--lib/puppet/face/catalog.rb86
-rw-r--r--lib/puppet/face/catalog/select.rb34
-rw-r--r--lib/puppet/face/certificate.rb46
-rw-r--r--lib/puppet/face/certificate_request.rb32
-rw-r--r--lib/puppet/face/certificate_revocation_list.rb30
-rw-r--r--lib/puppet/face/config.rb36
-rw-r--r--lib/puppet/face/configurer.rb12
-rw-r--r--lib/puppet/face/facts.rb37
-rw-r--r--lib/puppet/face/file.rb20
-rw-r--r--lib/puppet/face/file/download.rb36
-rw-r--r--lib/puppet/face/file/store.rb12
-rw-r--r--lib/puppet/face/help.rb38
-rw-r--r--lib/puppet/face/help/action.erb49
-rw-r--r--lib/puppet/face/help/face.erb47
-rw-r--r--lib/puppet/face/help/global.erb3
-rw-r--r--lib/puppet/face/key.rb23
-rw-r--r--lib/puppet/face/node.rb27
-rw-r--r--lib/puppet/face/parser.rb41
-rw-r--r--lib/puppet/face/plugin.rb47
-rw-r--r--lib/puppet/face/report.rb40
-rw-r--r--lib/puppet/face/resource.rb23
-rw-r--r--lib/puppet/face/resource_type.rb17
-rw-r--r--lib/puppet/face/secret_agent.rb39
-rw-r--r--lib/puppet/face/status.rb30
-rw-r--r--lib/puppet/file_serving/fileset.rb2
-rw-r--r--lib/puppet/indirector/face.rb (renamed from lib/puppet/face/indirector.rb)51
-rw-r--r--lib/puppet/indirector/request.rb51
-rw-r--r--lib/puppet/interface.rb60
-rw-r--r--lib/puppet/interface/action.rb101
-rw-r--r--lib/puppet/interface/action_builder.rb31
-rw-r--r--lib/puppet/interface/action_manager.rb4
-rw-r--r--lib/puppet/interface/documentation.rb197
-rw-r--r--lib/puppet/interface/face_collection.rb22
-rw-r--r--lib/puppet/interface/option.rb8
-rw-r--r--lib/puppet/interface/option_manager.rb29
-rw-r--r--lib/puppet/network/formats.rb36
-rw-r--r--lib/puppet/network/http/api/v1.rb2
-rw-r--r--lib/puppet/network/rest_authconfig.rb1
-rw-r--r--lib/puppet/node.rb23
-rwxr-xr-xlib/puppet/node/facts.rb20
-rw-r--r--lib/puppet/parser/templatewrapper.rb2
-rw-r--r--lib/puppet/resource/catalog.rb14
-rw-r--r--lib/puppet/status.rb2
-rw-r--r--lib/puppet/transaction/event_manager.rb17
-rwxr-xr-xlib/puppet/type/user.rb2
-rw-r--r--lib/puppet/type/whit.rb8
-rw-r--r--lib/puppet/util/command_line.rb9
-rw-r--r--spec/fixtures/faulty_face/puppet/face/syntax.rb8
-rwxr-xr-xspec/integration/application/doc_spec.rb4
-rwxr-xr-xspec/integration/faces/documentation_spec.rb55
-rwxr-xr-xspec/integration/transaction_spec.rb4
-rwxr-xr-xspec/lib/puppet/face/basetest.rb41
-rwxr-xr-xspec/lib/puppet/face/huzzah.rb4
-rw-r--r--spec/lib/puppet/face/version_matching.rb2
-rw-r--r--spec/lib/puppet_spec/matchers.rb87
-rw-r--r--spec/shared_behaviours/an_indirector_face.rb6
-rw-r--r--spec/shared_behaviours/documentation_on_faces.rb261
-rwxr-xr-xspec/shared_behaviours/things_that_declare_options.rb9
-rwxr-xr-xspec/spec_helper.rb13
-rwxr-xr-xspec/unit/application/agent_spec.rb44
-rwxr-xr-xspec/unit/application/apply_spec.rb54
-rwxr-xr-xspec/unit/application/cert_spec.rb10
-rwxr-xr-xspec/unit/application/device_spec.rb3
-rwxr-xr-xspec/unit/application/doc_spec.rb16
-rwxr-xr-xspec/unit/application/face_base_spec.rb169
-rwxr-xr-xspec/unit/application/facts_spec.rb27
-rwxr-xr-xspec/unit/application/filebucket_spec.rb10
-rwxr-xr-xspec/unit/application/indirection_base_spec.rb10
-rwxr-xr-xspec/unit/application/inspect_spec.rb2
-rwxr-xr-xspec/unit/application/kick_spec.rb30
-rwxr-xr-xspec/unit/application/master_spec.rb23
-rwxr-xr-xspec/unit/application/queue_spec.rb10
-rwxr-xr-xspec/unit/application/resource_spec.rb4
-rwxr-xr-xspec/unit/application/secret_agent_spec.rb (renamed from spec/unit/application/configurer_spec.rb)6
-rwxr-xr-xspec/unit/application_spec.rb20
-rwxr-xr-xspec/unit/daemon_spec.rb16
-rwxr-xr-xspec/unit/face/catalog_spec.rb3
-rwxr-xr-xspec/unit/face/certificate_request_spec.rb4
-rwxr-xr-xspec/unit/face/certificate_revocation_list_spec.rb4
-rwxr-xr-xspec/unit/face/certificate_spec.rb4
-rwxr-xr-xspec/unit/face/config_spec.rb1
-rwxr-xr-xspec/unit/face/facts_spec.rb11
-rwxr-xr-xspec/unit/face/file_spec.rb11
-rwxr-xr-xspec/unit/face/help_spec.rb15
-rwxr-xr-xspec/unit/face/key_spec.rb4
-rwxr-xr-xspec/unit/face/node_spec.rb1
-rwxr-xr-xspec/unit/face/plugin_spec.rb10
-rwxr-xr-xspec/unit/face/report_spec.rb4
-rwxr-xr-xspec/unit/face/resource_spec.rb4
-rwxr-xr-xspec/unit/face/resource_type_spec.rb4
-rwxr-xr-xspec/unit/face/secret_agent_spec.rb (renamed from spec/unit/face/configurer_spec.rb)8
-rwxr-xr-xspec/unit/file_serving/fileset_spec.rb7
-rwxr-xr-xspec/unit/indirector/face_spec.rb (renamed from spec/unit/face/indirector_spec.rb)24
-rwxr-xr-xspec/unit/indirector/request_spec.rb95
-rwxr-xr-xspec/unit/interface/action_builder_spec.rb52
-rwxr-xr-xspec/unit/interface/action_manager_spec.rb16
-rwxr-xr-xspec/unit/interface/action_spec.rb340
-rwxr-xr-xspec/unit/interface/face_collection_spec.rb24
-rwxr-xr-xspec/unit/interface/option_builder_spec.rb14
-rwxr-xr-xspec/unit/interface_spec.rb20
-rwxr-xr-xspec/unit/network/formats_spec.rb62
-rwxr-xr-xspec/unit/network/http/api/v1_spec.rb8
-rwxr-xr-xspec/unit/network/rest_authconfig_spec.rb13
-rwxr-xr-xspec/unit/node/facts_spec.rb33
-rwxr-xr-xspec/unit/node_spec.rb63
-rwxr-xr-xspec/unit/parser/compiler_spec.rb7
-rwxr-xr-xspec/unit/simple_graph_spec.rb18
-rwxr-xr-xspec/unit/ssl/host_spec.rb6
-rwxr-xr-xspec/unit/status_spec.rb4
-rwxr-xr-xspec/unit/type/whit_spec.rb2
-rwxr-xr-xspec/unit/util/command_line_spec.rb10
122 files changed, 2737 insertions, 1028 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 1f217a22f..68e54b439 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,166 @@
+2.7.0rc2
+===
+61edff9 (#7353) Remove :for_humans format entirely.
+d2b5ec6 Adding test for ticket 7139
+6f2a129 add clean-up step to test for ticket_5477 to prevent site.pp from leaking to other tests
+92905ff fixed test for ticket_7117
+5076c37 (#7179) Modify default ACL for /node/<name>.
+7b71745 (Maint) Adjust documentation whitespace
+358418c (#7303) Remove reference to not-yet-extant man action
+dce072a (#6962) Add self-documentation data to puppet faces
+855a0ec Maint: adjust faces.rb's help to match that of other applications
+75f164a (#7304) Remove full puppet help output when subcommand cannot be found
+8a8e198 adding test for ticket 7117
+dda3264 (#7353) Note the :for_humans compatibility issue.
+efd1181 (#7353) Use :console rendering format in our own code.
+5986e8a (#7353) Unify rendering in the face_bace application.
+a4e735e (#7353) Add 'console' format to FormatHandler
+94f0b09 add base test for ticket 7117
+dc0088f (#7277)Fixing all secret-agent functions, and the agent itself
+1f112cd (#7139) Accept '/' as a valid path in filesets
+18b3584 (#7329) Consistent naming for rendering formats and hooks.
+8f81f56 (#7326) Fix faces on Ruby 1.8.5
+5569fad (#7117) Return the environment as a Puppet::Node::Environment in uri2indirection
+5120a95 (#7276) Better reporting from the plugin download action.
+bb889bf (#7276) Create a plugin face application.
+5490f7a (#6962) Added 'returns' block to action documentation.
+0d6ac04 maint: remove emacs 'coding' cookie from files.
+48903f5 (#7278) Improve utility of the Catalog select action
+45adc1a (#7279) Adding some basic file actions
+a4a274b (#7315) Fix `to_pson` method to render correctly.
+1b42725 (#7314) Faces fail horribly when one has a syntax error.
+86c6ec2 maint: move the indirector face base out of puppet/face
+c63e9c2 maint: reset more global state in testing faces...
+b20e977 (#7304) Improve help from `puppet foo`
+3bb8bd3 (#7317) better error handling in CLI face facade.
+b23cc8a (#7282) action without `when_invoked` should fail...
+040e0fd (Maint.) Fix accidental debug output in tests.
+65b9a3c (#7221) Strip bad whitespace from face and action docs.
+97ae812 (#7248) Fail if two aliases of one option are passed...
+207deae (#7289) Specify order for option decorations.
+1707f27 Revert "maint: better error reporting for argument count mismatch."
+cd474b0 maint: better error reporting for argument count mismatch.
+69e4b1c (#7122) Enforce call arity on actions in the CLI wrapper.
+351b6fc maint: add a 'print' matcher to rspec, to inspect std{out,err}
+be4d7e6 (#7269) Fix error reporting for bad render formats...
+632a0a0 (#7269) Better error reporting for bad render formats.
+80adaea (#7160) Support 'json' as a rendering method for CLI faces.
+0256d67 (#6962) Add integration tests on Face documentation.
+e8eb290 (#6962) Finish documentation API on Face options.
+6e152ad (#6962) Give copyright and license for all faces.
+b8525c9 (#6962) Fill out documentation on Faces and Actions
+59e7ef1 (#6962) Move documentation support into a module.
+092ab09 (#6962) Extend documentation API for Faces.
+c627fad (#7251) Let exceptions raised in decorators rise.
+bbf777f (#7249) Publicize ActionBuilder DSL methods.
+95ed9aa add test for ticket 7101
+6064e8e (#7101) Fix template error messages in Ruby 1.8.5
+49c5152 (#7137) Get rid of spurious info messages in useradd
+349bd96 Fix test ticket_6928_puppet_master_parse_fails
+f25acf9 maint: add the 'to', 'not_to', and 'to_not' aliases to rspec...
+f77304b (#7157) Return a non-zero exit code on face failure.
+435c826 maint: use the exit_with helper everywhere...
+96195c1 maint: add an "exit was called" matcher for rspec.
+822d530 maint: clean up test headers on face spec files.
+5c24541 Fix #7084 Make the log messages produced by whits less confusing
+c7a0270 (#7121) Download plugins and upload reports in secret agent!
+2a2226c Revert "Fixing Facts pson methods more resilient"
+7591de7 maint: fix a race in catalog compilation versioning.
+aaf7e23 Revert "(7080) Adding json support to Indirector Request"
+17d176b Revert "Adding json support to Puppet::Node"
+27e0831 (#7181) Rename configurer face to secret_agent.
+de2199f (#6928) Don't blow up when the method is undefined...
+a0de328 (#6928) backport Symbol#to_proc for Ruby < 1.8.7
+f17f6bb (#7183) Implement "invisible glob" version matching for faces
+7414ee7 maint: better disabling of Signal#trap in our tests.
+03bd559 maint: more robust listing of valid faces.
+7db4793 maint: clean up testing code a fraction...
+eeb8236 maint: better error report for a missing version of a face.
+677752d maint: handle face clear/reset sanely in the interface spec.
+7b3744c maint: stop stubbing log level setting.
+220f308 Move tests from Puppet-acceptance repo
+379b46d (#7116) Handle application-level options in parse_options
+a1db585 maint: fix gratuitous whitespace in the code.
+601baf1 maint: remove redundant context from the test.
+5d7ef5c (#7062) better argument handling in the action wrapper methods
+33b5580 maint: move method comments outside the comment.
+c87d6c9 Fixed #7166 - Replaced deprecated stomp "send" method with "publish"
+557767b maint: Remove unused faces code
+311e3ec maint: mangle grammer in rspec to avoid Jenkins fail...
+0fed94f (#7013) Wire up rendering hooks on the CLI.
+12098f2 (#7013) Handle rendering modes out in the application layer.
+5938452 (#7013) Strip out old face-wide rendering defaults.
+5258e06 (#7013) Return bound methods for when_rendering hooks.
+86801b5 (#7013) Support 'when_rendering' and 'render_as' in actions.
+be23b84 (#7013) better default rendering support for faces
+e6caa24 maint: make sure we don't ever default to being default...
+36b100a maint: print 'false' in the default render method.
+2cf692c maint: delete README.strings, which is out of date.
+22355dc maint: test the 'help' face has the default action 'help'
+4efba71 maint: drop multi-version support from action loading.
+266f937 (#6962) Add 'description' to faces and action.
+32c667c (#7132) Reject 'summary' text with newlines embedded.
+1251311 (#7108) Update help/man text for puppet kick
+eeb1b60 (#7108) Modernize description of --listen in defaults.rb
+3ec9526 Maint: puppetmaster -> puppet master in defaults.rb
+5a10093 (Maint) Fix a leaking spec, patching intermittent failures.
+07b677c Merge remote-tracking branch 'community/feature/puppet-device' into 2.7.x
+13e64fe (#7131) Remove support for optional arguments to options
+977684e (Maint) Fixing an order-dependent failure.
+9d2ec21 (#7013) Add support for required options.
+eeb82f8 (Maint) Code cleanup.
+d85c2a8 maint: Fix the missed failure from the previous commit
+e946a17 maint: Fix a broken Puppet::Node::Facts spec
+d80500f maint: speed up testing output of the help face.
+9264526 (#7115) Enable default actions.
+ab541fa (#7059) Use option hooks for the indirector terminus option.
+f770325 (#6978) Enforce the calling convention of option hooks.
+c00e03d (#7059) Set the CA location using option hooks.
+dca1f07 (#6978) Add before and after decorators to actions from options.
+0c60aa2 maint: delete an empty describe block containing no tests.
+e424740 Adding json-specific matchers
+f37b2e1 Making watchr resilient to syntax errors in tests
+d3c94e6 Adding json support to Puppet::Node
+155b16d Fixing a failing test resulting from a fixed bug
+e0615cb (7080) Adding json support to Indirector Request
+07a7a68 Fixing Facts pson methods more resilient
+ff08ba2 (7118) Adding summaries for all faces
+a509821 Cleanup trailing whitespace
+5528911 (#7111) Clarify scoping deprecation warning
+ca9d68f (#6408) Update puppet cert help for new subcommand action syntax.
+174e87a (#4258) Fix pkgutil spec test to have the correct provider
+e119739 (#6928) Add a notice to Parser#validate action when using default
+9bc4bce (#7103) Fix HEAD requests in the HTTP handler
+cb552af (#4258) Remove superfluous command check that called pkgutil
+fd98341 (#4258) Fix fd leak opening pkgutil config files
+7726dc3 (#4258) Permit variations of -nv in both pkgutil.conf files
+f8c2f1a (#4258) Stop file and config checks from breaking spec
+ef86105 (#4258) Check wgetopts in pkgutil.conf
+557ed85 (#4258) Fix hash duplication affecting canonical provider instance
+7c99dd9 (#4258) Use pkgutil -a to reliably determine package common names/aliases
+ab5bc35 (#4258) Update pkgutil spec for recent impl changes
+e582709 (#4258) pkgutil: bug fix: if shortname is not equal to package name
+58ac7d3 (#4258) pkgutil provider: better handling of short package names
+15a53f0 (#4258) pkgutil provider: misc enhancements
+15e225b Add spec tests for pkgutil package provider
+8462acd * Fix exception on parse failure of pkgutil output * Fix exception when querying latest version for unknown package
+3eace85 Fixing indentation
+f8e9155 Removing blastwave references and unused PAGER
+485ac38 Changing indentation to 2-spaces as per 2.6+ style
+9d63171 Single package queries made more robust when dealing with pkgutil noise
+f50fac7 Fixing wget verbose regex
+3003719 These regular expressions will not match anything. pkgutil doesn't output anything that can be matched.
+2725fb3 Add comments that explain what we are ignoring in the package and remove legacy output
+143fc74 Ignoring lines from use_gpg and catalog fetching
+69a3451 Adding patch from Rudy Gevaert to fix not installed detection
+d026bb7 pkgutil provider: Using the --single option which speeds up execution.
+ec2a03c pkgutil provider: The path to the admin file is /var/opt/csw/pkgutil/admin
+0fc2aa6 pkgutil provider: Correcting a typo in a message.
+e02ba01 Using --single in the pkgutil provider.
+fc18591 Adding pkgutil support.
+9f365b1 Fixed #4258 - Added pkgutil package provider
+
+
2.7.0rc1
====
5915814 Revert "(#6928) Removed --ignoreimport"
diff --git a/acceptance/tests/ticket_5477_master_not_dectect_sitepp.rb b/acceptance/tests/ticket_5477_master_not_dectect_sitepp.rb
index d05735e50..792e88b46 100644
--- a/acceptance/tests/ticket_5477_master_not_dectect_sitepp.rb
+++ b/acceptance/tests/ticket_5477_master_not_dectect_sitepp.rb
@@ -41,3 +41,6 @@ agents.each { |agent|
fail_test "Site.pp not detect at Master?" unless
stdout.include? 'ticket_5477_notify'
}
+
+step "Clean-up site.pp"
+on master, "rm /etc/puppet/manifests/site.pp"
diff --git a/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb b/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb
index aac53138a..155e91d3f 100644
--- a/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb
+++ b/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb
@@ -7,8 +7,7 @@ create_remote_file(master, '/tmp/bad.pp', 'notify{bad:')
step "Master: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning"
on master, puppet_master( %w{--parseonly /tmp/bad.pp} ), :acceptable_exit_codes => [ 1 ]
- fail_test "Deprecation warning not issued for --parseonly" unless
- stdout.include? '--parseonly has been removed. Please use \'puppet parser validate <manifest>\''
+ assert_match(/--parseonly has been removed. Please use \'puppet parser validate <manifest>\'/, stdout, "Deprecation warning not issued for --parseonly on #{master}" )
step "Agents: create valid, invalid formatted manifests"
agents.each do |host|
@@ -19,8 +18,7 @@ end
step "Agents: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning"
agents.each do |host|
on(host, "puppet --parseonly /tmp/bad.pp}", :acceptable_exit_codes => [ 1 ]) do
- fail_test "Deprecation warning not issued for --parseonly" unless
- stdout.include? '--parseonly has been removed. Please use \'puppet parser validate <manifest>\''
+ assert_match(/--parseonly has been removed. Please use \'puppet parser validate <manifest>\'/, stdout, "Deprecation warning not issued for --parseonly on #{host}" )
end
end
@@ -29,10 +27,9 @@ agents.each do |host|
on(host, "puppet parser validate /tmp/good.pp", :acceptable_exit_codes => [ 0 ])
end
-step "Test Face for ‘parser validate’ with bad manifest -- should fail"
+step "Test Faces for ‘parser validate’ with bad manifest -- should fail"
agents.each do |host|
on(host, "puppet parser validate /tmp/bad.pp", :acceptable_exit_codes => [ 1 ]) do
- fail_test "Bad manifest detection failed" unless
- stderr.include? 'Could not run: Could not parse for environment production'
+ assert_match(/err: Could not parse for environment production/, stdout, "Bad manifest detection failed on #{host}" )
end
end
diff --git a/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb b/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb
new file mode 100644
index 000000000..76a6bf818
--- /dev/null
+++ b/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb
@@ -0,0 +1,33 @@
+test_name "#7117 Broke the environment criteria in auth.conf"
+
+# add to auth.conf
+add_2_authconf = %q{
+path /
+environment override
+auth any
+allow *
+}
+
+step "Save original auth.conf file and create a temp auth.conf"
+on master, "cp #{config['puppetpath']}/auth.conf /tmp/auth.conf-7117; echo '#{add_2_authconf}' > #{config['puppetpath']}/auth.conf"
+
+# Kill running Puppet Master -- should not be running at this point
+step "Master: kill running Puppet Master"
+on master, "ps -U puppet | awk '/puppet/ { print \$1 }' | xargs kill || echo \"Puppet Master not running\""
+step "Master: Start Puppet Master"
+on master, puppet_master("--certdnsnames=\"puppet:$(hostname -s):$(hostname -f)\" --verbose --noop")
+
+
+# Run test on Agents
+step "Agent: agent --test"
+on agents, puppet_agent("--test")
+
+step "Fetch agent facts from Puppet Master"
+agents.each do |host|
+ on(host, "curl -k -H \"Accept: yaml\" https://#{master}:8140/override/facts/\`hostname -f\`") do
+ assert_match(/--- !ruby\/object:Puppet::Node::Facts/, stdout, "Agent Facts not returned for #{host}")
+ end
+end
+
+step "Restore original auth.conf file"
+on master, "cp -f /tmp/auth.conf-7117 #{config['puppetpath']}/auth.conf"
diff --git a/acceptance/tests/ticket_7139_puppet_resource_file_qualified_paths.rm b/acceptance/tests/ticket_7139_puppet_resource_file_qualified_paths.rm
new file mode 100644
index 000000000..f773ba17c
--- /dev/null
+++ b/acceptance/tests/ticket_7139_puppet_resource_file_qualified_paths.rm
@@ -0,0 +1,11 @@
+test_name "#7139: Puppet resource file failes on path with leading '/'"
+
+step "Agents: create valid, invalid formatted manifests"
+create_remote_file(agents, '/tmp/ticket-7139', %w{foo bar contents} )
+
+step "Run puppet file resource on /tmp/ticket-7139"
+agents.each do |host|
+ on(host, "puppet resource file /tmp/ticket-7139") do
+ assert_match(/file \{ \'\/tmp\/ticket-7139\':/, stdout, "puppet resource file failed on #{host}")
+ end
+end
diff --git a/conf/auth.conf b/conf/auth.conf
index 431e4b205..cb202a989 100644
--- a/conf/auth.conf
+++ b/conf/auth.conf
@@ -53,6 +53,11 @@ path ~ ^/catalog/([^/]+)$
method find
allow $1
+# allow nodes to retrieve their own node definition
+path ~ ^/node/([^/]+)$
+method find
+allow $1
+
# allow all nodes to access the certificates services
path /certificate_revocation_list/ca
method find
diff --git a/lib/puppet/application/face_base.rb b/lib/puppet/application/face_base.rb
index 7bebd18bb..7a5ce3400 100644
--- a/lib/puppet/application/face_base.rb
+++ b/lib/puppet/application/face_base.rb
@@ -15,8 +15,8 @@ class Puppet::Application::FaceBase < Puppet::Application
Puppet::Util::Log.level = :info
end
- option("--render-as FORMAT") do |arg|
- @render_as = arg.to_sym
+ option("--render-as FORMAT") do |format|
+ self.render_as = format.to_sym
end
option("--mode RUNMODE", "-r") do |arg|
@@ -27,55 +27,23 @@ class Puppet::Application::FaceBase < Puppet::Application
attr_accessor :face, :action, :type, :arguments, :render_as
- 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
+ def render_as=(format)
+ if format == :json then
+ @render_as = Puppet::Network::FormatHandler.format(:pson)
+ else
+ @render_as = Puppet::Network::FormatHandler.format(format)
+ end
+ @render_as or raise ArgumentError, "I don't know how to render '#{format}'"
end
def render(result)
- format = render_as || action.render_as || :for_humans
-
# Invoke the rendering hook supplied by the user, if appropriate.
- if hook = action.when_rendering(format) then
+ if hook = action.when_rendering(render_as.name)
result = hook.call(result)
end
- if format == :for_humans then
- render_for_humans(result)
- else
- render_method = Puppet::Network::FormatHandler.format(format).render_method
- if render_method == "to_pson"
- PSON::pretty_generate(result, :allow_nan => true, :max_nesting => false)
- else
- result.send(render_method)
- end
- end
- end
-
- def render_for_humans(result)
- # String to String
- return result if result.is_a? String
- return result if result.is_a? Numeric
-
- # Simple hash to table
- if result.is_a? Hash and result.keys.all? { |x| x.is_a? String or x.is_a? Numeric }
- output = ''
- column_a = result.map do |k,v| k.to_s.length end.max + 2
- column_b = 79 - column_a
- result.sort_by { |k,v| k.to_s } .each do |key, value|
- output << key.to_s.ljust(column_a)
- output << PP.pp(value, '', column_b).
- chomp.gsub(/\n */) { |x| x + (' ' * column_a) }
- output << "\n"
- end
- return output
- end
-
- # ...or pretty-print the inspect outcome.
- return result.pretty_inspect
+ render_as.render(result)
end
def preinit
@@ -133,8 +101,13 @@ class Puppet::Application::FaceBase < Puppet::Application
end
if @action.nil?
- @action = @face.get_default_action()
- @is_default_action = true
+ if @action = @face.get_default_action() then
+ @is_default_action = true
+ else
+ Puppet.err "#{face.name} does not have a default action, and no action was given"
+ Puppet.err Puppet::Face[:help, :current].help(@face.name)
+ exit false
+ end
end
# Now we can interact with the default option code to build behaviour
@@ -142,7 +115,7 @@ class Puppet::Application::FaceBase < Puppet::Application
@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 if @action
+ end
# ...and invoke our parent to parse all the command line options.
super
@@ -194,23 +167,66 @@ class Puppet::Application::FaceBase < Puppet::Application
# would invoke the action with options set as global state in the
# interface object. --daniel 2011-03-28
@arguments << options
+
+ # If we don't have a rendering format, set one early.
+ self.render_as ||= (@action.render_as || :console)
end
def main
+ status = false
+
# Call the method associated with the provided action (e.g., 'find').
- if @action
- result = @face.send(@action.name, *arguments)
- puts render(result) unless result.nil?
- else
- if arguments.first.is_a? Hash
- puts "#{@face} does not have a default action"
- else
- puts "#{@face} does not respond to action #{arguments.first}"
- end
+ unless @action
+ puts Puppet::Face[:help, :current].help(@face.name)
+ raise "#{face} does not respond to action #{arguments.first}"
+ end
- puts Puppet::Face[:help, :current].help(@face.name, *arguments)
+ # We need to do arity checking here because this is generic code
+ # calling generic methods – that have argument defaulting. We need to
+ # make sure we don't accidentally pass the options as the first
+ # argument to a method that takes one argument. eg:
+ #
+ # puppet facts find
+ # => options => {}
+ # @arguments => [{}]
+ # => @face.send :bar, {}
+ #
+ # def face.bar(argument, options = {})
+ # => bar({}, {}) # oops! we thought the options were the
+ # # positional argument!!
+ #
+ # We could also fix this by making it mandatory to pass the options on
+ # every call, but that would make the Ruby API much more annoying to
+ # work with; having the defaulting is a much nicer convention to have.
+ #
+ # We could also pass the arguments implicitly, by having a magic
+ # 'options' method that was visible in the scope of the action, which
+ # returned the right stuff.
+ #
+ # That sounds attractive, but adds complications to all sorts of
+ # things, especially when you think about how to pass options when you
+ # are writing Ruby code that calls multiple faces. Especially if
+ # faces are involved in that. ;)
+ #
+ # --daniel 2011-04-27
+ if (arity = @action.positional_arg_count) > 0
+ unless (count = arguments.length) == arity then
+ s = arity == 2 ? '' : 's'
+ raise ArgumentError, "puppet #{@face.name} #{@action.name} takes #{arity-1} argument#{s}, but you gave #{count-1}"
+ end
end
- exit(exit_code)
+
+ result = @face.send(@action.name, *arguments)
+ puts render(result) unless result.nil?
+ status = true
+
+ rescue Exception => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err detail.to_s
+ Puppet.err "Try 'puppet help #{@face.name} #{@action.name}' for usage"
+
+ ensure
+ exit status
end
end
diff --git a/lib/puppet/application/faces.rb b/lib/puppet/application/faces.rb
index 3dd3f0312..e7fce66b1 100644
--- a/lib/puppet/application/faces.rb
+++ b/lib/puppet/application/faces.rb
@@ -10,16 +10,54 @@ class Puppet::Application::Faces < Puppet::Application
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 help
+ <<-HELP
+puppet-faces(8) -- List available Faces and actions
+========
+
+SYNOPSIS
+--------
+Lists the available subcommands (with applicable terminuses and/or actions)
+provided by the Puppet Faces API. This information is automatically read
+from the Puppet code present on the system. By default, the output includes
+all terminuses and actions.
+
+USAGE
+-----
+puppet faces [-d|--debug] [-v|--verbose] [actions|terminuses]
+
+OPTIONS
+-------
+Note that any configuration option valid in the configuration file is also
+a valid long 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'.
+
+* --verbose:
+ Sets the log level to "info." This option has no tangible effect at the time
+ of this writing.
+
+* --debug:
+ Sets the log level to "debug." This option has no tangible effect at the time
+ of this writing.
+
+AUTHOR
+------
+Puppet Labs
+
+COPYRIGHT
+---------
+Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
+
+ HELP
+ end
+
def list(*arguments)
if arguments.empty?
arguments = %w{terminuses actions}
diff --git a/lib/puppet/application/help.rb b/lib/puppet/application/help.rb
index 0d7767632..4829a2036 100644
--- a/lib/puppet/application/help.rb
+++ b/lib/puppet/application/help.rb
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
require 'puppet/application/face_base'
class Puppet::Application::Help < Puppet::Application::FaceBase
diff --git a/lib/puppet/application/plugin.rb b/lib/puppet/application/plugin.rb
new file mode 100644
index 000000000..2d0402e43
--- /dev/null
+++ b/lib/puppet/application/plugin.rb
@@ -0,0 +1,3 @@
+require 'puppet/application/face_base'
+class Puppet::Application::Plugin < Puppet::Application::FaceBase
+end
diff --git a/lib/puppet/application/configurer.rb b/lib/puppet/application/secret_agent.rb
index 6e86cd2d4..d704d14b2 100644
--- a/lib/puppet/application/configurer.rb
+++ b/lib/puppet/application/secret_agent.rb
@@ -1,7 +1,7 @@
require 'puppet/application'
require 'puppet/face'
-class Puppet::Application::Configurer < Puppet::Application
+class Puppet::Application::Secret_agent < Puppet::Application
should_parse_config
run_mode :agent
@@ -17,7 +17,7 @@ class Puppet::Application::Configurer < Puppet::Application
end
def run_command
- report = Puppet::Face[:configurer, '0.0.1'].synchronize(Puppet[:certname])
+ report = Puppet::Face[:secret_agent, '0.0.1'].synchronize(Puppet[:certname])
Puppet::Face[:report, '0.0.1'].submit(report)
end
end
diff --git a/lib/puppet/face/catalog.rb b/lib/puppet/face/catalog.rb
index 0dcde3591..4624313bc 100644
--- a/lib/puppet/face/catalog.rb
+++ b/lib/puppet/face/catalog.rb
@@ -1,8 +1,56 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
+
+Puppet::Indirector::Face.define(:catalog, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Compile, save, view, and convert catalogs."
+ description <<-EOT
+ This face primarily interacts with the compiling subsystem. By default,
+ it compiles a catalog using the default manifest and the hostname from
+ `certname`, but you can choose to retrieve a catalog from the server by
+ specifying `--terminus rest`. You can also choose to print any catalog
+ in 'dot' format (for easy graph viewing with OmniGraffle or Graphviz)
+ with '--render-as dot'.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `active_record`
+ * `compiler`
+ * `queue`
+ * `rest`
+ * `yaml`
+ EOT
-Puppet::Face::Indirector.define(:catalog, '0.0.1') do
action(:apply) do
- when_invoked do |catalog, options|
+ summary "Apply a Puppet::Resource::Catalog object"
+ description <<-EOT
+ Applies a catalog object retrieved with the `download` action. This
+ action cannot consume a serialized catalog, and is not intended for
+ command-line use."
+ EOT
+ notes <<-EOT
+ This action returns a Puppet::Transaction::Report object.
+ EOT
+ examples <<-EOT
+ From `secret_agent.rb`:
+
+ Puppet::Face[:plugin, '0.0.1'].download
+
+ 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)
+
+ Puppet::Face[:report, '0.0.1'].submit(report)
+ EOT
+
+ when_invoked do |options|
+ catalog = Puppet::Face[:catalog, "0.0.1"].find(Puppet[:certname]) or raise "Could not find catalog for #{Puppet[:certname]}"
+ catalog = catalog.to_ral
+
report = Puppet::Transaction::Report.new("apply")
report.configuration_version = catalog.version
@@ -23,18 +71,38 @@ Puppet::Face::Indirector.define(:catalog, '0.0.1') do
end
action(:download) do
- when_invoked do |certname, facts, options|
+ summary "Download this node's catalog from the puppet master server"
+ description <<-EOT
+ Retrieves a catalog from the puppet master. Unlike the `find` action,
+ `download` submits facts to the master as part of the request. This
+ action is not intended for command-line use.
+ EOT
+ notes "This action returns a Puppet::Resource::Catalog object."
+ examples <<-EOT
+ From `secret_agent.rb`:
+
+ Puppet::Face[:plugin, '0.0.1'].download
+
+ 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)
+
+ Puppet::Face[:report, '0.0.1'].submit(report)
+ EOT
+ when_invoked do |options|
Puppet::Resource::Catalog.indirection.terminus_class = :rest
- facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))}
+ Puppet::Resource::Catalog.indirection.cache_class = nil
catalog = nil
retrieval_duration = thinmark do
- catalog = Puppet::Face[:catalog, '0.0.1'].find(certname, facts_to_upload)
+ catalog = Puppet::Face[:catalog, '0.0.1'].find(Puppet[:certname])
end
- catalog = catalog.to_ral
- catalog.finalize
catalog.retrieval_duration = retrieval_duration
catalog.write_class_file
- catalog
+
+ Puppet::Resource::Catalog.indirection.terminus_class = :yaml
+ Puppet::Face[:catalog, "0.0.1"].save(catalog)
+ Puppet.notice "Saved catalog for #{Puppet[:certname]} to yaml"
+ nil
end
end
end
diff --git a/lib/puppet/face/catalog/select.rb b/lib/puppet/face/catalog/select.rb
index ba27117bc..a6c97a627 100644
--- a/lib/puppet/face/catalog/select.rb
+++ b/lib/puppet/face/catalog/select.rb
@@ -1,10 +1,42 @@
# Select and show a list of resources of a given type.
Puppet::Face.define(:catalog, '0.0.1') do
action :select do
+ summary "Select and show a list of resources of a given type"
+ description <<-EOT
+ Retrieves a catalog for the specified host and returns an array of
+ resources of the given type. This action is not intended for
+ command-line use.
+ EOT
+ notes <<-NOTES
+ The type name for this action must be given in its capitalized form.
+ That is, calling `catalog select mynode file` will return an empty
+ array, whereas calling it with 'File' will return a list of the node's
+ file resources.
+
+ By default, this action will retrieve a catalog from Puppet's compiler
+ subsystem; you must call the action with `--terminus rest` if you wish
+ to retrieve a catalog from the puppet master.
+ NOTES
when_invoked do |host, type, options|
+ # REVISIT: Eventually, type should have a default value that triggers
+ # the non-specific behaviour. For now, though, this will do.
+ # --daniel 2011-05-03
catalog = Puppet::Resource::Catalog.indirection.find(host)
- catalog.resources.reject { |res| res.type != type }.each { |res| puts res }
+ if type == '*'
+ catalog.resources
+ else
+ type = type.downcase
+ catalog.resources.reject { |res| res.type.downcase != type }
+ end
+ end
+
+ when_rendering :console do |value|
+ if value.nil? then
+ "no matching resources found"
+ else
+ value.map {|x| x.to_s }.join("\n")
+ end
end
end
end
diff --git a/lib/puppet/face/certificate.rb b/lib/puppet/face/certificate.rb
index 4c2950fb3..ee2b2873f 100644
--- a/lib/puppet/face/certificate.rb
+++ b/lib/puppet/face/certificate.rb
@@ -1,15 +1,51 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
require 'puppet/ssl/host'
-Puppet::Face::Indirector.define(:certificate, '0.0.1') do
+Puppet::Indirector::Face.define(:certificate, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Provide access to the CA for certificate management"
+ description <<-EOT
+ This face interacts with a local or remote Puppet certificate
+ authority. Currently, its behavior is not a full superset of puppet
+ cert; specifically, it is unable to mimic puppet cert's "clean" option,
+ and its "generate" action submits a CSR rather than creating a
+ signed certificate.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `ca`
+ * `file`
+ * `rest`
+ EOT
+
option "--ca-location LOCATION" do
+ summary "The certificate authority to query"
+ description <<-EOT
+ Whether to act on the local certificate authority or one provided by a
+ remote puppet master. Allowed values are 'local' and 'remote.'
+ EOT
+
before_action do |action, args, options|
Puppet::SSL::Host.ca_location = options[:ca_location].to_sym
end
end
action :generate do
- summary "Generate a new Certificate Signing Request for HOST"
+ summary "Generate a new certificate signing request for HOST"
+ description <<-EOT
+ Generates and submits a certificate signing request (CSR) for the
+ provided host identifier. This CSR will then have to be signed by a user
+ with the proper authorization on the certificate authority.
+
+ Puppet agent handles CSR submission automatically. This action is
+ primarily useful for requesting certificates for individual users and
+ external applications.
+ EOT
when_invoked do |name, options|
host = Puppet::SSL::Host.new(name)
@@ -19,7 +55,7 @@ Puppet::Face::Indirector.define(:certificate, '0.0.1') do
end
action :list do
- summary "List all Certificate Signing Requests"
+ summary "List all certificate signing requests"
when_invoked do |options|
Puppet::SSL::Host.indirection.search("*", {
@@ -29,7 +65,7 @@ Puppet::Face::Indirector.define(:certificate, '0.0.1') do
end
action :sign do
- summary "Sign a Certificate Signing Request for HOST"
+ summary "Sign a certificate signing request for HOST"
when_invoked do |name, options|
host = Puppet::SSL::Host.new(name)
diff --git a/lib/puppet/face/certificate_request.rb b/lib/puppet/face/certificate_request.rb
index 1feba25ab..cc6021517 100644
--- a/lib/puppet/face/certificate_request.rb
+++ b/lib/puppet/face/certificate_request.rb
@@ -1,4 +1,32 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
-Puppet::Face::Indirector.define(:certificate_request, '0.0.1') do
+Puppet::Indirector::Face.define(:certificate_request, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Manage certificate requests."
+ description <<-EOT
+ Retrieves and submits certificate signing requests (CSRs). Invoke
+ `search` with an unread key to retrieve all outstanding CSRs, invoke
+ `find` with a node certificate name to retrieve a specific request, and
+ invoke `save` to submit a CSR.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `ca`
+ * `file`
+ * `rest`
+ EOT
+ examples <<-EOT
+ Retrieve all CSRs from the local CA:
+
+ puppet certificate_request search no_key --terminus ca
+
+ Retrieve a single CSR from the puppet master's CA:
+
+ puppet certificate_request find mynode.puppetlabs.lan --terminus rest
+ EOT
end
diff --git a/lib/puppet/face/certificate_revocation_list.rb b/lib/puppet/face/certificate_revocation_list.rb
index 6a75aa578..2722b20f2 100644
--- a/lib/puppet/face/certificate_revocation_list.rb
+++ b/lib/puppet/face/certificate_revocation_list.rb
@@ -1,4 +1,30 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
-Puppet::Face::Indirector.define(:certificate_revocation_list, '0.0.1') do
+Puppet::Indirector::Face.define(:certificate_revocation_list, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Manage the list of revoked certificates."
+ description <<-EOT
+ This face is primarily for retrieving the certificate revocation
+ list from the CA. Although it exposes search/save/destroy methods,
+ they shouldn't be used under normal circumstances.
+ EOT
+ notes <<-EOT
+ Although the find action must be given an argument, this argument is
+ never read, and can contain the descriptive text of your choice.
+
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `ca`
+ * `file`
+ * `rest`
+ EOT
+ examples <<-EXAMPLES
+ Retrieve the CRL:
+
+ puppet certificate_revocation_list find crl
+ EXAMPLES
end
diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb
index 45cb6b156..9ca41085e 100644
--- a/lib/puppet/face/config.rb
+++ b/lib/puppet/face/config.rb
@@ -1,7 +1,43 @@
require 'puppet/face'
Puppet::Face.define(:config, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Interact with Puppet configuration options"
+
action(:print) do
+ summary "Examine Puppet's current configuration options"
+ description <<-EOT
+ Prints the value of a single configuration option or a list of
+ configuration options.
+
+ This action is an alternate interface to the information available with
+ `puppet agent --configprint`.
+ EOT
+ notes <<-EOT
+ The return data of this action varies depending on its arguments. When
+ called with "all," `print` will return a complete list of option names
+ and values. When called with a single configuration option name, it will
+ return the value of that option. When called with a list of
+ configuration option names, it will return the corresponding list of
+ option names and values.
+
+ By default, this action retrieves its configuration information in agent
+ mode. To examine the master's configuration, supply Puppet's global
+ `--mode master` option. To examine configurations from a specific
+ environment, you can use the `--environment` option.
+ EOT
+ examples <<-EOT
+ Get puppet's runfile directory:
+
+ puppet config print rundir
+
+ Get a list of important directories from the master's config:
+
+ puppet config print all --mode master | grep -E "(path|dir)"
+ EOT
+
when_invoked do |*args|
options = args.pop
Puppet.settings[:configprint] = args.join(",")
diff --git a/lib/puppet/face/configurer.rb b/lib/puppet/face/configurer.rb
deleted file mode 100644
index 74dfb854e..000000000
--- a/lib/puppet/face/configurer.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-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
index 04eab93a5..ecf4e371e 100644
--- a/lib/puppet/face/facts.rb
+++ b/lib/puppet/face/facts.rb
@@ -1,9 +1,40 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
require 'puppet/node/facts'
-Puppet::Face::Indirector.define(:facts, '0.0.1') do
- # Upload our facts to the server
+Puppet::Indirector::Face.define(:facts, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Retrieve, store, and view facts."
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `active_record`
+ * `couch`
+ * `facter`
+ * `inventory_active_record`
+ * `memory`
+ * `network_device`
+ * `rest`
+ * `yaml`
+ EOT
+
action(:upload) do
+ summary "Upload our facts to the puppet master."
+ description <<-EOT
+ Retrieves facts for the local system and saves them to the puppet master
+ server. This is essentially a shortcut action: it calls the `find`
+ action with the facter terminus, then passes the returned facts object
+ to the `save` action, which uses the rest terminus.
+ EOT
+ notes <<-EOT
+ This action uses the save action, which requires the puppet master's
+ auth.conf to allow save access to the `facts` REST terminus. See
+ `http://docs.puppetlabs.com/guides/rest_auth_conf.html` for more details.
+ EOT
+
render_as :yaml
when_invoked do |options|
diff --git a/lib/puppet/face/file.rb b/lib/puppet/face/file.rb
index 1aa9462dd..707ceafd4 100644
--- a/lib/puppet/face/file.rb
+++ b/lib/puppet/face/file.rb
@@ -1,5 +1,21 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
+
+Puppet::Indirector::Face.define(:file, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Retrieve and store files in a filebucket"
+ # TK this needs a description of how to find files in a filebucket, and
+ # some good use cases for retrieving/storing them. I can't write either
+ # of these yet.
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `file`
+ * `rest`
+ EOT
-Puppet::Face::Indirector.define(:file, '0.0.1') do
set_indirection_name :file_bucket_file
end
diff --git a/lib/puppet/face/file/download.rb b/lib/puppet/face/file/download.rb
new file mode 100644
index 000000000..f5413d493
--- /dev/null
+++ b/lib/puppet/face/file/download.rb
@@ -0,0 +1,36 @@
+# Download a specified file into the local filebucket.
+Puppet::Face.define(:file, '0.0.1') do
+ action :download do |*args|
+ when_invoked do |sum, options|
+ if sum =~ /^puppet:\/\// # it's a puppet url
+ require 'puppet/file_serving'
+ require 'puppet/file_serving/content'
+ raise "Could not find metadata for #{sum}" unless content = Puppet::FileServing::Content.indirection.find(sum)
+ file = Puppet::FileBucket::File.new(content.content)
+ else
+ tester = Object.new
+ tester.extend(Puppet::Util::Checksums)
+
+ type = tester.sumtype(sum)
+ sumdata = tester.sumdata(sum)
+
+ key = "#{type}/#{sumdata}"
+
+ Puppet::FileBucket::File.indirection.terminus_class = :file
+ if Puppet::FileBucket::File.indirection.find(key)
+ Puppet.info "Content for '#{sum}' already exists"
+ return
+ end
+
+ Puppet::FileBucket::File.indirection.terminus_class = :rest
+ raise "Could not download content for '#{sum}'" unless file = Puppet::FileBucket::File.indirection.find(key)
+ end
+
+
+ Puppet::FileBucket::File.indirection.terminus_class = :file
+ Puppet.notice "Saved #{sum} to filebucket"
+ Puppet::FileBucket::File.indirection.save file
+ return nil
+ end
+ end
+end
diff --git a/lib/puppet/face/file/store.rb b/lib/puppet/face/file/store.rb
new file mode 100644
index 000000000..4c9523b6c
--- /dev/null
+++ b/lib/puppet/face/file/store.rb
@@ -0,0 +1,12 @@
+# Store a specified file in our filebucket.
+Puppet::Face.define(:file, '0.0.1') do
+ action :store do |*args|
+ when_invoked do |path, options|
+ file = Puppet::FileBucket::File.new(File.read(path))
+
+ Puppet::FileBucket::File.indirection.terminus_class = :file
+ Puppet::FileBucket::File.indirection.save file
+ file.checksum
+ end
+ end
+end
diff --git a/lib/puppet/face/help.rb b/lib/puppet/face/help.rb
index a762fb02e..aef917447 100644
--- a/lib/puppet/face/help.rb
+++ b/lib/puppet/face/help.rb
@@ -4,13 +4,16 @@ require 'pathname'
require 'erb'
Puppet::Face.define(:help, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
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"
+ summary "which version of the interface to show help for"
end
default
@@ -21,29 +24,20 @@ Puppet::Face.define(:help, '0.0.1') do
options = args.pop
if options.nil? or args.length > 2 then
if args.select { |x| x == 'help' }.length > 2 then
- c = "\n !\"'),-./7:;<GIJLST\\_`abcdefhiklmnoprstuwx|}".split('')
+ c = "\n %'(),-./=ADEFHILORSTUXY\\_`gnv|".split('')
i = <<-'EOT'.gsub(/\s*/, '').to_i(36)
- 2s7ytxy5vpj74kbab5xzf1ik2roinzlefaspjrzckiert5xbaxvwlku3a91w7y1rsd
- nenp51gwpulmnrp54nwdil36fjgjarab801y0r5a9nh1hdfgi99arn5c5t3zhxbvzi
- u6wx5r1tb7lun7pro69nrxunqqixsh6qmmv0ms0i0yycqw3pystyzmiita0lpxynqs
- qkbjwadcx82n76wwpzbht8i8rgvqhqick8mk3cs3rvwdjookpgu0rxw4tcezned5sq
- z5x8z9vntyyz0s4h6hjhtwtbytsmmu7ltvdftaixc7fkt276sqm48ab4yv0ot9y26n
- z0xniy4pfl1x300lt6h9c8of49vf799ieuxwnoycsjlmtd4qntzit524j0tdn6n5aj
- mq3z10apjuhkzprvmu53z1gnacymnoforrz5mbqto062kckgw5463pxwzg8liglub4
- ubnr0dln1s6iy3ummxuhim7m5a7yedl3gyy6ow4qqtmsigv27lysooau24zpsccsvx
- ddwygjprqpbwon7i9s1279m1fpinvva8mfh6bgmotrpxsh1c8rc83l3u0utf5i200y
- l7ui0ngcbcjyr4erzdee2tqk3fpjvb82t8xhncruhgn7j5dh2m914qzhb0gkoom47k
- 6et7rp4tqjnrv0y2apk5qdl1x1hnbkkxup5ys6ip2ksmtpd3ipmrdtswxr5xwfiqtm
- 60uyjr1v79irhnkrbbt4fwhgqjby1qflgwt9c1wpayzzucep6npgbn3f1k6cn4pug3
- 1u02wel4tald4hij8m5p49xr8u4ero1ucs5uht42o8nhpmpe7c7xf9t85i85m9m5kk
- tgoqkgbu52gy5aoteyp8jkm3vri9fnkmwa5h60zt8otja72joxjb40p2rz2vp8f8q9
- nnggxt3x90pe5u4048ntyuha78q1oikhhpvw9j083yc3l00hz5ehv9c1au5gvctyap
- zprub289qruve9qsyuh75j04wzkemqw3uhisrfs92u1ahv2qlqxmorgob16c1vbqkx
- ttkoyp2agkt0v5l7lec25p0jqun9y39k41h67aeb5ihiqsftxc9azmg31hc73dk8ur
- lj88vgbmgt8yln9rchw60whgxvnv9zn6cxbr482svctswc5a07atj
+ 3he6737w1aghshs6nwrivl8mz5mu9nywg9tbtlt081uv6fq5kvxse1td3tj1wvccmte806nb
+ cy6de2ogw0fqjymbfwi6a304vd56vlq71atwmqsvz3gpu0hj42200otlycweufh0hylu79t3
+ gmrijm6pgn26ic575qkexyuoncbujv0vcscgzh5us2swklsp5cqnuanlrbnget7rt3956kam
+ j8adhdrzqqt9bor0cv2fqgkloref0ygk3dekiwfj1zxrt13moyhn217yy6w4shwyywik7w0l
+ xtuevmh0m7xp6eoswin70khm5nrggkui6z8vdjnrgdqeojq40fya5qexk97g4d8qgw0hvokr
+ pli1biaz503grqf2ycy0ppkhz1hwhl6ifbpet7xd6jjepq4oe0ofl575lxdzjeg25217zyl4
+ nokn6tj5pq7gcdsjre75rqylydh7iia7s3yrko4f5ud9v8hdtqhu60stcitirvfj6zphppmx
+ 7wfm7i9641d00bhs44n6vh6qvx39pg3urifgr6ihx3e0j1ychzypunyou7iplevitkyg6gbg
+ wm08oy1rvogcjakkqc1f7y1awdfvlb4ego8wrtgu9vzw4vmj59utwifn2ejcs569dh1oaavi
+ sc581n7jjg1dugzdu094fdobtx6rsvk3sfctvqnr36xctold
EOT
- 607.times{i,x=i.divmod(1035);a,b=x.divmod(23);print(c[a]*b)}
- raise ArgumentError, "Such panic is really not required."
+ 353.times{i,x=i.divmod(1184);a,b=x.divmod(37);print(c[a]*b)}
end
raise ArgumentError, "help only takes two (optional) arguments, a face name, and an action"
end
diff --git a/lib/puppet/face/help/action.erb b/lib/puppet/face/help/action.erb
index eaf131464..c26958a35 100644
--- a/lib/puppet/face/help/action.erb
+++ b/lib/puppet/face/help/action.erb
@@ -1,3 +1,48 @@
-Use: puppet <%= face.name %> [options] <%= action.name %> [options]
+puppet <%= face.name %><%= action.default? ? '' : " #{action.name}" %>(1) -- <%= action.summary || face.summary %>
+<%= '=' * (_erbout.length - 1) %>
-Summary: <%= action.summary %>
+% if action.synopsis
+SYNOPSIS
+--------
+
+<%= action.synopsis %>
+
+% end
+% if action.description
+DESCRIPTION
+-----------
+<%= action.description %>
+
+%end
+% unless action.options.empty?
+OPTIONS
+-------
+% action.options.sort.each do |name|
+% option = action.get_option name
+<%= " " + option.optparse.join(" |" ) %>
+<%= option.summary %>
+<%= option.description %>
+
+% end
+% end
+% if action.examples
+EXAMPLES
+--------
+<%= action.examples %>
+% end
+% if action.notes
+NOTES
+-----
+<%= action.notes %>
+
+% end
+% unless action.authors.empty?
+AUTHOR
+------
+<%= action.authors.map {|x| " * " + x } .join("\n") %>
+
+%end
+COPYRIGHT AND LICENSE
+---------------------
+<%= action.copyright %>
+<%= action.license %>
diff --git a/lib/puppet/face/help/face.erb b/lib/puppet/face/help/face.erb
index efe5fd809..b249981de 100644
--- a/lib/puppet/face/help/face.erb
+++ b/lib/puppet/face/help/face.erb
@@ -1,7 +1,48 @@
-Use: puppet <%= face.name %> [options] <action> [options]
+NAME
+ <%= face.name %> -- <%= face.summary || "unknown face..." %>
-Available actions:
+% if face.synopsis
+SYNOPSIS
+<%= face.synopsis.gsub(/^/, ' ') %>
+
+% end
+% if face.description
+DESCRIPTION
+<%= face.description.chomp.gsub(/^/, ' ') %>
+
+%end
+% unless face.options.empty?
+OPTIONS
+% face.options.sort.each do |name|
+% option = face.get_option name
+<%= " " + option.optparse.join(" |" ) %>
+<%= option.summary %>
+<%= option.description %>
+
+% end
+% end
+ACTIONS
+% padding = face.actions.map{|x| x.to_s.length}.max + 2
% face.actions.each do |actionname|
% action = face.get_action(actionname)
- <%= action.name.to_s.ljust(16) %> <%= action.summary %>
+ <%= action.name.to_s.ljust(padding) %> <%= action.summary %>
% end
+
+% if face.examples
+EXAMPLES
+<%= face.examples %>
+% end
+% if face.notes
+NOTES
+<%= face.notes %>
+
+% end
+% unless face.authors.empty?
+AUTHOR
+<%= face.authors.join("\n").gsub(/^/, ' * ') %>
+
+%end
+COPYRIGHT AND LICENSE
+<%= face.copyright.gsub(/^/, ' ') %>
+<%= face.license.gsub(/^/, ' ') %>
+
diff --git a/lib/puppet/face/help/global.erb b/lib/puppet/face/help/global.erb
index f4c761b2b..80c77ad26 100644
--- a/lib/puppet/face/help/global.erb
+++ b/lib/puppet/face/help/global.erb
@@ -1,4 +1,4 @@
-puppet <subcommand> [options] <action> [options]
+Usage: puppet <subcommand> [options] <action> [options]
Available subcommands, from Puppet Faces:
% Puppet::Face.faces.sort.each do |name|
@@ -16,5 +16,4 @@ Available applications, soon to be ported to Faces:
See 'puppet help <subcommand> <action>' for help on a specific subcommand action.
See 'puppet help <subcommand>' for help on a specific subcommand.
-See 'puppet man <subcommand>' for the full man page.
Puppet v<%= Puppet::PUPPETVERSION %>
diff --git a/lib/puppet/face/key.rb b/lib/puppet/face/key.rb
index 3a11ddb03..67d775ca4 100644
--- a/lib/puppet/face/key.rb
+++ b/lib/puppet/face/key.rb
@@ -1,4 +1,23 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
+
+Puppet::Indirector::Face.define(:key, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Create, save, and remove certificate keys"
+
+ description <<-EOT
+ Keys are created for you automatically when certificate requests are
+ generated with 'puppet certificate generate'. You should not have to use
+ this action directly from the command line.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `ca`
+ * `file`
+ EOT
-Puppet::Face::Indirector.define(:key, '0.0.1') do
end
diff --git a/lib/puppet/face/node.rb b/lib/puppet/face/node.rb
index 12336df8f..be38ad388 100644
--- a/lib/puppet/face/node.rb
+++ b/lib/puppet/face/node.rb
@@ -1,3 +1,26 @@
-require 'puppet/face/indirector'
-Puppet::Face::Indirector.define(:node, '0.0.1') do
+require 'puppet/indirector/face'
+Puppet::Indirector::Face.define(:node, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "View and manage node definitions"
+
+ description <<-EOT
+ This face interacts with node objects, which are what Puppet uses to
+ build a catalog. A node object consists of the node's facts,
+ environment, additional top-scope variables, and classes.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `active_record`
+ * `exec`
+ * `ldap`
+ * `memory`
+ * `plain`
+ * `rest`
+ * `yaml`
+ EOT
end
diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb
index d4aaaf043..e6a9503dd 100644
--- a/lib/puppet/face/parser.rb
+++ b/lib/puppet/face/parser.rb
@@ -2,19 +2,30 @@ 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
- if files.empty?
- files << Puppet[:manifest]
- Puppet.notice "No manifest specified. Validating the default manifest #{Puppet[:manifest]}"
- end
- files.each do |file|
- Puppet[:manifest] = file
- Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types.clear
- end
- nil
- end
- end
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Interact directly with the parser"
+
+ action :validate do
+ summary "Validate the syntax of one or more Puppet manifests"
+ description <<-EOT
+ This action validates Puppet DSL syntax without compiling a catalog or
+ syncing any resources. If no manifest files are provided, it will
+ validate the default site manifest.
+ EOT
+ when_invoked do |*args|
+ args.pop
+ files = args
+ if files.empty?
+ files << Puppet[:manifest]
+ Puppet.notice "No manifest specified. Validating the default manifest #{Puppet[:manifest]}"
+ end
+ 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/plugin.rb b/lib/puppet/face/plugin.rb
new file mode 100644
index 000000000..969d42389
--- /dev/null
+++ b/lib/puppet/face/plugin.rb
@@ -0,0 +1,47 @@
+require 'puppet/face'
+Puppet::Face.define(:plugin, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Interact with the Puppet plugin system"
+ description <<-EOT
+ This face provides network access to the puppet master's store of
+ plugins. It is intended for use in other faces, rather than for direct
+ command line access.
+ EOT
+ notes <<-EOT
+ The puppet master can serve Ruby code collected from the lib directories
+ of its modules. These plugins can be used on agent nodes to extend
+ Facter and implement custom types and providers.
+ EOT
+
+ action :download do
+ summary "Download plugins from the configured master"
+ returns <<-EOT
+ An array containing the files actually downloaded. If all files
+ were in sync, this array will be empty.
+ EOT
+ notes "This action modifies files on disk without returning any data."
+ examples <<-EOT
+ Retrieve plugins from the puppet master:
+
+ Puppet::Face[:plugin, '0.0.1'].download
+ EOT
+
+ when_invoked do |options|
+ require 'puppet/configurer/downloader'
+ Puppet::Configurer::Downloader.new("plugin",
+ Puppet[:plugindest],
+ Puppet[:pluginsource],
+ Puppet[:pluginsignore]).evaluate
+ end
+
+ when_rendering :console do |value|
+ if value.empty? then
+ "No plugins downloaded."
+ else
+ "Downloaded these plugins: #{value.join(', ')}"
+ end
+ end
+ end
+end
diff --git a/lib/puppet/face/report.rb b/lib/puppet/face/report.rb
index 6e6f0b335..c8549b14f 100644
--- a/lib/puppet/face/report.rb
+++ b/lib/puppet/face/report.rb
@@ -1,11 +1,43 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
+
+Puppet::Indirector::Face.define(:report, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Create, display, and submit reports"
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `processor`
+ * `rest`
+ * `yaml`
+ EOT
-Puppet::Face::Indirector.define(:report, '0.0.1') do
action(:submit) do
+ summary "Submit a report object to the puppet master"
+ description <<-EOT
+ This action is essentially a shortcut and wrapper for the `save` action
+ with a terminus of `rest`. It also can provide additional details in the
+ event of a report submission failure. It is not intended for use from
+ a command line.
+ EOT
+ examples <<-EOT
+ From secret_agent.rb:
+ Puppet::Face[:plugin, '0.0.1'].download
+
+ 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)
+
+ Puppet::Face[:report, '0.0.1'].submit(report)
+ EOT
when_invoked do |report, options|
begin
- Puppet::Transaction::Report.terminus_class = :rest
- report.save
+ Puppet::Transaction::Report.indirection.terminus_class = :rest
+ Puppet::Face[:report, "0.0.1"].save(report)
+ Puppet.notice "Uploaded report for #{report.name}"
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not send report: #{detail}"
diff --git a/lib/puppet/face/resource.rb b/lib/puppet/face/resource.rb
index d162f728a..ed6360888 100644
--- a/lib/puppet/face/resource.rb
+++ b/lib/puppet/face/resource.rb
@@ -1,4 +1,23 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
-Puppet::Face::Indirector.define(:resource, '0.0.1') do
+Puppet::Indirector::Face.define(:resource, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Interact directly with resources via the RAL, like ralsh"
+ description <<-EOT
+ This face provides a Ruby API with functionality similar to the puppet
+ resource (née ralsh) command line application. It is not intended to be
+ used from the command line.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `ral`
+ * `rest`
+ EOT
+
+ examples "TK we really need some examples for this one."
end
diff --git a/lib/puppet/face/resource_type.rb b/lib/puppet/face/resource_type.rb
index 0cdbd719f..77ccefa8f 100644
--- a/lib/puppet/face/resource_type.rb
+++ b/lib/puppet/face/resource_type.rb
@@ -1,4 +1,17 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
-Puppet::Face::Indirector.define(:resource_type, '0.0.1') do
+Puppet::Indirector::Face.define(:resource_type, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "View resource types, classes, and nodes from all manifests"
+ description "TK I have no idea what this does."
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `parser`
+ * `rest`
+ EOT
end
diff --git a/lib/puppet/face/secret_agent.rb b/lib/puppet/face/secret_agent.rb
new file mode 100644
index 000000000..c8c8e6629
--- /dev/null
+++ b/lib/puppet/face/secret_agent.rb
@@ -0,0 +1,39 @@
+require 'puppet/face'
+
+Puppet::Face.define(:secret_agent, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Provides agent-like behavior, with no plugin downloading or reporting"
+ description <<-EOT
+ This face currently functions as a proof of concept, demonstrating how
+ Faces allows the separation of application logic from Puppet's internal
+ systems; compare the code for puppet agent. It will eventually replace
+ puppet agent entirely, and can provide a template for users who wish to
+ implement agent-like functionality with drastically different
+ application logic.
+ EOT
+
+ action(:synchronize) do
+ summary "Retrieve and apply a catalog from the puppet master"
+ description <<-EOT
+ This action mimics the behavior of the puppet agent application. It does
+ not currently daemonize, but can download plugins, submit facts,
+ retrieve and apply a catalog, and submit a report to the puppet master.
+ EOT
+
+ when_invoked do |options|
+ Puppet::Face[:plugin, '0.0.1'].download
+
+ Puppet::Face[:facts, '0.0.1'].upload
+
+ Puppet::Face[:catalog, '0.0.1'].download
+
+ report = Puppet::Face[:catalog, '0.0.1'].apply
+
+ Puppet::Face[:report, '0.0.1'].submit(report)
+
+ return report
+ end
+ end
+end
diff --git a/lib/puppet/face/status.rb b/lib/puppet/face/status.rb
index 7085e7cd7..6a29debdd 100644
--- a/lib/puppet/face/status.rb
+++ b/lib/puppet/face/status.rb
@@ -1,4 +1,30 @@
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
-Puppet::Face::Indirector.define(:status, '0.0.1') do
+Puppet::Indirector::Face.define(:status, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "View puppet server status"
+ description <<-EOT
+ This subcommand is only useful for determining whether a puppet master
+ server (or an agent node, if puppet was started with the `--listen`
+ option) is responding to requests.
+
+ Only the `find` action is valid. If the server is responding to
+ requests, `find` will retrieve a status object; if not, the connection
+ will be refused. When invoked with the `local` terminus, `find` will
+ always return true.
+
+ If you wish to query a server other than the master configured in
+ puppet.conf, you must set the `--server` and `--masterport` options on
+ the command line.
+ EOT
+ notes <<-EOT
+ This is an indirector face, which exposes find, search, save, and
+ destroy actions for an indirected subsystem of Puppet. Valid terminuses
+ for this face include:
+
+ * `local`
+ * `rest`
+ EOT
end
diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb
index c020f036d..f29f70a53 100644
--- a/lib/puppet/file_serving/fileset.rb
+++ b/lib/puppet/file_serving/fileset.rb
@@ -59,7 +59,7 @@ class Puppet::FileServing::Fileset
end
def initialize(path, options = {})
- path = path.chomp(File::SEPARATOR)
+ path = path.chomp(File::SEPARATOR) unless path == File::SEPARATOR
raise ArgumentError.new("Fileset paths must be fully qualified") unless File.expand_path(path) == path
@path = path
diff --git a/lib/puppet/face/indirector.rb b/lib/puppet/indirector/face.rb
index 6c7708b51..1371c647e 100644
--- a/lib/puppet/face/indirector.rb
+++ b/lib/puppet/indirector/face.rb
@@ -1,10 +1,24 @@
-require 'puppet'
require 'puppet/face'
-class Puppet::Face::Indirector < Puppet::Face
+class Puppet::Indirector::Face < 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."
+ summary "The indirector terminus to use for this action"
+ description <<-EOT
+Indirector faces expose indirected subsystems of Puppet. These
+subsystems are each able to retrieve and alter a specific type of data
+(with the familiar actions of `find`, `search`, `save`, and `destroy`)
+from an arbitrary number of pluggable backends. In Puppet parlance,
+these backends are called terminuses.
+
+Almost all indirected subsystems have a `rest` terminus that interacts
+with the puppet master's data. Most of them have additional terminuses
+for various local data models, which are in turn used by the indirected
+subsystem on the puppet master whenever it receives a remote request.
+
+The terminus for an action is often determined by context, but
+occasionally needs to be set explicitly. See the "Notes" section of this
+face's manpage for more details.
+ EOT
before_action do |action, args, options|
set_terminus(options[:terminus])
@@ -23,11 +37,9 @@ that we should describe in this file somehow."
Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect { |t| t.to_s }.sort
end
- def call_indirection_method(method, *args)
- options = args.last
-
+ def call_indirection_method(method, key, options)
begin
- result = indirection.__send__(method, *args)
+ result = indirection.__send__(method, key, options)
rescue => detail
puts detail.backtrace if Puppet[:trace]
raise "Could not call '#{method}' on '#{indirection_name}': #{detail}"
@@ -37,23 +49,38 @@ that we should describe in this file somehow."
end
action :destroy do
- when_invoked { |*args| call_indirection_method(:destroy, *args) }
+ summary "Delete an object"
+ when_invoked { |key, options| call_indirection_method(:destroy, key, options) }
end
action :find do
- when_invoked { |*args| call_indirection_method(:find, *args) }
+ summary "Retrieve an object by name"
+ when_invoked { |key, options| call_indirection_method(:find, key, options) }
end
action :save do
- when_invoked { |*args| call_indirection_method(:save, *args) }
+ summary "Create or modify an object"
+ notes <<-EOT
+ Save actions cannot currently be invoked from the command line, and are
+ for API use only.
+ EOT
+ when_invoked { |key, options| call_indirection_method(:save, key, options) }
end
action :search do
- when_invoked { |*args| call_indirection_method(:search, *args) }
+ summary "Search for an object"
+ when_invoked { |key, options| call_indirection_method(:search, key, options) }
end
# Print the configuration for the current terminus class
action :info do
+ summary "Print the default terminus class for this face"
+ description <<-EOT
+ TK So this is per-face, right? No way to tell what the default terminus
+ is per-action, for subsystems that switch to REST for save but query
+ locally for find?
+ EOT
+
when_invoked do |*args|
if t = indirection.terminus_class
puts "Run mode '#{Puppet.run_mode.name}': #{t}"
diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb
index 1918a3fb5..fd8d654dd 100644
--- a/lib/puppet/indirector/request.rb
+++ b/lib/puppet/indirector/request.rb
@@ -14,51 +14,6 @@ class Puppet::Indirector::Request
OPTION_ATTRIBUTES = [:ip, :node, :authenticated, :ignore_terminus, :ignore_cache, :instance, :environment]
- def self.from_pson(json)
- raise ArgumentError, "No indirection name provided in json data" unless indirection_name = json['type']
- raise ArgumentError, "No method name provided in json data" unless method = json['method']
- raise ArgumentError, "No key provided in json data" unless key = json['key']
-
- request = new(indirection_name, method, key, json['attributes'])
-
- if instance = json['instance']
- klass = Puppet::Indirector::Indirection.instance(request.indirection_name).model
- if instance.is_a?(klass)
- request.instance = instance
- else
- request.instance = klass.from_pson(instance)
- end
- end
-
- request
- end
-
- def to_pson(*args)
- result = {
- 'document_type' => 'Puppet::Indirector::Request',
- 'data' => {
- 'type' => indirection_name,
- 'method' => method,
- 'key' => key
- }
- }
- data = result['data']
- attributes = {}
- OPTION_ATTRIBUTES.each do |key|
- next unless value = send(key)
- attributes[key] = value
- end
-
- options.each do |opt, value|
- attributes[opt] = value
- end
-
- data['attributes'] = attributes unless attributes.empty?
- data['instance'] = instance if instance
-
- result.to_pson(*args)
- end
-
# Is this an authenticated request?
def authenticated?
# Double negative, so we just get true or false
@@ -106,11 +61,9 @@ class Puppet::Indirector::Request
self.indirection_name = indirection_name
self.method = method
- options = options.inject({}) { |hash, ary| hash[ary[0].to_sym] = ary[1]; hash }
-
set_attributes(options)
- @options = options
+ @options = options.inject({}) { |hash, ary| hash[ary[0].to_sym] = ary[1]; hash }
if key_or_instance.is_a?(String) || key_or_instance.is_a?(Symbol)
key = key_or_instance
@@ -200,7 +153,7 @@ class Puppet::Indirector::Request
def set_attributes(options)
OPTION_ATTRIBUTES.each do |attribute|
- if options.include?(attribute.to_sym)
+ if options.include?(attribute)
send(attribute.to_s + "=", options[attribute])
options.delete(attribute)
end
diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb
index ced00863d..10e2ec8d7 100644
--- a/lib/puppet/interface.rb
+++ b/lib/puppet/interface.rb
@@ -1,7 +1,11 @@
require 'puppet'
require 'puppet/util/autoload'
+require 'puppet/interface/documentation'
+require 'prettyprint'
class Puppet::Interface
+ include FullDocs
+
require 'puppet/interface/face_collection'
require 'puppet/interface/action_manager'
@@ -65,27 +69,33 @@ class Puppet::Interface
Puppet.warning("set_default_format is deprecated (and ineffective); use render_as on your actions instead.")
end
+
########################################################################
# Documentation. We currently have to rewrite both getters because we share
# the same instance between build-time and the runtime instance. When that
# splits out this should merge into a module that both the action and face
# include. --daniel 2011-04-17
- attr_accessor :summary, :description
- def summary(value = nil)
- self.summary = value unless value.nil?
- @summary
- end
- def summary=(value)
- value = value.to_s
- value =~ /\n/ and
- raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead."
-
- @summary = value
- end
-
- def description(value = nil)
- self.description = value unless value.nil?
- @description
+ def synopsis
+ output = PrettyPrint.format do |s|
+ s.text("puppet #{name} <action>")
+ s.breakable
+
+ options.each do |option|
+ option = get_option(option)
+ wrap = option.required? ? %w{ < > } : %w{ [ ] }
+
+ s.group(0, *wrap) do
+ option.optparse.each do |item|
+ unless s.current_group.first?
+ s.breakable
+ s.text '|'
+ s.breakable
+ end
+ s.text item
+ end
+ end
+ end
+ end
end
@@ -97,9 +107,15 @@ class Puppet::Interface
raise ArgumentError, "Cannot create face #{name.inspect} with invalid version number '#{version}'!"
end
- @name = Puppet::Interface::FaceCollection.underscorize(name)
+ @name = Puppet::Interface::FaceCollection.underscorize(name)
@version = version
+ # The few bits of documentation we actually demand. The default license
+ # is a favour to our end users; if you happen to get that in a core face
+ # report it as a bug, please. --daniel 2011-04-26
+ @authors = []
+ @license = 'All Rights Reserved'
+
instance_eval(&block) if block_given?
end
@@ -139,12 +155,12 @@ class Puppet::Interface
action.get_option(name).__decoration_name(type)
end
+ methods.reverse! if type == :after
+
+ # Exceptions here should propagate up; this implements a hook we can use
+ # reasonably for option validation.
methods.each do |hook|
- begin
- respond_to? hook and self.__send__(hook, action, passed_args, passed_options)
- rescue => e
- Puppet.warning("invoking #{action} #{type} hook: #{e}")
- end
+ respond_to? hook and self.__send__(hook, action, passed_args, passed_options)
end
end
diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb
index f8eef69b1..114e5341b 100644
--- a/lib/puppet/interface/action.rb
+++ b/lib/puppet/interface/action.rb
@@ -1,15 +1,28 @@
-# -*- coding: utf-8 -*-
require 'puppet/interface'
-require 'puppet/interface/option'
+require 'puppet/interface/documentation'
+require 'prettyprint'
class Puppet::Interface::Action
+ extend Puppet::Interface::DocGen
+ include Puppet::Interface::FullDocs
+
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
+
+ # The few bits of documentation we actually demand. The default license
+ # is a favour to our end users; if you happen to get that in a core face
+ # report it as a bug, please. --daniel 2011-04-26
+ @authors = []
+ @license = 'All Rights Reserved'
+
attrs.each do |k, v| send("#{k}=", v) end
- @options = {}
+ # @options collects the added options in the order they're declared.
+ # @options_hash collects the options keyed by alias for quick lookups.
+ @options = []
+ @options_hash = {}
@when_rendering = {}
end
@@ -30,8 +43,32 @@ class Puppet::Interface::Action
!!@default
end
- attr_accessor :summary
-
+ ########################################################################
+ # Documentation...
+ attr_doc :returns
+ def synopsis
+ output = PrettyPrint.format do |s|
+ s.text("puppet #{@face.name}")
+ s.text(" #{name}") unless default?
+ s.breakable
+
+ options.each do |option|
+ option = get_option(option)
+ wrap = option.required? ? %w{ < > } : %w{ [ ] }
+
+ s.group(0, *wrap) do
+ option.optparse.each do |item|
+ unless s.current_group.first?
+ s.breakable
+ s.text '|'
+ s.breakable
+ end
+ s.text item
+ end
+ end
+ end
+ end
+ end
########################################################################
# Support for rendering formats and all.
@@ -39,8 +76,15 @@ class Puppet::Interface::Action
unless type.is_a? Symbol
raise ArgumentError, "The rendering format must be a symbol, not #{type.class.name}"
end
- return unless @when_rendering.has_key? type
- return @when_rendering[type].bind(@face)
+ # Do we have a rendering hook for this name?
+ return @when_rendering[type].bind(@face) if @when_rendering.has_key? type
+
+ # How about by another name?
+ alt = type.to_s.sub(/^to_/, '').to_sym
+ return @when_rendering[alt].bind(@face) if @when_rendering.has_key? alt
+
+ # Guess not, nothing to run.
+ return nil
end
def set_rendering_method_for(type, proc)
unless proc.is_a? Proc
@@ -83,18 +127,6 @@ class Puppet::Interface::Action
########################################################################
- # Documentation stuff, whee!
- attr_accessor :summary, :description
- def summary=(value)
- value = value.to_s
- value =~ /\n/ and
- raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead."
-
- @summary = value
- end
-
-
- ########################################################################
# 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
@@ -148,11 +180,13 @@ class Puppet::Interface::Action
# 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
+ attr_reader :positional_arg_count
+ attr_accessor :when_invoked
def when_invoked=(block)
internal_name = "#{@name} implementation, required on Ruby 1.8".to_sym
- arity = block.arity
+ arity = @positional_arg_count = block.arity
if arity == 0 then
# This will never fire on 1.8.7, which treats no arguments as "*args",
# but will on 1.9.2, which treats it as "no arguments". Which bites,
@@ -195,9 +229,11 @@ WRAPPER
if @face.is_a?(Class)
@face.class_eval do eval wrapper, nil, file, line end
@face.define_method(internal_name, &block)
+ @when_invoked = @face.instance_method(name)
else
@face.instance_eval do eval wrapper, nil, file, line end
@face.meta_def(internal_name, &block)
+ @when_invoked = @face.method(name).unbind
end
end
@@ -211,7 +247,8 @@ WRAPPER
end
option.aliases.each do |name|
- @options[name] = option
+ @options << name
+ @options_hash[name] = option
end
option
@@ -223,15 +260,15 @@ WRAPPER
end
def option?(name)
- @options.include? name.to_sym
+ @options_hash.include? name.to_sym
end
def options
- (@options.keys + @face.options).sort
+ @face.options + @options
end
def get_option(name, with_inherited_options = true)
- option = @options[name.to_sym]
+ option = @options_hash[name.to_sym]
if option.nil? and with_inherited_options
option = @face.get_option(name)
end
@@ -239,12 +276,26 @@ WRAPPER
end
def validate_args(args)
+ # Check for multiple aliases for the same option...
+ args.last.keys.each do |name|
+ # #7290: If this isn't actually an option, ignore it for now. We should
+ # probably fail, but that wasn't our API, and I don't want to perturb
+ # behaviour this late in the RC cycle. --daniel 2011-04-29
+ if option = get_option(name) then
+ overlap = (option.aliases & args.last.keys)
+ unless overlap.length == 1 then
+ raise ArgumentError, "Multiple aliases for the same option passed: #{overlap.join(', ')}"
+ end
+ end
+ end
+
+ # Check for missing mandatory options.
required = options.map do |name|
get_option(name)
end.select(&:required?).collect(&:name) - args.last.keys
return if required.empty?
- raise ArgumentError, "missing required options (#{required.join(', ')})"
+ raise ArgumentError, "The following options are required: #{required.join(', ')}"
end
########################################################################
diff --git a/lib/puppet/interface/action_builder.rb b/lib/puppet/interface/action_builder.rb
index fd8b0856f..ba5531f1d 100644
--- a/lib/puppet/interface/action_builder.rb
+++ b/lib/puppet/interface/action_builder.rb
@@ -9,13 +9,6 @@ class Puppet::Interface::ActionBuilder
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
@@ -55,7 +48,7 @@ class Puppet::Interface::ActionBuilder
def render_as(value = nil)
value.nil? and raise ArgumentError, "You must give a rendering format to render_as"
- formats = Puppet::Network::FormatHandler.formats << :for_humans
+ formats = Puppet::Network::FormatHandler.formats
unless formats.include? value
raise ArgumentError, "#{value.inspect} is not a valid rendering format: #{formats.sort.join(", ")}"
end
@@ -66,18 +59,26 @@ class Puppet::Interface::ActionBuilder
# Metaprogram the simple DSL from the target class.
Puppet::Interface::Action.instance_methods.grep(/=$/).each do |setter|
next if setter =~ /^=/
- dsl = setter.sub(/=$/, '')
+ property = setter.sub(/=$/, '')
- unless private_instance_methods.include? dsl
+ unless public_instance_methods.include? property
# Using eval because the argument handling semantics are less awful than
# when we use the define_method/block version. The later warns on older
# Ruby versions if you pass the wrong number of arguments, but carries
# on, which is totally not what we want. --daniel 2011-04-18
- eval <<METHOD
-def #{dsl}(value)
- @action.#{dsl} = value
-end
-METHOD
+ eval <<-METHOD
+ def #{property}(value)
+ @action.#{property} = value
+ end
+ METHOD
end
end
+
+ private
+ def initialize(face, name, &block)
+ @face = face
+ @action = Puppet::Interface::Action.new(face, name)
+ instance_eval(&block)
+ @action.when_invoked or raise ArgumentError, "actions need to know what to do when_invoked; please add the block"
+ end
end
diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb
index 440be2d1b..c5eb8e08a 100644
--- a/lib/puppet/interface/action_manager.rb
+++ b/lib/puppet/interface/action_manager.rb
@@ -1,9 +1,11 @@
-require 'puppet/interface/action_builder'
+require 'puppet/interface/action'
module Puppet::Interface::ActionManager
# Declare that this app can take a specific action, and provide
# the code to do so.
def action(name, &block)
+ require 'puppet/interface/action_builder'
+
@actions ||= {}
@default_action ||= nil
raise "Action #{name} already defined for #{self}" if action?(name)
diff --git a/lib/puppet/interface/documentation.rb b/lib/puppet/interface/documentation.rb
new file mode 100644
index 000000000..48e9a8b1a
--- /dev/null
+++ b/lib/puppet/interface/documentation.rb
@@ -0,0 +1,197 @@
+# This isn't usable outside Puppet::Interface; don't load it alone.
+class Puppet::Interface
+ module DocGen
+ def self.strip_whitespace(text)
+ text.gsub!(/[ \t\f]+$/, '')
+
+ # We need to identify an indent: the minimum number of whitespace
+ # characters at the start of any line in the text.
+ #
+ # Using split rather than each_line is because the later only takes a
+ # block on Ruby 1.8.5 / Centos, and we support that. --daniel 2011-05-03
+ indent = text.split(/\n/).map {|x| x.index(/[^\s]/) }.compact.min
+
+ if indent > 0 then
+ text.gsub!(/^[ \t\f]{0,#{indent}}/, '')
+ end
+
+ return text
+ end
+
+ # The documentation attributes all have some common behaviours; previously
+ # we open-coded them across the set of six things, but that seemed
+ # wasteful - especially given that they were literally the same, and had
+ # the same bug hidden in them.
+ #
+ # This feels a bit like overkill, but at least the common code is common
+ # now. --daniel 2011-04-29
+ def attr_doc(name, &validate)
+ # Now, which form of the setter do we want, validated or not?
+ get_arg = "value.to_s"
+ if validate
+ define_method(:"_validate_#{name}", validate)
+ get_arg = "_validate_#{name}(#{get_arg})"
+ end
+
+ # We use module_eval, which I don't like much, because we can't have an
+ # argument to a block with a default value in Ruby 1.8, and I don't like
+ # the side-effects (eg: no argument count validation) of using blocks
+ # without as metheds. When we are 1.9 only (hah!) you can totally
+ # replace this with some up-and-up define_method. --daniel 2011-04-29
+ module_eval(<<-EOT, __FILE__, __LINE__ + 1)
+ def #{name}(value = nil)
+ self.#{name} = value unless value.nil?
+ @#{name}
+ end
+
+ def #{name}=(value)
+ @#{name} = Puppet::Interface::DocGen.strip_whitespace(#{get_arg})
+ end
+ EOT
+ end
+ end
+
+ module TinyDocs
+ extend Puppet::Interface::DocGen
+
+ attr_doc :summary do |value|
+ value =~ /\n/ and
+ raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead."
+ value
+ end
+
+ attr_doc :description
+ end
+
+ module FullDocs
+ extend Puppet::Interface::DocGen
+ include TinyDocs
+
+ attr_doc :examples
+ attr_doc :notes
+ attr_doc :license
+
+ attr_doc :short_description
+ def short_description(value = nil)
+ self.short_description = value unless value.nil?
+ if @short_description.nil? then
+ return nil if @description.nil?
+ lines = @description.split("\n")
+ grab = [5, lines.index('') || 5].min
+ @short_description = lines[0, grab].join("\n")
+ end
+ @short_description
+ end
+
+ def author(value = nil)
+ unless value.nil? then
+ unless value.is_a? String
+ raise ArgumentError, 'author must be a string; use multiple statements for multiple authors'
+ end
+
+ if value =~ /\n/ then
+ raise ArgumentError, 'author should be a single line; use multiple statements for multiple authors'
+ end
+ @authors.push(Puppet::Interface::DocGen.strip_whitespace(value))
+ end
+ @authors.empty? ? nil : @authors.join("\n")
+ end
+ def authors
+ @authors
+ end
+ def author=(value)
+ if Array(value).any? {|x| x =~ /\n/ } then
+ raise ArgumentError, 'author should be a single line; use multiple statements'
+ end
+ @authors = Array(value).map{|x| Puppet::Interface::DocGen.strip_whitespace(x) }
+ end
+ alias :authors= :author=
+
+ def copyright(owner = nil, years = nil)
+ if years.nil? and not owner.nil? then
+ raise ArgumentError, 'copyright takes the owners names, then the years covered'
+ end
+ self.copyright_owner = owner unless owner.nil?
+ self.copyright_years = years unless years.nil?
+
+ if self.copyright_years or self.copyright_owner then
+ "Copyright #{self.copyright_years} by #{self.copyright_owner}"
+ else
+ "Unknown copyright owner and years."
+ end
+ end
+
+ attr_accessor :copyright_owner
+ def copyright_owner=(value)
+ case value
+ when String then @copyright_owner = value
+ when Array then @copyright_owner = value.join(", ")
+ else
+ raise ArgumentError, "copyright owner must be a string or an array of strings"
+ end
+ @copyright_owner
+ end
+
+ attr_accessor :copyright_years
+ def copyright_years=(value)
+ years = munge_copyright_year value
+ years = (years.is_a?(Array) ? years : [years]).
+ sort_by do |x| x.is_a?(Range) ? x.first : x end
+
+ @copyright_years = years.map do |year|
+ if year.is_a? Range then
+ "#{year.first}-#{year.last}"
+ else
+ year
+ end
+ end.join(", ")
+ end
+
+ def munge_copyright_year(input)
+ case input
+ when Range then input
+ when Integer then
+ if input < 1970 then
+ fault = "before 1970"
+ elsif input > (future = Time.now.year + 2) then
+ fault = "after #{future}"
+ end
+ if fault then
+ raise ArgumentError, "copyright with a year #{fault} is very strange; did you accidentally add or subtract two years?"
+ end
+
+ input
+
+ when String then
+ input.strip.split(/,/).map do |part|
+ part = part.strip
+ if part =~ /^\d+$/ then
+ part.to_i
+ elsif found = part.split(/-/) then
+ unless found.length == 2 and found.all? {|x| x.strip =~ /^\d+$/ }
+ raise ArgumentError, "#{part.inspect} is not a good copyright year or range"
+ end
+ Range.new(found[0].to_i, found[1].to_i)
+ else
+ raise ArgumentError, "#{part.inspect} is not a good copyright year or range"
+ end
+ end
+
+ when Array then
+ result = []
+ input.each do |item|
+ item = munge_copyright_year item
+ if item.is_a? Array
+ result.concat item
+ else
+ result << item
+ end
+ end
+ result
+
+ else
+ raise ArgumentError, "#{input.inspect} is not a good copyright year, set, or range"
+ end
+ end
+ end
+end
diff --git a/lib/puppet/interface/face_collection.rb b/lib/puppet/interface/face_collection.rb
index 6e6afc545..12d3c56b1 100644
--- a/lib/puppet/interface/face_collection.rb
+++ b/lib/puppet/interface/face_collection.rb
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
require 'puppet/interface'
module Puppet::Interface::FaceCollection
@@ -10,21 +9,12 @@ module Puppet::Interface::FaceCollection
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
+ Dir.glob("#{dir}/puppet/face/*.rb").
+ collect {|f| File.basename(f, '.rb') }.
+ each {|name| self[name, :current] }
end
end
- return @faces.keys.select {|name| @faces[name].length > 0 }
+ @faces.keys.select {|name| @faces[name].length > 0 }
end
def self.validate_version(version)
@@ -124,6 +114,10 @@ module Puppet::Interface::FaceCollection
rescue LoadError => e
raise unless e.message =~ %r{-- puppet/face/#{name}$}
# ...guess we didn't find the file; return a much better problem.
+ rescue SyntaxError => e
+ raise unless e.message =~ %r{puppet/face/#{name}\.rb:\d+: }
+ Puppet.err "Failed to load face #{name}:\n#{e}"
+ # ...but we just carry on after complaining.
end
return get_face(name, version)
diff --git a/lib/puppet/interface/option.rb b/lib/puppet/interface/option.rb
index f4c56cb2c..b68bdeb12 100644
--- a/lib/puppet/interface/option.rb
+++ b/lib/puppet/interface/option.rb
@@ -1,4 +1,10 @@
+require 'puppet/interface'
+
class Puppet::Interface::Option
+ include Puppet::Interface::TinyDocs
+ # For compatibility, deprecated, and should go fairly soon...
+ ['', '='].each { |x| alias :"desc#{x}" :"description#{x}" }
+
def initialize(parent, *declaration, &block)
@parent = parent
@optparse = []
@@ -78,7 +84,7 @@ class Puppet::Interface::Option
end
attr_reader :parent, :name, :aliases, :optparse
- attr_accessor :required, :desc
+ attr_accessor :required
attr_accessor :before_action
def before_action=(proc)
diff --git a/lib/puppet/interface/option_manager.rb b/lib/puppet/interface/option_manager.rb
index d42359c07..326a91d92 100644
--- a/lib/puppet/interface/option_manager.rb
+++ b/lib/puppet/interface/option_manager.rb
@@ -8,6 +8,11 @@ module Puppet::Interface::OptionManager
end
def add_option(option)
+ # @options collects the added options in the order they're declared.
+ # @options_hash collects the options keyed by alias for quick lookups.
+ @options ||= []
+ @options_hash ||= {}
+
option.aliases.each do |name|
if conflict = get_option(name) then
raise ArgumentError, "Option #{option} conflicts with existing option #{conflict}"
@@ -21,25 +26,30 @@ module Puppet::Interface::OptionManager
end
end
- option.aliases.each { |name| @options[name] = option }
- option
+ option.aliases.each do |name|
+ @options << name
+ @options_hash[name] = option
+ end
+
+ return option
end
def options
- @options ||= {}
- result = @options.keys
+ result = (@options ||= [])
if self.is_a?(Class) and superclass.respond_to?(:options)
- result += superclass.options
+ result = superclass.options + result
elsif self.class.respond_to?(:options)
- result += self.class.options
+ result = self.class.options + result
end
- result.sort
+
+ return result
end
def get_option(name, with_inherited_options = true)
- @options ||= {}
- result = @options[name.to_sym]
+ @options_hash ||= {}
+
+ result = @options_hash[name.to_sym]
if result.nil? and with_inherited_options then
if self.is_a?(Class) and superclass.respond_to?(:get_option)
result = superclass.get_option(name)
@@ -47,6 +57,7 @@ module Puppet::Interface::OptionManager
result = self.class.get_option(name)
end
end
+
return result
end
diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb
index 4ca3240d4..082c83ee3 100644
--- a/lib/puppet/network/formats.rb
+++ b/lib/puppet/network/formats.rb
@@ -160,3 +160,39 @@ end
# This is really only ever going to be used for Catalogs.
Puppet::Network::FormatHandler.create_serialized_formats(:dot, :required_methods => [:render_method])
+
+
+Puppet::Network::FormatHandler.create(:console,
+ :mime => 'text/x-console-text',
+ :weight => 0) do
+ def json
+ @json ||= Puppet::Network::FormatHandler.format(:pson)
+ end
+
+ def render(datum)
+ # String to String
+ return datum if datum.is_a? String
+ return datum if datum.is_a? Numeric
+
+ # Simple hash to table
+ if datum.is_a? Hash and datum.keys.all? { |x| x.is_a? String or x.is_a? Numeric }
+ output = ''
+ column_a = datum.map do |k,v| k.to_s.length end.max + 2
+ column_b = 79 - column_a
+ datum.sort_by { |k,v| k.to_s } .each do |key, value|
+ output << key.to_s.ljust(column_a)
+ output << json.render(value).
+ chomp.gsub(/\n */) { |x| x + (' ' * column_a) }
+ output << "\n"
+ end
+ return output
+ end
+
+ # ...or pretty-print the inspect outcome.
+ return json.render(datum)
+ end
+
+ def render_multiple(data)
+ data.collect(&:render).join("\n")
+ end
+end
diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb
index 61307f01e..388d54961 100644
--- a/lib/puppet/network/http/api/v1.rb
+++ b/lib/puppet/network/http/api/v1.rb
@@ -30,7 +30,7 @@ module Puppet::Network::HTTP::API::V1
method = indirection_method(http_method, indirection)
- params[:environment] = environment
+ params[:environment] = Puppet::Node::Environment.new(environment)
raise ArgumentError, "No request key specified in #{uri}" if key == "" or key.nil?
diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb
index cf76978fe..dfe8f85c4 100644
--- a/lib/puppet/network/rest_authconfig.rb
+++ b/lib/puppet/network/rest_authconfig.rb
@@ -8,6 +8,7 @@ module Puppet
DEFAULT_ACL = [
{ :acl => "~ ^\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true },
+ { :acl => "~ ^\/node\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true },
# this one will allow all file access, and thus delegate
# to fileserver.conf
{ :acl => "/file" },
diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb
index 4bd4d1de6..5b0a98615 100644
--- a/lib/puppet/node.rb
+++ b/lib/puppet/node.rb
@@ -20,29 +20,6 @@ class Puppet::Node
attr_accessor :name, :classes, :source, :ipaddress, :parameters
attr_reader :time
- def self.from_pson(pson)
- raise ArgumentError, "No name provided in pson data" unless name = pson['name']
-
- node = new(name)
- node.classes = pson['classes']
- node.parameters = pson['parameters']
- node.environment = pson['environment']
- node
- end
-
- def to_pson(*args)
- result = {
- 'document_type' => "Puppet::Node",
- 'data' => {}
- }
- result['data']['name'] = name
- result['data']['classes'] = classes unless classes.empty?
- result['data']['parameters'] = parameters unless parameters.empty?
- result['data']['environment'] = environment.name
-
- result.to_pson(*args)
- end
-
def environment
return super if @environment
diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb
index 2ff7156c8..577b62b62 100755
--- a/lib/puppet/node/facts.rb
+++ b/lib/puppet/node/facts.rb
@@ -61,22 +61,18 @@ class Puppet::Node::Facts
def self.from_pson(data)
result = new(data['name'], data['values'])
- result.timestamp = Time.parse(data['timestamp']) if data['timestamp']
- result.expiration = Time.parse(data['expiration']) if data['expiration']
+ result.timestamp = Time.parse(data['timestamp'])
+ result.expiration = Time.parse(data['expiration'])
result
end
def to_pson(*args)
- result = {
- 'document_type' => "Puppet::Node::Facts",
- 'data' => {}
- }
-
- result['data']['name'] = name
- result['data']['expiration'] = expiration if expiration
- result['data']['timestamp'] = timestamp if timestamp
- result['data']['values'] = strip_internal
- result.to_pson(*args)
+ {
+ 'expiration' => expiration,
+ 'name' => name,
+ 'timestamp' => timestamp,
+ 'values' => strip_internal,
+ }.to_pson(*args)
end
# Add internal data to the facts for storage.
diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb
index 180a03dc9..27d75bf92 100644
--- a/lib/puppet/parser/templatewrapper.rb
+++ b/lib/puppet/parser/templatewrapper.rb
@@ -20,7 +20,7 @@ class Puppet::Parser::TemplateWrapper
def script_line
# find which line in the template (if any) we were called from
- caller.find { |l| l =~ /#{file}:/ }.first[/:(\d+):/,1]
+ (caller.find { |l| l =~ /#{file}:/ }||"")[/:(\d+):/,1]
end
# Should return true if a variable is defined, false if it is not
diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb
index a6cff9bdc..b742d283f 100644
--- a/lib/puppet/resource/catalog.rb
+++ b/lib/puppet/resource/catalog.rb
@@ -74,7 +74,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
raise ArgumentError, "Can only add objects that respond to :ref, not instances of #{resource.class}" unless resource.respond_to?(:ref)
fail_on_duplicate_type_and_title(resource)
title_key = title_key_for_ref(resource.ref)
-
+
@transient_resources << resource if applying?
@resource_table[title_key] = resource
@@ -339,8 +339,8 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
@relationship_graph
end
- # Impose our container information on another graph by using it
- # to replace any container vertices X with a pair of verticies
+ # Impose our container information on another graph by using it
+ # to replace any container vertices X with a pair of verticies
# { admissible_X and completed_X } such that that
#
# 0) completed_X depends on admissible_X
@@ -353,8 +353,8 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
# Note that this requires attention to the possible case of containers
# which contain or depend on other containers, but has the advantage
# that the number of new edges created scales linearly with the number
- # of contained verticies regardless of how containers are related;
- # alternatives such as replacing container-edges with content-edges
+ # of contained verticies regardless of how containers are related;
+ # alternatives such as replacing container-edges with content-edges
# scale as the product of the number of external dependences, which is
# to say geometrically in the case of nested / chained containers.
#
@@ -374,8 +374,8 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
admissible = Hash.new { |h,k| k }
completed = Hash.new { |h,k| k }
containers.each { |x|
- admissible[x] = whit_class.new(:name => "admissible_#{x.name}", :catalog => self)
- completed[x] = whit_class.new(:name => "completed_#{x.name}", :catalog => self)
+ admissible[x] = whit_class.new(:name => "admissible_#{x.ref}", :catalog => self)
+ completed[x] = whit_class.new(:name => "completed_#{x.ref}", :catalog => self)
}
#
# Implement the six requierments listed above
diff --git a/lib/puppet/status.rb b/lib/puppet/status.rb
index eecd0e18c..ea6a601f3 100644
--- a/lib/puppet/status.rb
+++ b/lib/puppet/status.rb
@@ -10,7 +10,7 @@ class Puppet::Status
@status = status || {"is_alive" => true}
end
- def to_pson
+ def to_pson(*args)
@status.to_pson
end
diff --git a/lib/puppet/transaction/event_manager.rb b/lib/puppet/transaction/event_manager.rb
index f5da870ed..8f1a695af 100644
--- a/lib/puppet/transaction/event_manager.rb
+++ b/lib/puppet/transaction/event_manager.rb
@@ -62,7 +62,18 @@ class Puppet::Transaction::EventManager
end
def queue_events_for_resource(source, target, callback, events)
- source.info "Scheduling #{callback} of #{target}"
+ whit = Puppet::Type.type(:whit)
+
+ # The message that a resource is refreshing the completed-whit for its own class
+ # is extremely counter-intuitive. Basically everything else is easy to understand,
+ # if you suppress the whit-lookingness of the whit resources
+ refreshing_c_whit = target.is_a?(whit) && target.name =~ /^completed_/
+
+ if refreshing_c_whit
+ source.debug "The container #{target} will propagate my #{callback} event"
+ else
+ source.info "Scheduling #{callback} of #{target}"
+ end
@event_queues[target] ||= {}
@event_queues[target][callback] ||= []
@@ -82,7 +93,9 @@ class Puppet::Transaction::EventManager
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"
+ if not resource.is_a?(Puppet::Type.type(:whit))
+ resource.notice "Triggered '#{callback}' from #{events.length} events"
+ end
return true
rescue => detail
resource.err "Failed to call #{callback}: #{detail}"
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index 767959308..572d5796d 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -451,8 +451,6 @@ module Puppet
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
diff --git a/lib/puppet/type/whit.rb b/lib/puppet/type/whit.rb
index 55ed0386e..4c77915b3 100644
--- a/lib/puppet/type/whit.rb
+++ b/lib/puppet/type/whit.rb
@@ -5,8 +5,14 @@ Puppet::Type.newtype(:whit) do
desc "The name of the whit, because it must have one."
end
+
+ # Hide the fact that we're a whit from logs
def to_s
- "(#{name})"
+ name.sub(/^completed_|^admissible_/, "")
+ end
+
+ def path
+ to_s
end
def refresh
diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb
index 714d03f74..8190f8ac1 100644
--- a/lib/puppet/util/command_line.rb
+++ b/lib/puppet/util/command_line.rb
@@ -65,14 +65,9 @@ module Puppet
# return to the caller. How strange we are. --daniel 2011-04-11
else
unless subcommand_name.nil? then
- puts "Error: Unknown Puppet subcommand #{subcommand_name}.\n"
+ puts "Error: Unknown Puppet subcommand '#{subcommand_name}'"
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
+ puts "See 'puppet help' for help on available puppet subcommands"
end
end
diff --git a/spec/fixtures/faulty_face/puppet/face/syntax.rb b/spec/fixtures/faulty_face/puppet/face/syntax.rb
new file mode 100644
index 000000000..3b1e36c3f
--- /dev/null
+++ b/spec/fixtures/faulty_face/puppet/face/syntax.rb
@@ -0,0 +1,8 @@
+Puppet::Face.define(:syntax, '1.0.0') do
+ action :foo do
+ when_invoked do |whom|
+ "hello, #{whom}"
+ end
+ # This 'end' is deliberately omitted, to induce a syntax error.
+ # Please don't fix that, as it is used for testing. --daniel 2011-05-02
+end
diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb
index df9b91608..c1e463033 100755
--- a/spec/integration/application/doc_spec.rb
+++ b/spec/integration/application/doc_spec.rb
@@ -36,8 +36,8 @@ describe Puppet::Application::Doc do
Puppet[:modulepath] = modules_dir
Puppet[:manifest] = site_file
puppet.options[:mode] = :rdoc
- puppet.expects(:exit).with(0)
- puppet.run_command
+
+ expect { puppet.run_command }.to exit_with 0
File.should be_exist('doc')
ensure
diff --git a/spec/integration/faces/documentation_spec.rb b/spec/integration/faces/documentation_spec.rb
new file mode 100755
index 000000000..9ddf2f1b3
--- /dev/null
+++ b/spec/integration/faces/documentation_spec.rb
@@ -0,0 +1,55 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
+describe "documentation of faces" do
+ it "should generate global help" do
+ help = nil
+ expect { help = Puppet::Face[:help, :current].help }.not_to raise_error
+ help.should be_an_instance_of String
+ help.length.should be > 200
+ end
+
+ ########################################################################
+ # Can we actually generate documentation for the face, and the actions it
+ # has? This avoids situations where the ERB template turns out to have a
+ # bug in it, triggered in something the user might do.
+ Puppet::Face.faces.sort.each do |face_name|
+ # REVISIT: We should walk all versions of the face here...
+ let :help do Puppet::Face[:help, :current] end
+
+ context "generating help" do
+ it "for #{face_name}" do
+ expect {
+ text = help.help(face_name)
+ text.should be_an_instance_of String
+ text.length.should be > 100
+ }.not_to raise_error
+ end
+
+ Puppet::Face[face_name, :current].actions.sort.each do |action_name|
+ it "for #{face_name}.#{action_name}" do
+ expect {
+ text = help.help(face_name, action_name)
+ text.should be_an_instance_of String
+ text.length.should be > 100
+ }.not_to raise_error
+ end
+ end
+ end
+
+ ########################################################################
+ # Ensure that we have authorship and copyright information in *our* faces;
+ # if you apply this to third party faces you might well be disappointed.
+ context "licensing of Puppet Labs face '#{face_name}'" do
+ subject { Puppet::Face[face_name, :current] }
+ its :license do should =~ /Apache\s*2/ end
+ its :copyright do should =~ /Puppet Labs/ end
+
+ # REVISIT: This is less that ideal, I think, but right now I am more
+ # comfortable watching us ship with some copyright than without any; we
+ # can redress that when it becomes appropriate. --daniel 2011-04-27
+ its :copyright do should =~ /2011/ end
+ end
+ end
+end
diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb
index 78d62fc51..0ff50f47c 100755
--- a/spec/integration/transaction_spec.rb
+++ b/spec/integration/transaction_spec.rb
@@ -275,7 +275,7 @@ describe Puppet::Transaction do
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",
+ :command => "/bin/mkdir /this/path/cannot/possibly/exist",
:title => "mkdir"
)
@@ -309,7 +309,7 @@ describe Puppet::Transaction do
)
exec = Puppet::Type.type(:exec).new(
- :command => "/bin/mkdir /this/path/cannot/possibly/exit",
+ :command => "/bin/mkdir /this/path/cannot/possibly/exist",
:title => "mkdir",
:notify => create_file1
)
diff --git a/spec/lib/puppet/face/basetest.rb b/spec/lib/puppet/face/basetest.rb
index e935161ae..9398f5b2d 100755
--- a/spec/lib/puppet/face/basetest.rb
+++ b/spec/lib/puppet/face/basetest.rb
@@ -1,2 +1,41 @@
require 'puppet/face'
-Puppet::Face.define(:basetest, '0.0.1')
+
+Puppet::Face.define(:basetest, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+ summary "This is just so tests don't fail"
+
+ option "--[no-]boolean"
+ option "--mandatory ARGUMENT"
+
+ action :foo do
+ option("--action")
+ when_invoked do |*args| args.length end
+ end
+
+ action :return_true do
+ summary "just returns true"
+ when_invoked do |options| true end
+ end
+
+ action :return_false do
+ summary "just returns false"
+ when_invoked do |options| false end
+ end
+
+ action :return_nil do
+ summary "just returns nil"
+ when_invoked do |options| nil end
+ end
+
+ action :raise do
+ summary "just raises an exception"
+ when_invoked do |options| raise ArgumentError, "your failure" end
+ end
+
+ action :with_s_rendering_hook do
+ summary "has a rendering hook for 's'"
+ when_invoked do |options| "this is not the hook you are looking for" end
+ when_rendering :s do |value| "you invoked the 's' rendering hook" end
+ end
+end
diff --git a/spec/lib/puppet/face/huzzah.rb b/spec/lib/puppet/face/huzzah.rb
index 3428c6816..6593d358a 100755
--- a/spec/lib/puppet/face/huzzah.rb
+++ b/spec/lib/puppet/face/huzzah.rb
@@ -1,5 +1,7 @@
require 'puppet/face'
Puppet::Face.define(:huzzah, '2.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
summary "life is a thing for celebration"
- action :bar do "is where beer comes from" end
+ script :bar do "is where beer comes from" end
end
diff --git a/spec/lib/puppet/face/version_matching.rb b/spec/lib/puppet/face/version_matching.rb
index bfd0013f7..52bc71dbd 100644
--- a/spec/lib/puppet/face/version_matching.rb
+++ b/spec/lib/puppet/face/version_matching.rb
@@ -4,6 +4,8 @@ require 'puppet/face'
# change this you need to ensure that is still correct. --daniel 2011-04-21
['1.0.0', '1.0.1', '1.1.0', '1.1.1', '2.0.0'].each do |version|
Puppet::Face.define(:version_matching, version) do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
summary "version matching face #{version}"
script :version do version end
end
diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb
new file mode 100644
index 000000000..77f580330
--- /dev/null
+++ b/spec/lib/puppet_spec/matchers.rb
@@ -0,0 +1,87 @@
+require 'stringio'
+
+########################################################################
+# Backward compatibility for Jenkins outdated environment.
+module RSpec
+ module Matchers
+ module BlockAliases
+ alias_method :to, :should unless method_defined? :to
+ alias_method :to_not, :should_not unless method_defined? :to_not
+ alias_method :not_to, :should_not unless method_defined? :not_to
+ end
+ end
+end
+
+
+########################################################################
+# Custom matchers...
+RSpec::Matchers.define :have_matching_element do |expected|
+ match do |actual|
+ actual.any? { |item| item =~ expected }
+ end
+end
+
+
+RSpec::Matchers.define :exit_with do |expected|
+ actual = nil
+ match do |block|
+ begin
+ block.call
+ rescue SystemExit => e
+ actual = e.status
+ end
+ actual and actual == expected
+ end
+ failure_message_for_should do |block|
+ "expected exit with code #{expected} but " +
+ (actual.nil? ? " exit was not called" : "we exited with #{actual} instead")
+ end
+ failure_message_for_should_not do |block|
+ "expected that exit would not be called with #{expected}"
+ end
+ description do
+ "expect exit with #{expected}"
+ end
+end
+
+
+RSpec::Matchers.define :have_printed do |expected|
+ match do |block|
+ $stderr = $stdout = StringIO.new
+
+ begin
+ block.call
+ ensure
+ $stdout.rewind
+ @actual = $stdout.read
+
+ $stdout = STDOUT
+ $stderr = STDERR
+ end
+
+ if @actual then
+ case expected
+ when String
+ @actual.include? expected
+ when Regexp
+ expected.match @actual
+ else
+ raise ArgumentError, "No idea how to match a #{@actual.class.name}"
+ end
+ end
+ end
+
+ failure_message_for_should do |actual|
+ if actual.nil? then
+ "expected #{expected.inspect}, but nothing was printed"
+ else
+ "expected #{expected.inspect} to be printed; got:\n#{actual}"
+ end
+ end
+
+ description do
+ "expect #{expected.inspect} to be printed"
+ end
+
+ diffable
+end
diff --git a/spec/shared_behaviours/an_indirector_face.rb b/spec/shared_behaviours/an_indirector_face.rb
new file mode 100644
index 000000000..cba74b696
--- /dev/null
+++ b/spec/shared_behaviours/an_indirector_face.rb
@@ -0,0 +1,6 @@
+shared_examples_for "an indirector face" do
+ [:find, :search, :save, :destroy, :info].each do |action|
+ it { should be_action action }
+ it { should respond_to action }
+ end
+end
diff --git a/spec/shared_behaviours/documentation_on_faces.rb b/spec/shared_behaviours/documentation_on_faces.rb
index 41b4015c9..3cfb178f7 100644
--- a/spec/shared_behaviours/documentation_on_faces.rb
+++ b/spec/shared_behaviours/documentation_on_faces.rb
@@ -1,34 +1,255 @@
# encoding: UTF-8
shared_examples_for "documentation on faces" do
- context "description" do
- describe "#summary" do
- it "should accept a summary" do
- text = "this is my summary"
- expect { subject.summary = text }.to_not raise_error
- subject.summary.should == text
+ defined?(Attrs) or
+ Attrs = [:summary, :description, :examples, :short_description, :notes, :author]
+
+ defined?(SingleLineAttrs) or
+ SingleLineAttrs = [:summary, :author]
+
+ # Simple, procedural tests that apply to a bunch of methods.
+ Attrs.each do |attr|
+ it "should accept a #{attr}" do
+ expect { subject.send("#{attr}=", "hello") }.not_to raise_error
+ subject.send(attr).should == "hello"
+ end
+
+ it "should accept a long (single line) value for #{attr}" do
+ text = "I never know when to stop with the word banana" + ("na" * 1000)
+ expect { subject.send("#{attr}=", text) }.to_not raise_error
+ subject.send(attr).should == text
+ end
+ end
+
+ Attrs.each do |getter|
+ setter = "#{getter}=".to_sym
+ context "#{getter}" do
+ it "should strip leading whitespace on a single line" do
+ subject.send(setter, " death to whitespace")
+ subject.send(getter).should == "death to whitespace"
+ end
+
+ it "should strip trailing whitespace on a single line" do
+ subject.send(setter, "death to whitespace ")
+ subject.send(getter).should == "death to whitespace"
+ end
+
+ it "should strip whitespace at both ends at once" do
+ subject.send(setter, " death to whitespace ")
+ subject.send(getter).should == "death to whitespace"
+ end
+
+ multiline_text = "with\nnewlines"
+ if SingleLineAttrs.include? getter then
+ it "should not accept multiline values" do
+ expect { subject.send(setter, multiline_text) }.
+ to raise_error ArgumentError, /#{getter} should be a single line/
+ subject.send(getter).should be_nil
+ end
+ else
+ it "should accept multiline values" do
+ expect { subject.send(setter, multiline_text) }.not_to raise_error
+ subject.send(getter).should == multiline_text
+ end
+
+ [1, 2, 4, 7, 25].each do |length|
+ context "#{length} chars indent" do
+ indent = ' ' * length
+
+ it "should strip leading whitespace on multiple lines" do
+ text = "this\nis\the\final\outcome"
+ subject.send(setter, text.gsub(/^/, indent))
+ subject.send(getter).should == text
+ end
+
+ it "should not remove formatting whitespace, only global indent" do
+ text = "this\n is\n the\n ultimate\ntest\n"
+ subject.send(setter, text.gsub(/^/, indent))
+ subject.send(getter).should == text
+ end
+ end
+ end
+
+ it "should strip whitespace with a blank line" do
+ subject.send(setter, " this\n\n should outdent\n")
+ subject.send(getter).should == "this\n\nshould outdent\n"
+ end
+ end
+ end
+ end
+
+ describe "#short_description" do
+ it "should return the set value if set after description" do
+ subject.description = "hello\ngoodbye"
+ subject.short_description = "whatever"
+ subject.short_description.should == "whatever"
+ end
+
+ it "should return the set value if set before description" do
+ subject.short_description = "whatever"
+ subject.description = "hello\ngoodbye"
+ subject.short_description.should == "whatever"
+ end
+
+ it "should return nothing if not set and no description" do
+ subject.short_description.should be_nil
+ end
+
+ it "should return the first paragraph of description if not set (where it is one line long)" do
+ subject.description = "hello"
+ subject.short_description.should == subject.description
+ end
+
+ it "should return the first paragraph of description if not set (where there is no paragraph break)" do
+ subject.description = "hello\ngoodbye"
+ subject.short_description.should == subject.description
+ end
+
+ it "should return the first paragraph of description if not set (where there is a paragraph break)" do
+ subject.description = "hello\ngoodbye\n\nmore\ntext\nhere\n\nfinal\nparagraph"
+ subject.short_description.should == "hello\ngoodbye"
+ end
+
+ it "should trim a very, very long first paragraph" do
+ line = "this is a very, very, very long long line full of text\n"
+ subject.description = line * 20 + "\n\nwhatever, dude."
+
+ subject.short_description.should == (line * 5).chomp
+ end
+ end
+
+ describe "multiple authors" do
+ authors = %w{John Paul George Ringo}
+
+ context "in the DSL" do
+ it "should support multiple authors" do
+
+ authors.each {|name| subject.author name }
+ subject.authors.should =~ authors
+
+ subject.author.should == authors.join("\n")
+ end
+
+ it "should reject author as an array" do
+ expect { subject.author ["Foo", "Bar"] }.
+ to raise_error ArgumentError, /author must be a string/
+ end
+ end
+
+ context "#author=" do
+ it "should accept a single name" do
+ subject.author = "Fred"
+ subject.author.should == "Fred"
+ end
+
+ it "should accept an array of names" do
+ subject.author = authors
+ subject.authors.should =~ authors
+ subject.author.should == authors.join("\n")
end
- it "should accept a long, long, long summary" do
- text = "I never know when to stop with the word banana" + ("na" * 1000)
- expect { subject.summary = text }.to_not raise_error
- subject.summary.should == text
+ it "should not append when set multiple times" do
+ subject.author = "Fred"
+ subject.author = "John"
+ subject.author.should == "John"
end
- it "should reject a summary with a newline" do
- expect { subject.summary = "with\nnewlines" }.
- to raise_error ArgumentError, /summary should be a single line/
+ it "should reject arrays with embedded newlines" do
+ expect { subject.author = ["Fred\nJohn"] }.
+ to raise_error ArgumentError, /author should be a single line/
end
end
+ end
+
+ describe "#license" do
+ it "should default to reserving rights" do
+ subject.license.should =~ /All Rights Reserved/
+ end
+
+ it "should accept an arbitrary license string on the object" do
+ subject.license = "foo"
+ subject.license.should == "foo"
+ end
+
+ it "should accept symbols to specify existing licenses..."
+ end
+
+ describe "#copyright" do
+ it "should fail with just a name" do
+ expect { subject.copyright("invalid") }.
+ to raise_error ArgumentError, /copyright takes the owners names, then the years covered/
+ end
+
+ [1997, "1997"].each do |year|
+ it "should accept an entity name and a #{year.class.name} year" do
+ subject.copyright("me", year)
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /#{year}/
+ end
+
+ it "should accept multiple entity names and a #{year.class.name} year" do
+ subject.copyright ["me", "you"], year
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /\byou\b/
+ subject.copyright.should =~ /#{year}/
+ end
+ end
+
+ ["1997-2003", "1997 - 2003", 1997..2003].each do |range|
+ it "should accept a #{range.class.name} range of years" do
+ subject.copyright("me", range)
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /1997-2003/
+ end
+
+ it "should accept a #{range.class.name} range of years" do
+ subject.copyright ["me", "you"], range
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /\byou\b/
+ subject.copyright.should =~ /1997-2003/
+ end
+ end
+
+ [[1997, 2003], ["1997", 2003], ["1997", "2003"]].each do |input|
+ it "should accept the set of years #{input.inspect} in an array" do
+ subject.copyright "me", input
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /1997, 2003/
+ end
+
+ it "should accept the set of years #{input.inspect} in an array" do
+ subject.copyright ["me", "you"], input
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /\byou\b/
+ subject.copyright.should =~ /1997, 2003/
+ end
+ end
+
+ it "should warn if someone does math accidentally on the range of years" do
+ expect { subject.copyright "me", 1997-2003 }.
+ to raise_error ArgumentError, /copyright with a year before 1970 is very strange; did you accidentally add or subtract two years\?/
+ end
+
+ it "should accept complex copyright years" do
+ years = [1997, 1999, 2000..2002, 2005].reverse
+ subject.copyright "me", years
+ subject.copyright.should =~ /\bme\b/
+ subject.copyright.should =~ /1997, 1999, 2000-2002, 2005/
+ end
+ end
+
+ # Things that are automatically generated.
+ [:name, :options, :synopsis].each do |attr|
+ describe "##{attr}" do
+ it "should not allow you to set #{attr}" do
+ subject.should_not respond_to :"#{attr}="
+ end
- describe "#description" do
- it "should accept a description" do
- subject.description = "hello"
- subject.description.should == "hello"
+ it "should have a #{attr}" do
+ subject.send(attr).should_not be_nil
end
- it "should accept a description with a newline" do
- subject.description = "hello \n my \n fine \n friend"
- subject.description.should == "hello \n my \n fine \n friend"
+ it "'s #{attr} should not be empty..." do
+ subject.send(attr).should_not == ''
end
end
end
diff --git a/spec/shared_behaviours/things_that_declare_options.rb b/spec/shared_behaviours/things_that_declare_options.rb
index 5300a159a..6e7056157 100755
--- a/spec/shared_behaviours/things_that_declare_options.rb
+++ b/spec/shared_behaviours/things_that_declare_options.rb
@@ -28,6 +28,8 @@ shared_examples_for "things that declare options" do
thing = add_options_to do
option "--foo" do
desc text
+ description text
+ summary text
end
end
@@ -37,9 +39,12 @@ shared_examples_for "things that declare options" do
it "should list all the options" do
thing = add_options_to do
option "--foo"
- option "--bar"
+ option "--bar", '-b'
+ option "-q", "--quux"
+ option "-f"
+ option "--baz"
end
- thing.options.should =~ [:foo, :bar]
+ thing.options.should == [:foo, :bar, :b, :q, :quux, :f, :baz]
end
it "should detect conflicts in long options" do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 01ffabc48..6b6b1c2fb 100755
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -17,9 +17,10 @@ end
require 'pathname'
require 'tmpdir'
-require 'lib/puppet_spec/verbose'
-require 'lib/puppet_spec/files'
-require 'lib/puppet_spec/fixtures'
+require 'puppet_spec/verbose'
+require 'puppet_spec/files'
+require 'puppet_spec/fixtures'
+require 'puppet_spec/matchers'
require 'monkey_patches/alias_should_to_must'
require 'monkey_patches/publicize_methods'
require 'monkey_patches/disable_signal_trap'
@@ -74,9 +75,3 @@ RSpec.configure do |config|
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/application/agent_spec.rb b/spec/unit/application/agent_spec.rb
index b30a8cc6c..2e946e6bb 100755
--- a/spec/unit/application/agent_spec.rb
+++ b/spec/unit/application/agent_spec.rb
@@ -253,19 +253,16 @@ describe Puppet::Application::Agent do
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
+ Puppet.settings.expects(:print_configs).returns true
+ expect { @puppetd.setup }.to exit_with 0
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)
+ expect { @puppetd.setup }.to exit_with 0
end
it "should set a central log destination with --centrallogs" do
@@ -346,17 +343,14 @@ describe Puppet::Application::Agent do
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)
+ expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0
end
end
it "should finally exit" do
- lambda { @puppetd.enable_disable_client(@agent) }.should raise_error(SystemExit)
+ expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0
end
end
@@ -410,7 +404,6 @@ describe Puppet::Application::Agent do
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
@@ -419,10 +412,7 @@ describe Puppet::Application::Agent do
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
+ expect { @puppetd.setup_listen }.to exit_with 14
end
it "should create a server to listen on at least the Runner handler" do
@@ -483,35 +473,27 @@ describe Puppet::Application::Agent 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
+ expect { @puppetd.onetime }.to exit_with 43
end
it "should setup traps" do
@daemon.expects(:set_signal_traps)
-
- @puppetd.onetime
+ expect { @puppetd.onetime }.to exit_with 0
end
it "should let the agent run" do
@agent.expects(:run).returns(:report)
-
- @puppetd.onetime
+ expect { @puppetd.onetime }.to exit_with 0
end
it "should finish by exiting with 0 error code" do
- @puppetd.expects(:exit).with(0)
-
- @puppetd.onetime
+ expect { @puppetd.onetime }.to exit_with 0
end
describe "and --detailed-exitcodes" do
@@ -523,18 +505,16 @@ describe Puppet::Application::Agent do
Puppet[:noop] = false
report = stub 'report', :exit_status => 666
@agent.stubs(:run).returns(report)
- @puppetd.expects(:exit).with(666)
- @puppetd.onetime
+ expect { @puppetd.onetime }.to exit_with 666
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
+ expect { @puppetd.onetime }.to exit_with 666
end
end
end
diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb
index ec3f083db..74c883a3e 100755
--- a/spec/unit/application/apply_spec.rb
+++ b/spec/unit/application/apply_spec.rb
@@ -93,18 +93,14 @@ describe Puppet::Application::Apply do
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
+ Puppet.settings.stubs(:print_configs?).returns true
+ Puppet.settings.expects(:print_configs).returns true
+ expect { @apply.setup }.to exit_with 0
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)
+ expect { @apply.setup }.to exit_with 1
end
it "should tell the report handler to cache locally as yaml" do
@@ -155,8 +151,6 @@ describe Puppet::Application::Apply do
@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
@@ -165,7 +159,7 @@ describe Puppet::Application::Apply do
@apply.options.stubs(:[]).with(:code).returns("code to run")
Puppet.expects(:[]=).with(:code,"code to run")
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should set the code to run from STDIN if no arguments" do
@@ -174,7 +168,7 @@ describe Puppet::Application::Apply do
Puppet.expects(:[]=).with(:code,"code to run")
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should set the manifest if a file is passed on command line and the file exists" do
@@ -183,7 +177,7 @@ describe Puppet::Application::Apply do
Puppet.expects(:[]=).with(:manifest,"site.pp")
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should raise an error if a file is passed on command line and the file does not exist" do
@@ -200,13 +194,13 @@ describe Puppet::Application::Apply do
Puppet.expects(:[]=).with(:manifest,"starwarsIV")
Puppet.expects(:warning).with('Only one file can be applied per run. Skipping starwarsI, starwarsII')
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should collect the node facts" do
Puppet::Node::Facts.indirection.expects(:find).returns(@facts)
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should raise an error if we can't find the node" do
@@ -218,7 +212,7 @@ describe Puppet::Application::Apply do
it "should look for the node" do
Puppet::Node.indirection.expects(:find).returns(@node)
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should raise an error if we can't find the node" do
@@ -232,7 +226,7 @@ describe Puppet::Application::Apply do
@node.expects(:merge).with("values")
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should load custom classes if loadclasses" do
@@ -244,39 +238,39 @@ describe Puppet::Application::Apply do
@node.expects(:classes=)
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should compile the catalog" do
Puppet::Resource::Catalog.indirection.expects(:find).returns(@catalog)
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should transform the catalog to ral" do
@catalog.expects(:to_ral).returns(@catalog)
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should finalize the catalog" do
@catalog.expects(:finalize)
- @apply.main
+ expect { @apply.main }.to exit_with 0
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
+ expect { @apply.main }.to exit_with 0
end
it "should apply the catalog" do
@catalog.expects(:apply).returns(stub_everything('transaction'))
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
it "should save the last run summary" do
@@ -285,7 +279,7 @@ describe Puppet::Application::Apply do
Puppet::Transaction::Report.stubs(:new).returns(report)
Puppet::Configurer.any_instance.expects(:save_last_run_summary).with(report)
- @apply.main
+ expect { @apply.main }.to exit_with 0
end
describe "with detailed_exitcodes" do
@@ -293,18 +287,16 @@ describe Puppet::Application::Apply 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
+ expect { @apply.main }.to exit_with 666
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
+ expect { @apply.main }.to exit_with 666
end
it "should always exit with 0 if option is disabled" do
@@ -312,9 +304,8 @@ describe Puppet::Application::Apply do
@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
+ expect { @apply.main }.to exit_with 0
end
it "should always exit with 0 if --noop" do
@@ -322,9 +313,8 @@ describe Puppet::Application::Apply do
@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
+ expect { @apply.main }.to exit_with 0
end
end
end
diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb
index 4a91c1e6c..1b1c61ab4 100755
--- a/spec/unit/application/cert_spec.rb
+++ b/spec/unit/application/cert_spec.rb
@@ -79,18 +79,14 @@ describe Puppet::Application::Cert do
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
+ Puppet.settings.expects(:print_configs).returns true
+ expect { @cert_app.setup }.to exit_with 0
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)
+ expect { @cert_app.setup }.to exit_with 1
end
it "should set the CA location to 'only'" do
diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb
index 832b7e55b..df8cd3eaf 100755
--- a/spec/unit/application/device_spec.rb
+++ b/spec/unit/application/device_spec.rb
@@ -260,8 +260,7 @@ describe Puppet::Application::Device do
end
it "should exit if the device list is empty" do
- @device.expects(:exit).with(1)
- @device.main
+ expect { @device.main }.to exit_with 1
end
describe "for each device" do
diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb
index 43a4b9849..971378cd4 100755
--- a/spec/unit/application/doc_spec.rb
+++ b/spec/unit/application/doc_spec.rb
@@ -106,9 +106,8 @@ describe Puppet::Application::Doc do
Puppet::Util::Reference.expects(:reference).with(reference).returns(ref)
ref.expects(:doc)
- @doc.expects(:exit)
- @doc.handle_list(nil)
+ expect { @doc.handle_list(nil) }.to exit_with 0
end
it "should add reference to references list with --reference" do
@@ -279,7 +278,6 @@ describe Puppet::Application::Doc do
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([])
@@ -289,30 +287,30 @@ describe Puppet::Application::Doc do
@doc.options.expects(:[]).with(:all).returns(true)
Puppet.settings.expects(:[]=).with(:document_all, true)
- @doc.rdoc
+ expect { @doc.rdoc }.to exit_with 0
end
it "should call Puppet::Util::RDoc.rdoc in full mode" do
Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil)
- @doc.rdoc
+ expect { @doc.rdoc }.to exit_with 0
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
+ expect { @doc.rdoc }.to exit_with 0
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
+ expect { @doc.rdoc }.to exit_with 0
end
it "should call Puppet::Util::RDoc.manifestdoc in manifest mode" do
@doc.manifest = true
Puppet::Util::RDoc.expects(:manifestdoc)
- @doc.rdoc
+ expect { @doc.rdoc }.to exit_with 0
end
it "should get modulepath and manifestdir values from the environment" do
@@ -321,7 +319,7 @@ describe Puppet::Application::Doc do
Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil)
- @doc.rdoc
+ expect { @doc.rdoc }.to exit_with 0
end
end
diff --git a/spec/unit/application/face_base_spec.rb b/spec/unit/application/face_base_spec.rb
index f7c55c556..2a9a22356 100755
--- a/spec/unit/application/face_base_spec.rb
+++ b/spec/unit/application/face_base_spec.rb
@@ -7,18 +7,6 @@ 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")
-
- 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')
@@ -66,7 +54,7 @@ describe Puppet::Application::FaceBase do
it "should use the default action if not given any arguments" do
app.command_line.stubs(:args).returns []
- action = stub(:options => [])
+ action = stub(:options => [], :render_as => nil)
Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(action)
app.stubs(:main)
app.run
@@ -76,7 +64,7 @@ describe Puppet::Application::FaceBase do
it "should use the default action if not given a valid one" do
app.command_line.stubs(:args).returns %w{bar}
- action = stub(:options => [])
+ action = stub(:options => [], :render_as => nil)
Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(action)
app.stubs(:main)
app.run
@@ -88,9 +76,8 @@ describe Puppet::Application::FaceBase do
app.command_line.stubs(:args).returns %w{bar}
Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil)
app.stubs(:main)
- app.run
- app.action.should be_nil
- app.arguments.should == [ 'bar', { } ]
+ expect { app.run }.to exit_with 1
+ @logs.first.message.should =~ /does not have a default action/
end
it "should report a sensible error when options with = fail" do
@@ -162,7 +149,7 @@ describe Puppet::Application::FaceBase do
end
it "should handle application-level options" do
- app.command_line.stubs(:args).returns %w{help --verbose help}
+ app.command_line.stubs(:args).returns %w{basetest --verbose return_true}
app.preinit
app.parse_options
app.face.name.should == :basetest
@@ -189,7 +176,7 @@ describe Puppet::Application::FaceBase do
describe "#main" do
before :each do
- app.expects(:exit).with(0)
+ app.stubs(:puts) # don't dump text to screen.
app.face = Puppet::Face[:basetest, '0.0.1']
app.action = app.face.get_action(:foo)
@@ -198,69 +185,101 @@ describe Puppet::Application::FaceBase do
it "should send the specified verb and name to the face" do
app.face.expects(:foo).with(*app.arguments)
- app.main
+ expect { app.main }.to exit_with 0
end
it "should lookup help when it cannot do anything else" do
app.action = nil
- Puppet::Face[:help, :current].expects(:help).with(:basetest, *app.arguments)
- app.stubs(:puts) # meh. Don't print nil, thanks. --daniel 2011-04-12
- app.main
+ Puppet::Face[:help, :current].expects(:help).with(:basetest)
+ expect { app.main }.to exit_with 1
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
+ expect { app.main }.to exit_with 0
end
end
- describe "#render" do
+ describe "error reporting" do
before :each do
- app.face = Puppet::Face[:basetest, '0.0.1']
- app.action = app.face.get_action(:foo)
+ app.stubs(:puts) # don't dump text to screen.
+
+ app.render_as = :json
+ app.face = Puppet::Face[:basetest, '0.0.1']
+ app.arguments = [{}] # we always have options in there...
end
- ["hello", 1, 1.0].each do |input|
- it "should just return a #{input.class.name}" do
- app.render(input).should == input
- end
+ it "should exit 0 when the action returns true" do
+ app.action = app.face.get_action :return_true
+ expect { app.main }.to exit_with 0
end
- [[1, 2], ["one"], [{ 1 => 1 }]].each do |input|
- it "should render #{input.class} using the 'pp' library" do
- app.render(input).should == input.pretty_inspect
- end
+ it "should exit 0 when the action returns false" do
+ app.action = app.face.get_action :return_false
+ expect { app.main }.to exit_with 0
+ end
+
+ it "should exit 0 when the action returns nil" do
+ app.action = app.face.get_action :return_nil
+ expect { app.main }.to exit_with 0
+ end
+
+ it "should exit non-0 when the action raises" do
+ app.action = app.face.get_action :return_raise
+ expect { app.main }.not_to exit_with 0
end
+ end
- it "should render a non-trivially-keyed Hash with the 'pp' library" do
- hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 }
- app.render(hash).should == hash.pretty_inspect
+ describe "#render" do
+ before :each do
+ app.face = Puppet::Face[:basetest, '0.0.1']
+ app.action = app.face.get_action(:foo)
end
- it "should render a {String,Numeric}-keyed Hash into a table" do
- object = Object.new
- hash = { "one" => 1, "two" => [], "three" => {}, "four" => object,
- 5 => 5, 6.0 => 6 }
+ context "default rendering" do
+ before :each do app.setup end
- # Gotta love ASCII-betical sort order. Hope your objects are better
- # structured for display than my test one is. --daniel 2011-04-18
- app.render(hash).should == <<EOT
+ ["hello", 1, 1.0].each do |input|
+ it "should just return a #{input.class.name}" do
+ app.render(input).should == input
+ end
+ end
+
+ [[1, 2], ["one"], [{ 1 => 1 }]].each do |input|
+ it "should render #{input.class} using JSON" do
+ app.render(input).should == input.to_pson.chomp
+ end
+ end
+
+ it "should render a non-trivially-keyed Hash with using JSON" do
+ hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 }
+ app.render(hash).should == hash.to_pson.chomp
+ end
+
+ it "should render a {String,Numeric}-keyed Hash into a table" do
+ object = Object.new
+ hash = { "one" => 1, "two" => [], "three" => {}, "four" => object,
+ 5 => 5, 6.0 => 6 }
+
+ # Gotta love ASCII-betical sort order. Hope your objects are better
+ # structured for display than my test one is. --daniel 2011-04-18
+ app.render(hash).should == <<EOT
5 5
6.0 6
-four #{object.pretty_inspect.chomp}
+four #{object.to_pson.chomp}
one 1
three {}
two []
EOT
- end
+ end
- it "should render a hash nicely with a multi-line value" do
- hash = {
- "number" => { "1" => '1' * 40, "2" => '2' * 40, '3' => '3' * 40 },
- "text" => { "a" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 }
- }
- app.render(hash).should == <<EOT
+ it "should render a hash nicely with a multi-line value" do
+ pending "Moving to PSON rather than PP makes this unsupportable."
+ hash = {
+ "number" => { "1" => '1' * 40, "2" => '2' * 40, '3' => '3' * 40 },
+ "text" => { "a" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 }
+ }
+ app.render(hash).should == <<EOT
number {"1"=>"1111111111111111111111111111111111111111",
"2"=>"2222222222222222222222222222222222222222",
"3"=>"3333333333333333333333333333333333333333"}
@@ -268,12 +287,46 @@ text {"a"=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"b"=>"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"c"=>"cccccccccccccccccccccccccccccccccccccccc"}
EOT
+ end
+
+ it "should invoke the action rendering hook while rendering" do
+ app.action.set_rendering_method_for(:console, proc { |value| "bi-winning!" })
+ app.render("bi-polar?").should == "bi-winning!"
+ end
+
+ it "should render JSON when asked for json" do
+ app.render_as = :json
+ json = app.render({ :one => 1, :two => 2 })
+ json.should =~ /"one":\s*1\b/
+ json.should =~ /"two":\s*2\b/
+ PSON.parse(json).should == { "one" => 1, "two" => 2 }
+ end
+ end
+
+ it "should fail early if asked to render an invalid format" do
+ app.command_line.stubs(:args).returns %w{--render-as interpretive-dance help help}
+ # We shouldn't get here, thanks to the exception, and our expectation on
+ # it, but this helps us fail if that slips up and all. --daniel 2011-04-27
+ Puppet::Face[:help, :current].expects(:help).never
+
+ expect {
+ expect { app.run }.to exit_with 1
+ }.to have_printed(/I don't know how to render 'interpretive-dance'/)
+ end
+
+ it "should work if asked to render a NetworkHandler format" do
+ app.command_line.stubs(:args).returns %w{dummy find dummy --render-as yaml}
+ expect {
+ expect { app.run }.to exit_with 0
+ }.to have_printed(/--- 3/)
end
- it "should invoke the action rendering hook while rendering" do
- app.action.set_rendering_method_for(:for_humans, proc { |value| "bi-winning!" })
- app.action.render_as = :for_humans
- app.render("bi-polar?").should == "bi-winning!"
+ it "should invoke when_rendering hook 's' when asked to render-as 's'" do
+ app.command_line.stubs(:args).returns %w{with_s_rendering_hook --render-as s}
+ app.action = app.face.get_action(:with_s_rendering_hook)
+ expect {
+ expect { app.run }.to exit_with 0
+ }.to have_printed(/you invoked the 's' rendering hook/)
end
end
end
diff --git a/spec/unit/application/facts_spec.rb b/spec/unit/application/facts_spec.rb
new file mode 100755
index 000000000..2981dc805
--- /dev/null
+++ b/spec/unit/application/facts_spec.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/application/facts'
+
+describe Puppet::Application::Facts do
+ before :each do
+ subject.command_line.stubs(:subcommand_name).returns 'facts'
+ end
+
+ it "should fail if no key is given to find" do
+ subject.command_line.stubs(:args).returns %w{find}
+ expect {
+ expect { subject.run }.to exit_with 1
+ }.to have_printed /err: puppet facts find takes 1 argument, but you gave 0/
+ @logs.first.to_s.should =~ /puppet facts find takes 1 argument, but you gave 0/
+ end
+
+ it "should return facts if a key is given to find" do
+ subject.command_line.stubs(:args).returns %w{find whatever --render-as yaml}
+
+ expect {
+ expect { subject.run }.to exit_with 0
+ }.should have_printed(/object:Puppet::Node::Facts/)
+
+ @logs.should be_empty
+ end
+end
diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb
index 92bc0410a..ee30e7d12 100755
--- a/spec/unit/application/filebucket_spec.rb
+++ b/spec/unit/application/filebucket_spec.rb
@@ -78,18 +78,14 @@ describe Puppet::Application::Filebucket do
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
+ Puppet.settings.expects(:print_configs).returns(true)
+ expect { @filebucket.setup }.to exit_with 0
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)
+ expect { @filebucket.setup }.to exit_with 1
end
describe "with local bucket" do
diff --git a/spec/unit/application/indirection_base_spec.rb b/spec/unit/application/indirection_base_spec.rb
index 57740384a..910774c14 100755
--- a/spec/unit/application/indirection_base_spec.rb
+++ b/spec/unit/application/indirection_base_spec.rb
@@ -1,14 +1,15 @@
#!/usr/bin/env rspec
require 'spec_helper'
require 'puppet/application/indirection_base'
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
########################################################################
# 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
+face = Puppet::Indirector::Face.define(:testindirection, '0.0.1') do
+ summary "fake summary"
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
@@ -27,11 +28,12 @@ describe Puppet::Application::IndirectionBase do
Puppet::Indirector::Indirection.expects(:instance).
with(:testindirection).returns(terminus)
- subject.command_line.instance_variable_set('@args', %w{--terminus foo save})
+ subject.command_line.instance_variable_set('@args', %w{--terminus foo save bar})
# Not a very nice thing. :(
$stderr.stubs(:puts)
+ Puppet.stubs(:err)
- expect { subject.run }.should raise_error SystemExit
+ expect { subject.run }.to exit_with 0
end
end
diff --git a/spec/unit/application/inspect_spec.rb b/spec/unit/application/inspect_spec.rb
index fda61c6e4..571683f37 100755
--- a/spec/unit/application/inspect_spec.rb
+++ b/spec/unit/application/inspect_spec.rb
@@ -19,7 +19,7 @@ describe Puppet::Application::Inspect do
Puppet[:configprint] = "all"
Puppet.settings.expects(:print_configs).returns(true)
- lambda { @inspect.setup }.should raise_error(SystemExit)
+ expect { @inspect.setup }.to exit_with 0
end
it "should fail if reporting is turned off" do
diff --git a/spec/unit/application/kick_spec.rb b/spec/unit/application/kick_spec.rb
index 742c0fff6..b24e78452 100755
--- a/spec/unit/application/kick_spec.rb
+++ b/spec/unit/application/kick_spec.rb
@@ -184,9 +184,7 @@ describe Puppet::Application::Kick, :if => Puppet.features.posix? do
$stderr.stubs(:puts)
@kick.classes = ['class']
- @kick.expects(:exit).with(24)
-
- @kick.setup
+ expect { @kick.setup }.to exit_with 24
end
end
end
@@ -212,9 +210,7 @@ describe Puppet::Application::Kick, :if => Puppet.features.posix? do
describe "the test command" do
it "should exit with exit code 0 " do
- @kick.expects(:exit).with(0)
-
- @kick.test
+ expect { @kick.test }.to exit_with 0
end
end
@@ -226,7 +222,6 @@ describe Puppet::Application::Kick, :if => Puppet.features.posix? do
@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
@@ -236,17 +231,15 @@ describe Puppet::Application::Kick, :if => Puppet.features.posix? do
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
+ expect { @kick.main }.to raise_error SystemExit
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)
@@ -272,31 +265,22 @@ describe Puppet::Application::Kick, :if => Puppet.features.posix? do
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')
+ expect { @kick.run_for_host('host') }.to exit_with 0
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')
+ expect { @kick.run_for_host('host') }.to exit_with 0
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')
+ expect { @kick.run_for_host('host') }.to exit_with 3
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')
+ expect { @kick.run_for_host('host') }.to exit_with 12
end
end
end
diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb
index 2a24086f3..2f6a328e2 100755
--- a/spec/unit/application/master_spec.rb
+++ b/spec/unit/application/master_spec.rb
@@ -152,18 +152,14 @@ describe Puppet::Application::Master do
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
+ Puppet.settings.expects(:print_configs).returns(true)
+ expect { @master.setup }.to exit_with 0
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)
+ expect { @master.setup }.to exit_with 1
end
it "should tell Puppet.settings to use :main,:ssl,:master and :metrics category" do
@@ -241,7 +237,6 @@ describe Puppet::Application::Master do
Puppet.stubs(:[]).with(:manifest).returns("site.pp")
Puppet.stubs(:err)
@master.stubs(:jj)
- @master.stubs(:exit)
Puppet.features.stubs(:pson?).returns true
end
@@ -255,7 +250,7 @@ describe Puppet::Application::Master do
Puppet::Resource::Catalog.indirection.expects(:find).with("foo").returns Puppet::Resource::Catalog.new
$stdout.stubs(:puts)
- @master.compile
+ expect { @master.compile }.to exit_with 0
end
it "should convert the catalog to a pure-resource catalog and use 'jj' to pretty-print the catalog" do
@@ -267,25 +262,21 @@ describe Puppet::Application::Master do
@master.options[:node] = "foo"
@master.expects(:jj).with("rescat")
- @master.compile
+ expect { @master.compile }.to exit_with 0
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
+ expect { @master.compile }.to exit_with 30
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
+ expect { @master.compile }.to exit_with 30
end
end
diff --git a/spec/unit/application/queue_spec.rb b/spec/unit/application/queue_spec.rb
index d71c879a9..15e3927a1 100755
--- a/spec/unit/application/queue_spec.rb
+++ b/spec/unit/application/queue_spec.rb
@@ -86,18 +86,14 @@ describe Puppet::Application::Queue do
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
+ Puppet.settings.expects(:print_configs).returns(true)
+ expect { @queue.setup }.to exit_with 0
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)
+ expect { @queue.setup }.to exit_with 1
end
it "should call setup_logs" do
diff --git a/spec/unit/application/resource_spec.rb b/spec/unit/application/resource_spec.rb
index af60f12c1..9ee6dd71b 100755
--- a/spec/unit/application/resource_spec.rb
+++ b/spec/unit/application/resource_spec.rb
@@ -77,10 +77,8 @@ describe Puppet::Application::Resource do
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)
+ expect { @resource.handle_types(nil) }.to exit_with 0
end
it "should add param to extra_params list" do
diff --git a/spec/unit/application/configurer_spec.rb b/spec/unit/application/secret_agent_spec.rb
index 791a367ea..eba936447 100755
--- a/spec/unit/application/configurer_spec.rb
+++ b/spec/unit/application/secret_agent_spec.rb
@@ -1,11 +1,11 @@
#!/usr/bin/env rspec
require 'spec_helper'
-require 'puppet/application/configurer'
+require 'puppet/application/secret_agent'
require 'puppet/indirector/catalog/rest'
require 'puppet/indirector/report/rest'
require 'tempfile'
-describe "Puppet::Application::Configurer" do
+describe "Puppet::Application::Secret_agent" 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"
@@ -25,7 +25,7 @@ describe "Puppet::Application::Configurer" do
Puppet::Util::Log.stubs(:newdestination)
- Puppet::Application::Configurer.new.run
+ Puppet::Application::Secret_agent.new.run
@report.status.should == "changed"
end
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index de1ca1257..fc1bbceb6 100755
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -27,11 +27,11 @@ describe Puppet::Application do
end
it "should not find classes outside the namespace" do
- lambda { @klass.find("String") }.should raise_error(SystemExit)
+ expect { @klass.find("String") }.to exit_with 1
end
it "should exit if it can't find a class" do
- lambda { @klass.find("ThisShallNeverEverEverExistAsdf") }.should raise_error(SystemExit)
+ expect { @klass.find("ThisShallNeverEverEverExist") }.to exit_with 1
end
end
@@ -286,10 +286,8 @@ describe Puppet::Application do
describe "when using --help" do
it "should call exit" do
- @app.expects(:exit)
@app.stubs(:puts)
-
- @app.handle_help(nil)
+ expect { @app.handle_help(nil) }.to exit_with 0
end
end
@@ -301,8 +299,7 @@ describe Puppet::Application do
it "should exit after printing the version" do
@app.stubs(:puts)
-
- lambda { @app.handle_version(nil) }.should raise_error(SystemExit)
+ expect { @app.handle_version(nil) }.to exit_with 0
end
end
@@ -505,22 +502,19 @@ describe Puppet::Application do
it "should warn and exit if no command can be called" do
$stderr.expects(:puts)
- @app.expects(:exit).with(1)
- @app.run
+ expect { @app.run }.to exit_with 1
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
+ expect { @app.run }.to exit_with 1
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
+ expect { @app.run }.to exit_with 1
end
end
diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb
index ed8dec2a3..e2679a966 100755
--- a/spec/unit/daemon_spec.rb
+++ b/spec/unit/daemon_spec.rb
@@ -86,7 +86,6 @@ describe Puppet::Daemon do
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
@@ -102,34 +101,29 @@ describe Puppet::Daemon do
server = mock 'server'
server.expects(:stop)
@daemon.stubs(:server).returns server
-
- @daemon.stop
+ expect { @daemon.stop }.to exit_with 0
end
it 'should request a stop from Puppet::Application' do
Puppet::Application.expects(:stop!)
- @daemon.stop
+ expect { @daemon.stop }.to exit_with 0
end
it "should remove its pidfile" do
@daemon.expects(:remove_pidfile)
-
- @daemon.stop
+ expect { @daemon.stop }.to exit_with 0
end
it "should close all logs" do
Puppet::Util::Log.expects(:close_all)
-
- @daemon.stop
+ expect { @daemon.stop }.to exit_with 0
end
it "should exit unless called with ':exit => false'" do
- @daemon.expects(:exit)
- @daemon.stop
+ expect { @daemon.stop }.to exit_with 0
end
it "should not exit if called with ':exit => false'" do
- @daemon.expects(:exit).never
@daemon.stop :exit => false
end
end
diff --git a/spec/unit/face/catalog_spec.rb b/spec/unit/face/catalog_spec.rb
index 28c2aa9be..c77a9d153 100755
--- a/spec/unit/face/catalog_spec.rb
+++ b/spec/unit/face/catalog_spec.rb
@@ -1,4 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
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
index a83a92df8..e237800ff 100755
--- a/spec/unit/face/certificate_request_spec.rb
+++ b/spec/unit/face/certificate_request_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
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
index 22c0fa2bf..1033df7ff 100755
--- a/spec/unit/face/certificate_revocation_list_spec.rb
+++ b/spec/unit/face/certificate_revocation_list_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
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
index b0bbf1af6..0cb905b75 100755
--- a/spec/unit/face/certificate_spec.rb
+++ b/spec/unit/face/certificate_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
require 'puppet/ssl/host'
describe Puppet::Face[:certificate, '0.0.1'] do
diff --git a/spec/unit/face/config_spec.rb b/spec/unit/face/config_spec.rb
index 6004d700f..0c762f2aa 100755
--- a/spec/unit/face/config_spec.rb
+++ b/spec/unit/face/config_spec.rb
@@ -1,5 +1,6 @@
#!/usr/bin/env rspec
require 'spec_helper'
+require 'puppet/face'
describe Puppet::Face[:config, '0.0.1'] do
it "should use Settings#print_config_options when asked to print" do
diff --git a/spec/unit/face/facts_spec.rb b/spec/unit/face/facts_spec.rb
index 6ab6ad5be..27b5b9e3d 100755
--- a/spec/unit/face/facts_spec.rb
+++ b/spec/unit/face/facts_spec.rb
@@ -1,5 +1,6 @@
#!/usr/bin/env rspec
require 'spec_helper'
+require 'puppet/face'
describe Puppet::Face[:facts, '0.0.1'] do
it "should define an 'upload' action" do
@@ -8,9 +9,15 @@ describe Puppet::Face[:facts, '0.0.1'] do
describe "when uploading" do
it "should set the terminus_class to :facter"
+ it "should set the cache_class to :rest"
+ it "should find the current certname"
+ end
- it "should set the cach_eclass to :rest"
+ describe "#find" do
+ it { should be_action :find }
- it "should find the current certname"
+ it "should fail without a key" do
+ expect { subject.find }.to raise_error ArgumentError, /wrong number of arguments/
+ end
end
end
diff --git a/spec/unit/face/file_spec.rb b/spec/unit/face/file_spec.rb
index 97e8bcc08..c3f05720f 100755
--- a/spec/unit/face/file_spec.rb
+++ b/spec/unit/face/file_spec.rb
@@ -1,3 +1,12 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
describe Puppet::Face[:file, '0.0.1'] do
- it "should actually have some tests..."
+ it_should_behave_like "an indirector face"
+
+ [:download, :store].each do |action|
+ it { should be_action action }
+ it { should respond_to action }
+ end
end
diff --git a/spec/unit/face/help_spec.rb b/spec/unit/face/help_spec.rb
index faa5f9617..ef66660ff 100755
--- a/spec/unit/face/help_spec.rb
+++ b/spec/unit/face/help_spec.rb
@@ -1,5 +1,6 @@
+#!/usr/bin/env rspec
require 'spec_helper'
-require 'puppet/face/help'
+require 'puppet/face'
describe Puppet::Face[:help, '0.0.1'] do
it "should have a help action" do
@@ -46,6 +47,12 @@ describe Puppet::Face[:help, '0.0.1'] do
context "when listing subcommands" do
subject { Puppet::Face[:help, :current].help }
+ RSpec::Matchers.define :have_a_summary do
+ match do |instance|
+ instance.summary.is_a?(String)
+ end
+ end
+
# 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
@@ -65,6 +72,12 @@ describe Puppet::Face[:help, '0.0.1'] do
end
end
+ Puppet::Face.faces.each do |name|
+ it "should have a summary for #{name}" do
+ Puppet::Face[name, :current].should have_a_summary
+ end
+ end
+
it "should list all legacy applications" do
Puppet::Face[:help, :current].legacy_applications.each do |appname|
subject.should =~ %r{ #{appname} }
diff --git a/spec/unit/face/key_spec.rb b/spec/unit/face/key_spec.rb
index 10d664790..7de4c6e76 100755
--- a/spec/unit/face/key_spec.rb
+++ b/spec/unit/face/key_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
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
index d19312c58..027a4cce0 100755
--- a/spec/unit/face/node_spec.rb
+++ b/spec/unit/face/node_spec.rb
@@ -1,5 +1,6 @@
#!/usr/bin/env rspec
require 'spec_helper'
+require 'puppet/face'
describe Puppet::Face[:node, '0.0.1'] do
it "REVISIT: really should have some tests"
diff --git a/spec/unit/face/plugin_spec.rb b/spec/unit/face/plugin_spec.rb
new file mode 100755
index 000000000..383aaa3d3
--- /dev/null
+++ b/spec/unit/face/plugin_spec.rb
@@ -0,0 +1,10 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
+describe Puppet::Face[:plugin, '0.0.1'] do
+ [:download].each do |action|
+ it { should be_action action }
+ it { should respond_to action }
+ end
+end
diff --git a/spec/unit/face/report_spec.rb b/spec/unit/face/report_spec.rb
index b1b28167e..befc4e496 100755
--- a/spec/unit/face/report_spec.rb
+++ b/spec/unit/face/report_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
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
index 084e2a6a9..0671af4c2 100755
--- a/spec/unit/face/resource_spec.rb
+++ b/spec/unit/face/resource_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
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
index 2adaedca1..30a1adfcb 100755
--- a/spec/unit/face/resource_type_spec.rb
+++ b/spec/unit/face/resource_type_spec.rb
@@ -1,3 +1,7 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+require 'puppet/face'
+
describe Puppet::Face[:resource_type, '0.0.1'] do
it "should actually have some tests..."
end
diff --git a/spec/unit/face/configurer_spec.rb b/spec/unit/face/secret_agent_spec.rb
index 56b45031f..a5ec01f27 100755
--- a/spec/unit/face/configurer_spec.rb
+++ b/spec/unit/face/secret_agent_spec.rb
@@ -1,13 +1,13 @@
#!/usr/bin/env rspec
require 'spec_helper'
+require 'puppet/face'
require 'puppet/indirector/catalog/rest'
require 'tempfile'
-describe Puppet::Face[:configurer, '0.0.1'] do
+describe Puppet::Face[:secret_agent, '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"
-
+ pending "This test doesn't work, but the code actually does - tested by LAK"
dirname = Dir.mktmpdir("puppetdir")
Puppet[:vardir] = dirname
Puppet[:confdir] = dirname
@@ -16,7 +16,7 @@ describe Puppet::Face[:configurer, '0.0.1'] do
@catalog.add_resource(@file)
Puppet::Resource::Catalog::Rest.any_instance.stubs(:find).returns(@catalog)
- report = subject.synchronize("foo")
+ report = subject.synchronize
report.kind.should == "apply"
report.status.should == "changed"
diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb
index a369ad39c..41810650a 100755
--- a/spec/unit/file_serving/fileset_spec.rb
+++ b/spec/unit/file_serving/fileset_spec.rb
@@ -20,6 +20,13 @@ describe Puppet::FileServing::Fileset, " when initializing" do
fileset.path.should == path
end
+ it "should not fail if the path is just the file separator" do
+ path = File::SEPARATOR
+ File.stubs(:lstat).with(path).returns stub('stat')
+ fileset = Puppet::FileServing::Fileset.new(path)
+ 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)
diff --git a/spec/unit/face/indirector_spec.rb b/spec/unit/indirector/face_spec.rb
index bb06fcfe2..1530f7270 100755
--- a/spec/unit/face/indirector_spec.rb
+++ b/spec/unit/indirector/face_spec.rb
@@ -1,10 +1,10 @@
#!/usr/bin/env rspec
require 'spec_helper'
-require 'puppet/face/indirector'
+require 'puppet/indirector/face'
-describe Puppet::Face::Indirector do
+describe Puppet::Indirector::Face do
subject do
- instance = Puppet::Face::Indirector.new(:test, '0.0.1')
+ instance = Puppet::Indirector::Face.new(:test, '0.0.1')
indirection = stub('indirection',
:name => :stub_indirection,
:reset_terminus_class => nil)
@@ -13,33 +13,33 @@ describe Puppet::Face::Indirector do
end
it "should be able to return a list of indirections" do
- Puppet::Face::Indirector.indirections.should be_include("catalog")
+ Puppet::Indirector::Face.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")
+ Puppet::Indirector::Face.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)
+ Puppet::Indirector::Face.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)
+ Puppet::Indirector::Face.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")
+ subject.indirection.expects(method).with(:test, {})
+ subject.send(method, :test)
end
it "should forward passed options" do
- subject.indirection.expects(method).with(:test, "action", {'one'=>'1'})
- subject.send(method, :test, 'action', {'one'=>'1'})
+ subject.indirection.expects(method).with(:test, {'one'=>'1'})
+ subject.send(method, :test, {'one'=>'1'})
end
end
@@ -54,6 +54,6 @@ describe Puppet::Face::Indirector do
end
it "should define a class-level 'info' action" do
- Puppet::Face::Indirector.should be_action(:info)
+ Puppet::Indirector::Face.should be_action(:info)
end
end
diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb
index ba7dc815e..87b9af438 100755
--- a/spec/unit/indirector/request_spec.rb
+++ b/spec/unit/indirector/request_spec.rb
@@ -301,99 +301,4 @@ describe Puppet::Indirector::Request do
lambda { @request.query_string }.should raise_error(ArgumentError)
end
end
-
- describe "when converting to json" do
- before do
- @request = Puppet::Indirector::Request.new(:facts, :find, "foo")
- end
-
- it "should produce a hash with the document_type set to 'request'" do
- @request.should set_json_document_type_to("Puppet::Indirector::Request")
- end
-
- it "should set the 'key'" do
- @request.should set_json_attribute("key").to("foo")
- end
-
- it "should include an attribute for its indirection name" do
- @request.should set_json_attribute("type").to("facts")
- end
-
- it "should include a 'method' attribute set to its method" do
- @request.should set_json_attribute("method").to("find")
- end
-
- it "should add all attributes under the 'attributes' attribute" do
- @request.ip = "127.0.0.1"
- @request.should set_json_attribute("attributes", "ip").to("127.0.0.1")
- end
-
- it "should add all options under the 'attributes' attribute" do
- @request.options["opt"] = "value"
- PSON.parse(@request.to_pson)["data"]['attributes']['opt'].should == "value"
- end
-
- it "should include the instance if provided" do
- facts = Puppet::Node::Facts.new("foo")
- @request.instance = facts
- PSON.parse(@request.to_pson)["data"]['instance'].should be_instance_of(Puppet::Node::Facts)
- end
- end
-
- describe "when converting from json" do
- before do
- @request = Puppet::Indirector::Request.new(:facts, :find, "foo")
- @klass = Puppet::Indirector::Request
- @format = Puppet::Network::FormatHandler.format('pson')
- end
-
- def from_json(json)
- @format.intern(Puppet::Indirector::Request, json)
- end
-
- it "should set the 'key'" do
- from_json(@request.to_pson).key.should == "foo"
- end
-
- it "should fail if no key is provided" do
- json = PSON.parse(@request.to_pson)
- json['data'].delete("key")
- lambda { from_json(json.to_pson) }.should raise_error(ArgumentError)
- end
-
- it "should set its indirector name" do
- from_json(@request.to_pson).indirection_name.should == :facts
- end
-
- it "should fail if no type is provided" do
- json = PSON.parse(@request.to_pson)
- json['data'].delete("type")
- lambda { from_json(json.to_pson) }.should raise_error(ArgumentError)
- end
-
- it "should set its method" do
- from_json(@request.to_pson).method.should == "find"
- end
-
- it "should fail if no method is provided" do
- json = PSON.parse(@request.to_pson)
- json['data'].delete("method")
- lambda { from_json(json.to_pson) }.should raise_error(ArgumentError)
- end
-
- it "should initialize with all attributes and options" do
- @request.ip = "127.0.0.1"
- @request.options["opt"] = "value"
- result = from_json(@request.to_pson)
- result.options[:opt].should == "value"
- result.ip.should == "127.0.0.1"
- end
-
- it "should set its instance as an instance if one is provided" do
- facts = Puppet::Node::Facts.new("foo")
- @request.instance = facts
- result = from_json(@request.to_pson)
- result.instance.should be_instance_of(Puppet::Node::Facts)
- end
- end
end
diff --git a/spec/unit/interface/action_builder_spec.rb b/spec/unit/interface/action_builder_spec.rb
index 6f36d2b44..e9f10a1a6 100755
--- a/spec/unit/interface/action_builder_spec.rb
+++ b/spec/unit/interface/action_builder_spec.rb
@@ -7,7 +7,8 @@ describe Puppet::Interface::ActionBuilder do
let :face do Puppet::Interface.new(:puppet_interface_actionbuilder, '0.0.1') end
it "should build an action" do
- action = Puppet::Interface::ActionBuilder.build(nil, :foo) do
+ action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
end
action.should be_a(Puppet::Interface::Action)
action.name.should == :foo
@@ -26,17 +27,25 @@ describe Puppet::Interface::ActionBuilder do
should raise_error("Action :foo must specify a block")
end
+ it "should require an invocation block" do
+ expect {
+ Puppet::Interface::ActionBuilder.build(face, :foo) {}
+ }.to raise_error(/actions need to know what to do when_invoked; please add the block/)
+ end
+
describe "when handling options" do
it "should have a #option DSL function" do
method = nil
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
method = self.method(:option)
end
- method.should be
+ method.should be_an_instance_of Method
end
it "should define an option without a block" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
option "--bar"
end
action.should be_option :bar
@@ -44,6 +53,7 @@ describe Puppet::Interface::ActionBuilder do
it "should accept an empty block" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
option "--bar" do
# This space left deliberately blank.
end
@@ -58,15 +68,18 @@ describe Puppet::Interface::ActionBuilder do
option '-w'
action(:foo) do
+ when_invoked do true end
option '-x', '--ex'
option '-y', '--why'
end
action(:bar) do
+ when_invoked do true end
option '-z', '--zee'
end
action(:baz) do
+ when_invoked do true end
option '-z', '--zed'
end
end
@@ -75,6 +88,7 @@ describe Puppet::Interface::ActionBuilder do
it 'should add the options from the specified action' do
foo = face.get_action(:foo)
action = Puppet::Interface::ActionBuilder.build(face, :inherit_options) do
+ when_invoked do true end
inherit_options_from foo
end
action.options.should == foo.options
@@ -84,15 +98,17 @@ describe Puppet::Interface::ActionBuilder do
foo = face.get_action(:foo)
bar = face.get_action(:bar)
action = Puppet::Interface::ActionBuilder.build(face, :inherit_options) do
+ when_invoked do true end
inherit_options_from foo
inherit_options_from bar
end
- action.options.should == (foo.options + bar.options).uniq.sort
+ action.options.should == (foo.options + bar.options).uniq
end
it 'should permit symbolic names for actions in the same face' do
foo = face.get_action(:foo)
action = Puppet::Interface::ActionBuilder.build(face, :inherit_options) do
+ when_invoked do true end
inherit_options_from :foo
end
action.options.should == foo.options
@@ -101,6 +117,7 @@ describe Puppet::Interface::ActionBuilder do
it 'should raise a useful error if you supply a bad action name' do
expect do
Puppet::Interface::ActionBuilder.build(face, :inherit_options) do
+ when_invoked do true end
inherit_options_from :nowhere
end
end.to raise_error /nowhere/
@@ -110,6 +127,7 @@ describe Puppet::Interface::ActionBuilder do
context "inline documentation" do
it "should set the summary" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
summary "this is some text"
end
action.summary.should == "this is some text"
@@ -119,13 +137,16 @@ describe Puppet::Interface::ActionBuilder do
context "action defaulting" do
it "should set the default to true" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
default
end
action.default.should be_true
end
it "should not be default by, er, default. *cough*" do
- action = Puppet::Interface::ActionBuilder.build(face, :foo) do end
+ action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
+ end
action.default.should be_false
end
end
@@ -134,6 +155,7 @@ describe Puppet::Interface::ActionBuilder do
it "should fail if no rendering format is given" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering do true end
end
}.to raise_error ArgumentError, /must give a rendering format to when_rendering/
@@ -142,6 +164,7 @@ describe Puppet::Interface::ActionBuilder do
it "should fail if no block is given" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json
end
}.to raise_error ArgumentError, /must give a block to when_rendering/
@@ -150,6 +173,7 @@ describe Puppet::Interface::ActionBuilder do
it "should fail if the block takes no arguments" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do true end
end
}.to raise_error ArgumentError, /when_rendering methods take one argument, the result, not/
@@ -158,6 +182,7 @@ describe Puppet::Interface::ActionBuilder do
it "should fail if the block takes more than one argument" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do |a, b, c| true end
end
}.to raise_error ArgumentError, /when_rendering methods take one argument, the result, not/
@@ -166,6 +191,7 @@ describe Puppet::Interface::ActionBuilder do
it "should fail if the block takes a variable number of arguments" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do |*args| true end
end
}.to raise_error(ArgumentError,
@@ -174,6 +200,7 @@ describe Puppet::Interface::ActionBuilder do
it "should stash a rendering block" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do |a| true end
end
action.when_rendering(:json).should be_an_instance_of Method
@@ -182,6 +209,7 @@ describe Puppet::Interface::ActionBuilder do
it "should fail if you try to set the same rendering twice" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do |a| true end
when_rendering :json do |a| true end
end
@@ -190,6 +218,7 @@ describe Puppet::Interface::ActionBuilder do
it "should work if you set two different renderings" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do |a| true end
when_rendering :yaml do |a| true end
end
@@ -199,6 +228,7 @@ describe Puppet::Interface::ActionBuilder do
it "should be bound to the face when called" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
when_rendering :json do |a| self end
end
action.when_rendering(:json).call(true).should == face
@@ -207,13 +237,16 @@ describe Puppet::Interface::ActionBuilder do
context "#render_as" do
it "should default to nil (eg: based on context)" do
- action = Puppet::Interface::ActionBuilder.build(face, :foo) do end
+ action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
+ end
action.render_as.should be_nil
end
it "should fail if not rendering format is given" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
render_as
end
}.to raise_error ArgumentError, /must give a rendering format to render_as/
@@ -222,23 +255,18 @@ describe Puppet::Interface::ActionBuilder do
Puppet::Network::FormatHandler.formats.each do |name|
it "should accept #{name.inspect} format" do
action = Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
render_as name
end
action.render_as.should == name
end
end
- it "should accept :for_humans format" do
- action = Puppet::Interface::ActionBuilder.build(face, :foo) do
- render_as :for_humans
- end
- action.render_as.should == :for_humans
- end
-
[:if_you_define_this_format_you_frighten_me, "json", 12].each do |input|
it "should fail if given #{input.inspect}" do
expect {
Puppet::Interface::ActionBuilder.build(face, :foo) do
+ when_invoked do true end
render_as input
end
}.to raise_error ArgumentError, /#{input.inspect} is not a valid rendering format/
diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb
index 07d517c18..5a479ad5c 100755
--- a/spec/unit/interface/action_manager_spec.rb
+++ b/spec/unit/interface/action_manager_spec.rb
@@ -94,7 +94,7 @@ describe Puppet::Interface::ActionManager do
end
it "should be able to indicate when an action is defined" do
- subject.action(:foo) { "something" }
+ subject.action(:foo) { when_invoked do true end }
subject.should be_action(:foo)
end
end
@@ -218,25 +218,29 @@ describe Puppet::Interface::ActionManager do
describe "#action" do
it 'should add an action' do
- subject.action(:foo) { }
+ subject.action(:foo) { when_invoked do true end }
subject.get_action(:foo).should be_a Puppet::Interface::Action
end
it 'should support default actions' do
- subject.action(:foo) { default }
+ subject.action(:foo) { when_invoked do true end; default }
subject.get_default_action.should == subject.get_action(:foo)
end
it 'should not support more than one default action' do
- subject.action(:foo) { default }
- expect { subject.action(:bar) { default } }.should raise_error
+ subject.action(:foo) { when_invoked do true end; default }
+ expect { subject.action(:bar) {
+ when_invoked do true end
+ default
+ }
+ }.should raise_error /cannot both be default/
end
end
describe "#get_action" do
let :parent_class do
parent_class = Class.new(Puppet::Interface)
- parent_class.action(:foo) {}
+ parent_class.action(:foo) { when_invoked do true end }
parent_class
end
diff --git a/spec/unit/interface/action_spec.rb b/spec/unit/interface/action_spec.rb
index 735fbcb72..f43968709 100755
--- a/spec/unit/interface/action_spec.rb
+++ b/spec/unit/interface/action_spec.rb
@@ -74,25 +74,29 @@ describe Puppet::Interface::Action do
option '-w'
action(:foo) do
+ when_invoked do true end
option '-x', '--ex'
option '-y', '--why'
end
action(:bar) do
+ when_invoked do true end
option '-z', '--zee'
end
action(:baz) do
+ when_invoked do true end
option '-z', '--zed'
end
action(:noopts) do
# no options declared
+ when_invoked do true end
end
end
end
- subject { action = face.action(:new_action) { } }
+ subject { action = face.action(:new_action) { when_invoked do true end } }
it 'should add the options from the specified action' do
subject.inherit_options_from(foo = face.get_action(:foo))
@@ -108,7 +112,7 @@ describe Puppet::Interface::Action do
it 'should add the options from multiple actions' do
subject.inherit_options_from(foo = face.get_action(:foo))
subject.inherit_options_from(bar = face.get_action(:bar))
- subject.options.should == (foo.options + bar.options).uniq.sort
+ subject.options.should == (foo.options + bar.options).uniq
end
it 'should not inherit face options' do
@@ -205,6 +209,7 @@ describe Puppet::Interface::Action do
it "should support options with an empty block" do
face = Puppet::Interface.new(:action_level_options, '0.0.1') do
action :foo do
+ when_invoked do true end
option "--bar" do
# this line left deliberately blank
end
@@ -217,7 +222,10 @@ describe Puppet::Interface::Action do
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
+ action :foo do
+ when_invoked do true end
+ option "--bar"
+ end
end
face.get_action(:foo).options.should =~ [:bar]
@@ -226,8 +234,8 @@ describe Puppet::Interface::Action do
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
+ action :foo do when_invoked do true end ; option "--bar" end
+ action :baz do when_invoked do true end ; option "--bim" end
option "--quux"
end
end
@@ -241,7 +249,10 @@ describe Puppet::Interface::Action do
parent.option "--foo"
child = parent.new(:inherited_options, '0.0.1') do
option "--bar"
- action :action do option "--baz" end
+ action :action do
+ when_invoked do true end
+ option "--baz"
+ end
end
action = child.get_action(:action)
@@ -270,7 +281,10 @@ describe Puppet::Interface::Action do
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)
+ action(:foo) do
+ when_invoked do true end
+ self.instance_eval &block
+ end
end
face.get_action(:foo)
end
@@ -292,7 +306,7 @@ describe Puppet::Interface::Action do
when_invoked { }
end
end
- expect { face.bar }.to raise_error ArgumentError, /missing required options \(foo\)/
+ expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/
end
it "should fail when a required face option is not provided" do
@@ -300,184 +314,251 @@ describe Puppet::Interface::Action do
option('--foo') { required }
action(:bar) { when_invoked { } }
end
- expect { face.bar }.to raise_error ArgumentError, /missing required options \(foo\)/
+ expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/
end
end
- context "with action decorators" do
- context "local only" do
+ context "with decorators" do
+ context "declared locally" do
let :face do
Puppet::Interface.new(:action_decorators, '0.0.1') do
action :bar do when_invoked do true end end
- def report(arg) end
+ def reported; @reported; end
+ def report(arg)
+ (@reported ||= []) << arg
+ end
end
end
- it "should call action before decorators" do
- face.action(:baz) do
- option "--baz" do
- before_action do |action, args, options|
- report(:action_option)
- end
- end
- when_invoked do true end
+ it "should execute before advice on action options in declaration order" do
+ face.action(:boo) do
+ option("--foo") { before_action { |_,_,_| report :foo } }
+ option("--bar", '-b') { before_action { |_,_,_| report :bar } }
+ option("-q", "--quux") { before_action { |_,_,_| report :quux } }
+ option("-f") { before_action { |_,_,_| report :f } }
+ option("--baz") { before_action { |_,_,_| report :baz } }
+ when_invoked { }
end
- face.expects(:report).with(:action_option)
- face.baz :baz => true
+ face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1
+ face.reported.should == [ :foo, :bar, :quux, :f, :baz ]
end
- it "should call action after decorators" do
- face.action(:baz) do
- option "--baz" do
- after_action do |action, args, options|
- report(:action_option)
- end
- end
- when_invoked do true end
+ it "should execute after advice on action options in declaration order" do
+ face.action(:boo) do
+ option("--foo") { after_action { |_,_,_| report :foo } }
+ option("--bar", '-b') { after_action { |_,_,_| report :bar } }
+ option("-q", "--quux") { after_action { |_,_,_| report :quux } }
+ option("-f") { after_action { |_,_,_| report :f } }
+ option("--baz") { after_action { |_,_,_| report :baz } }
+ when_invoked { }
end
- face.expects(:report).with(:action_option)
- face.baz :baz => true
+ face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1
+ face.reported.should == [ :foo, :bar, :quux, :f, :baz ].reverse
end
- it "should call local before decorators" do
- face.option "--foo FOO" do
- before_action do |action, args, options|
- report(:before)
- end
+ it "should execute before advice on face options in declaration order" do
+ face.instance_eval do
+ option("--foo") { before_action { |_,_,_| report :foo } }
+ option("--bar", '-b') { before_action { |_,_,_| report :bar } }
+ option("-q", "--quux") { before_action { |_,_,_| report :quux } }
+ option("-f") { before_action { |_,_,_| report :f } }
+ option("--baz") { before_action { |_,_,_| report :baz } }
end
- face.expects(:report).with(:before)
- face.bar({:foo => 12})
+ face.script(:boo) { }
+
+ face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1
+ face.reported.should == [ :foo, :bar, :quux, :f, :baz ]
end
- it "should call local after decorators" do
- face.option "--foo FOO" do
- after_action do |action, args, options| report(:after) end
+ it "should execute after advice on face options in declaration order" do
+ face.instance_eval do
+ option("--foo") { after_action { |_,_,_| report :foo } }
+ option("--bar", '-b') { after_action { |_,_,_| report :bar } }
+ option("-q", "--quux") { after_action { |_,_,_| report :quux } }
+ option("-f") { after_action { |_,_,_| report :f } }
+ option("--baz") { after_action { |_,_,_| report :baz } }
end
- face.expects(:report).with(:after)
- face.bar({:foo => 12})
+ face.script(:boo) { }
+
+ face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1
+ face.reported.should == [ :foo, :bar, :quux, :f, :baz ].reverse
end
- context "with inactive decorators" do
- it "should not invoke a decorator if the options are empty" do
- face.option "--foo FOO" do
- before_action do |action, args, options|
- report :before_action
- end
+ it "should execute before advice on face options before action options" do
+ face.instance_eval do
+ option("--face-foo") { before_action { |_,_,_| report :face_foo } }
+ option("--face-bar", '-r') { before_action { |_,_,_| report :face_bar } }
+ action(:boo) do
+ option("--action-foo") { before_action { |_,_,_| report :action_foo } }
+ option("--action-bar", '-b') { before_action { |_,_,_| report :action_bar } }
+ option("-q", "--action-quux") { before_action { |_,_,_| report :action_quux } }
+ option("-a") { before_action { |_,_,_| report :a } }
+ option("--action-baz") { before_action { |_,_,_| report :action_baz } }
+ when_invoked { }
end
- face.expects(:report).never # I am testing the negative.
- face.bar
+ option("-u", "--face-quux") { before_action { |_,_,_| report :face_quux } }
+ option("-f") { before_action { |_,_,_| report :f } }
+ option("--face-baz") { before_action { |_,_,_| report :face_baz } }
+ end
+
+ expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz,
+ :action_foo, :action_bar, :action_quux, :a, :action_baz ]
+ face.boo Hash[ *expected_calls.zip([]).flatten ]
+ face.reported.should == expected_calls
+ end
+
+ it "should execute after advice on face options in declaration order" do
+ face.instance_eval do
+ option("--face-foo") { after_action { |_,_,_| report :face_foo } }
+ option("--face-bar", '-r') { after_action { |_,_,_| report :face_bar } }
+ action(:boo) do
+ option("--action-foo") { after_action { |_,_,_| report :action_foo } }
+ option("--action-bar", '-b') { after_action { |_,_,_| report :action_bar } }
+ option("-q", "--action-quux") { after_action { |_,_,_| report :action_quux } }
+ option("-a") { after_action { |_,_,_| report :a } }
+ option("--action-baz") { after_action { |_,_,_| report :action_baz } }
+ when_invoked { }
+ end
+ option("-u", "--face-quux") { after_action { |_,_,_| report :face_quux } }
+ option("-f") { after_action { |_,_,_| report :f } }
+ option("--face-baz") { after_action { |_,_,_| report :face_baz } }
end
- context "with some decorators only" do
- before :each do
- face.option "--foo" do
- before_action do |action, args, options| report :foo end
- end
- face.option "--bar" do
- before_action do |action, args, options| report :bar end
- end
- end
+ expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz,
+ :action_foo, :action_bar, :action_quux, :a, :action_baz ]
+ face.boo Hash[ *expected_calls.zip([]).flatten ]
+ face.reported.should == expected_calls.reverse
+ end
- it "should work with the foo option" do
- face.expects(:report).with(:foo)
- face.bar(:foo => true)
- end
+ it "should not invoke a decorator if the options are empty" do
+ face.option("--foo FOO") { before_action { |_,_,_| report :before_action } }
+ face.expects(:report).never
+ face.bar
+ end
- it "should work with the bar option" do
- face.expects(:report).with(:bar)
- face.bar(:bar => true)
- end
+ context "passing a subset of the options" do
+ before :each do
+ face.option("--foo") { before_action { |_,_,_| report :foo } }
+ face.option("--bar") { before_action { |_,_,_| report :bar } }
+ end
- it "should work with both options" do
- face.expects(:report).with(:foo)
- face.expects(:report).with(:bar)
- face.bar(:foo => true, :bar => true)
- end
+ it "should invoke only foo's advice when passed only 'foo'" do
+ face.bar(:foo => true)
+ face.reported.should == [ :foo ]
+ end
+
+ it "should invoke only bar's advice when passed only 'bar'" do
+ face.bar(:bar => true)
+ face.reported.should == [ :bar ]
+ end
+
+ it "should invoke advice for all passed options" do
+ face.bar(:foo => true, :bar => true)
+ face.reported.should == [ :foo, :bar ]
end
end
end
- context "with inherited decorators" do
+ context "and inheritance" do
let :parent do
- parent = Class.new(Puppet::Interface)
- parent.script :on_parent do :on_parent end
- parent.define_method :report do |arg| arg end
- parent
+ Class.new(Puppet::Interface) do
+ script(:on_parent) { :on_parent }
+
+ def reported; @reported; end
+ def report(arg)
+ (@reported ||= []) << arg
+ end
+ end
end
let :child do
- child = parent.new(:inherited_decorators, '0.0.1') do
- script :on_child do :on_child end
+ parent.new(:inherited_decorators, '0.0.1') do
+ script(:on_child) { :on_child }
end
end
- context "with a child decorator" do
+ context "locally declared face options" do
subject do
- child.option "--foo FOO" do
- before_action do |action, args, options|
- report(:child_before)
- end
- end
- child.expects(:report).with(:child_before)
+ child.option("--foo=") { before_action { |_,_,_| report :child_before } }
child
end
- it "child actions should invoke the decorator" do
- subject.on_child({:foo => true, :bar => true}).should == :on_child
+ it "should be invoked when calling a child action" do
+ subject.on_child(:foo => true, :bar => true).should == :on_child
+ subject.reported.should == [ :child_before ]
end
- it "parent actions should invoke the decorator" do
- subject.on_parent({:foo => true, :bar => true}).should == :on_parent
+ it "should be invoked when calling a parent action" do
+ subject.on_parent(:foo => true, :bar => true).should == :on_parent
+ subject.reported.should == [ :child_before ]
end
end
- context "with a parent decorator" do
+ context "inherited face option decorators" do
subject do
- parent.option "--foo FOO" do
- before_action do |action, args, options|
- report(:parent_before)
- end
- end
- child.expects(:report).with(:parent_before)
+ parent.option("--foo=") { before_action { |_,_,_| report :parent_before } }
child
end
- it "child actions should invoke the decorator" do
- subject.on_child({:foo => true, :bar => true}).should == :on_child
+ it "should be invoked when calling a child action" do
+ subject.on_child(:foo => true, :bar => true).should == :on_child
+ subject.reported.should == [ :parent_before ]
end
- it "parent actions should invoke the decorator" do
- subject.on_parent({:foo => true, :bar => true}).should == :on_parent
+ it "should be invoked when calling a parent action" do
+ subject.on_parent(:foo => true, :bar => true).should == :on_parent
+ subject.reported.should == [ :parent_before ]
end
end
- context "with child and parent decorators" do
+ context "with both inherited and local face options" do
+ # Decorations should be invoked in declaration order, according to
+ # inheritance (e.g. parent class options should be handled before
+ # subclass options).
subject do
- parent.option "--foo FOO" do
- before_action { |action, args, options| report(:parent_before) }
- after_action { |action, args, options| report(:parent_after) }
+ child.option "-c" do
+ before_action { |action, args, options| report :c_before }
+ after_action { |action, args, options| report :c_after }
end
- child.option "--bar BAR" do
- before_action { |action, args, options| report(:child_before) }
- after_action { |action, args, options| report(:child_after) }
+
+ parent.option "-a" do
+ before_action { |action, args, options| report :a_before }
+ after_action { |action, args, options| report :a_after }
end
- child.expects(:report).with(:child_before)
- child.expects(:report).with(:parent_before)
- child.expects(:report).with(:parent_after)
- child.expects(:report).with(:child_after)
+ child.option "-d" do
+ before_action { |action, args, options| report :d_before }
+ after_action { |action, args, options| report :d_after }
+ end
+
+ parent.option "-b" do
+ before_action { |action, args, options| report :b_before }
+ after_action { |action, args, options| report :b_after }
+ end
+
+ child.script(:decorations) { report :invoked }
child
end
- it "child actions should invoke all the decorator" do
- subject.on_child({:foo => true, :bar => true}).should == :on_child
+ it "should invoke all decorations when calling a child action" do
+ subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1)
+ subject.reported.should == [
+ :a_before, :b_before, :c_before, :d_before,
+ :invoked,
+ :d_after, :c_after, :b_after, :a_after
+ ]
end
- it "parent actions should invoke all the decorator" do
- subject.on_parent({:foo => true, :bar => true}).should == :on_parent
+ it "should invoke all decorations when calling a parent action" do
+ subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1)
+ subject.reported.should == [
+ :a_before, :b_before, :c_before, :d_before,
+ :invoked,
+ :d_after, :c_after, :b_after, :a_after
+ ]
end
end
end
@@ -486,7 +567,9 @@ describe Puppet::Interface::Action do
it_should_behave_like "documentation on faces" do
subject do
face = Puppet::Interface.new(:action_documentation, '0.0.1') do
- action :documentation do end
+ action :documentation do
+ when_invoked do true end
+ end
end
face.get_action(:documentation)
end
@@ -500,4 +583,23 @@ describe Puppet::Interface::Action do
it "should fail if a second block is given for the same type"
it "should return the block if asked"
end
+
+ context "#validate_args" do
+ subject do
+ Puppet::Interface.new(:validate_args, '1.0.0') do
+ script :test do true end
+ end
+ end
+
+ it "should fail if a required option is not passed" do
+ subject.option "--foo" do required end
+ expect { subject.test }.to raise_error ArgumentError, /options are required/
+ end
+
+ it "should fail if two aliases to one option are passed" do
+ subject.option "--foo", "-f"
+ expect { subject.test :foo => true, :f => true }.
+ to raise_error ArgumentError, /Multiple aliases for the same option/
+ end
+ end
end
diff --git a/spec/unit/interface/face_collection_spec.rb b/spec/unit/interface/face_collection_spec.rb
index 890e06a9e..4ad8787c5 100755
--- a/spec/unit/interface/face_collection_spec.rb
+++ b/spec/unit/interface/face_collection_spec.rb
@@ -16,12 +16,13 @@ describe Puppet::Interface::FaceCollection 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
+ subject.instance_variable_get(:@faces).clear
+ subject.instance_variable_set(:@loaded, false)
end
after :each do
- subject.instance_variable_set("@faces", @original_faces)
- $".clear ; @original_required.each do |item| $" << item end
+ subject.instance_variable_set(:@faces, @original_faces)
+ @original_required.each {|f| $".push f unless $".include? f }
end
describe "::prefix_match?" do
@@ -159,4 +160,21 @@ describe Puppet::Interface::FaceCollection do
end
end
end
+
+ context "faulty faces" do
+ before :each do
+ $:.unshift "#{PuppetSpec::FIXTURE_DIR}/faulty_face"
+ end
+
+ after :each do
+ $:.delete_if {|x| x == "#{PuppetSpec::FIXTURE_DIR}/faulty_face"}
+ end
+
+ it "should not die if a face has a syntax error" do
+ subject.faces.should be_include :help
+ subject.faces.should_not be_include :syntax
+ @logs.should_not be_empty
+ @logs.first.message.should =~ /syntax error/
+ end
+ end
end
diff --git a/spec/unit/interface/option_builder_spec.rb b/spec/unit/interface/option_builder_spec.rb
index e9346852c..3e91c683b 100755
--- a/spec/unit/interface/option_builder_spec.rb
+++ b/spec/unit/interface/option_builder_spec.rb
@@ -16,13 +16,15 @@ describe Puppet::Interface::OptionBuilder do
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
+ [:description, :summary].each do |doc|
+ it "should support #{doc} declarations" do
+ text = "this is the #{doc}"
+ option = Puppet::Interface::OptionBuilder.build(face, "--foo") do
+ self.send doc, text
+ end
+ option.should be_an_instance_of Puppet::Interface::Option
+ option.send(doc).should == text
end
- option.should be_an_instance_of Puppet::Interface::Option
- option.desc.should == text
end
context "before_action hook" do
diff --git a/spec/unit/interface_spec.rb b/spec/unit/interface_spec.rb
index a1d70cf64..e28e55aac 100755
--- a/spec/unit/interface_spec.rb
+++ b/spec/unit/interface_spec.rb
@@ -76,7 +76,11 @@ describe Puppet::Interface do
# Required documentation methods...
{ :summary => "summary",
- :description => "This is the description of the stuff\n\nWhee"
+ :description => "This is the description of the stuff\n\nWhee",
+ :examples => "This is my example",
+ :short_description => "This is my custom short description",
+ :notes => "These are my notes...",
+ :author => "This is my authorship data",
}.each do |attr, value|
it "should support #{attr} in the builder" do
face = subject.new(:builder, '1.0.0') do
@@ -142,6 +146,7 @@ describe Puppet::Interface do
option "--foo"
option "--bar"
action :baz do
+ when_invoked { true }
option "--quux"
end
end
@@ -151,7 +156,10 @@ describe Puppet::Interface do
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
+ action :bar do
+ when_invoked { true }
+ option "--foo"
+ end
option "--foo"
end
}.should raise_error ArgumentError, /Option foo conflicts with existing option foo on/i
@@ -159,8 +167,8 @@ describe Puppet::Interface do
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
+ action :foo do when_invoked { true } ; option "--quux" end
+ action :bar do when_invoked { true } ; option "--quux" end
end
face.get_action(:foo).options.should =~ [:quux]
@@ -172,14 +180,14 @@ describe Puppet::Interface do
let :parent do
parent = Class.new(subject)
parent.option("--inherited")
- parent.action(:parent_action) do end
+ parent.action(:parent_action) do when_invoked { true } end
parent
end
let :face do
face = parent.new(:example, '0.2.1')
face.option("--local")
- face.action(:face_action) do end
+ face.action(:face_action) do when_invoked { true } end
face
end
diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb
index 72d355192..62c2dbb9d 100755
--- a/spec/unit/network/formats_spec.rb
+++ b/spec/unit/network/formats_spec.rb
@@ -330,4 +330,66 @@ describe "Puppet Network Format" do
end
end
end
+
+ describe ":console format" do
+ subject { Puppet::Network::FormatHandler.format(:console) }
+ it { should be_an_instance_of Puppet::Network::Format }
+ let :json do Puppet::Network::FormatHandler.format(:pson) end
+
+ [:intern, :intern_multiple].each do |method|
+ it "should not implement #{method}" do
+ expect { subject.send(method, String, 'blah') }.to raise_error NotImplementedError
+ end
+ end
+
+ ["hello", 1, 1.0].each do |input|
+ it "should just return a #{input.inspect}" do
+ subject.render(input).should == input
+ end
+ end
+
+ [[1, 2], ["one"], [{ 1 => 1 }]].each do |input|
+ it "should render #{input.inspect} as JSON" do
+ subject.render(input).should == json.render(input).chomp
+ end
+ end
+
+ it "should render a non-trivially-keyed Hash as JSON" do
+ hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 }
+ subject.render(hash).should == json.render(hash).chomp
+ end
+
+ it "should render a {String,Numeric}-keyed Hash into a table" do
+ object = Object.new
+ hash = { "one" => 1, "two" => [], "three" => {}, "four" => object,
+ 5 => 5, 6.0 => 6 }
+
+ # Gotta love ASCII-betical sort order. Hope your objects are better
+ # structured for display than my test one is. --daniel 2011-04-18
+ subject.render(hash).should == <<EOT
+5 5
+6.0 6
+four #{json.render(object).chomp}
+one 1
+three {}
+two []
+EOT
+ end
+
+ it "should render a hash nicely with a multi-line value" do
+ pending "Moving to PSON rather than PP makes this unsupportable."
+ hash = {
+ "number" => { "1" => '1' * 40, "2" => '2' * 40, '3' => '3' * 40 },
+ "text" => { "a" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 }
+ }
+ subject.render(hash).should == <<EOT
+number {"1"=>"1111111111111111111111111111111111111111",
+ "2"=>"2222222222222222222222222222222222222222",
+ "3"=>"3333333333333333333333333333333333333333"}
+text {"a"=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "b"=>"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ "c"=>"cccccccccccccccccccccccccccccccccccccccc"}
+EOT
+ end
+ end
end
diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb
index bd95071c1..a952f24e2 100755
--- a/spec/unit/network/http/api/v1_spec.rb
+++ b/spec/unit/network/http/api/v1_spec.rb
@@ -31,7 +31,7 @@ describe Puppet::Network::HTTP::API::V1 do
end
it "should use the first field of the URI as the environment" do
- @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should == "env"
+ @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].to_s.should == "env"
end
it "should fail if the environment is not alphanumeric" do
@@ -39,7 +39,11 @@ describe Puppet::Network::HTTP::API::V1 do
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"
+ @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].to_s.should == "env"
+ end
+
+ it "should return the environment as a Puppet::Node::Environment" do
+ @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should be_a Puppet::Node::Environment
end
it "should use the second field of the URI as the indirection name" do
diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb
index 499a14b78..e1403997f 100755
--- a/spec/unit/network/rest_authconfig_spec.rb
+++ b/spec/unit/network/rest_authconfig_spec.rb
@@ -5,18 +5,7 @@ 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 },
- ]
+ DEFAULT_ACL = Puppet::Network::RestAuthConfig::DEFAULT_ACL
before :each do
FileTest.stubs(:exists?).returns(true)
diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb
index 07f5f7e1b..efaa76e12 100755
--- a/spec/unit/node/facts_spec.rb
+++ b/spec/unit/node/facts_spec.rb
@@ -110,11 +110,7 @@ describe Puppet::Node::Facts, "when indirecting" do
end
it "should accept properly formatted pson" do
- facts = Puppet::Node::Facts.new("foo")
- facts.values = {"a" => "1", "b" => "2", "c" => "3"}
- facts.expiration = Time.now
- #pson = %Q({"document_type": "Puppet::Node::Facts", "data: {"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}})
- pson = %Q({"data": {"name":"foo", "expiration":"#{@expiration}", "timestamp": "#{@timestamp}", "values":{"a":"1","b":"2","c":"3"}}, "document_type":"Puppet::Node::Facts"})
+ 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'
@@ -127,29 +123,10 @@ describe Puppet::Node::Facts, "when indirecting" do
facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3})
facts.expiration = @expiration
result = PSON.parse(facts.to_pson)
- result.name.should == facts.name
- result.values.should == facts.values
- result.timestamp.should == facts.timestamp
- result.expiration.should == facts.expiration
- result.type.should == Puppet::Node::Facts
- end
-
- it "should not include nil values" do
- facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3})
-
- # XXX:LAK For some reason this is resurrection the full instance, instead
- # of just returning the hash. This code works, but I can't figure out what's
- # going on.
- newfacts = PSON.parse(facts.to_pson)
- newfacts.expiration.should be_nil
- end
-
- it "should be able to handle nil values" do
- pson = %Q({"name": "foo", "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 be_nil
+ result['name'].should == facts.name
+ result['values'].should == facts.values.reject { |key, value| key.to_s =~ /_/ }
+ result['timestamp'].should == facts.timestamp.to_s
+ result['expiration'].should == facts.expiration.to_s
end
end
end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index e8f826dca..169a9cdcf 100755
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -36,69 +36,6 @@ describe Puppet::Node do
node.environment.name.should == :bar
end
end
-
- describe "when converting to json" do
- before do
- @node = Puppet::Node.new("mynode")
- end
-
- it "should provide its name" do
- @node.should set_json_attribute('name').to("mynode")
- end
-
- it "should include the classes if set" do
- @node.classes = %w{a b c}
- @node.should set_json_attribute("classes").to(%w{a b c})
- end
-
- it "should not include the classes if there are none" do
- @node.should_not set_json_attribute('classes')
- end
-
- it "should include parameters if set" do
- @node.parameters = {"a" => "b", "c" => "d"}
- @node.should set_json_attribute('parameters').to({"a" => "b", "c" => "d"})
- end
-
- it "should not include the parameters if there are none" do
- @node.should_not set_json_attribute('parameters')
- end
-
- it "should include the environment" do
- @node.environment = "production"
- @node.should set_json_attribute('environment').to('production')
- end
- end
-
- describe "when converting from json" do
- before do
- @node = Puppet::Node.new("mynode")
- @format = Puppet::Network::FormatHandler.format('pson')
- end
-
- def from_json(json)
- @format.intern(Puppet::Node, json)
- end
-
- it "should set its name" do
- Puppet::Node.should read_json_attribute('name').from(@node.to_pson).as("mynode")
- end
-
- it "should include the classes if set" do
- @node.classes = %w{a b c}
- Puppet::Node.should read_json_attribute('classes').from(@node.to_pson).as(%w{a b c})
- end
-
- it "should include parameters if set" do
- @node.parameters = {"a" => "b", "c" => "d"}
- Puppet::Node.should read_json_attribute('parameters').from(@node.to_pson).as({"a" => "b", "c" => "d"})
- end
-
- it "should include the environment" do
- @node.environment = "production"
- Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(Puppet::Node::Environment.new(:production))
- end
- end
end
describe Puppet::Node, "when initializing" do
diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb
index 48aeb98dd..88f7167f3 100755
--- a/spec/unit/parser/compiler_spec.rb
+++ b/spec/unit/parser/compiler_spec.rb
@@ -48,6 +48,13 @@ describe Puppet::Parser::Compiler do
end
before :each do
+ # Push me faster, I wanna go back in time! (Specifically, freeze time
+ # across the test since we have a bunch of version == timestamp code
+ # hidden away in the implementation and we keep losing the race.)
+ # --daniel 2011-04-21
+ now = Time.now
+ Time.stubs(:now).returns(now)
+
@node = Puppet::Node.new "testnode"
@known_resource_types = Puppet::Resource::TypeCollection.new "development"
@compiler = Puppet::Parser::Compiler.new(@node)
diff --git a/spec/unit/simple_graph_spec.rb b/spec/unit/simple_graph_spec.rb
index c8fea3b58..472ebbd36 100755
--- a/spec/unit/simple_graph_spec.rb
+++ b/spec/unit/simple_graph_spec.rb
@@ -525,6 +525,10 @@ describe Puppet::SimpleGraph do
def to_s
@name
end
+
+ def ref
+ "Container[#{self}]"
+ end
end
require "puppet/resource/catalog"
@@ -536,7 +540,7 @@ describe Puppet::SimpleGraph do
@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")
@@ -574,7 +578,7 @@ describe Puppet::SimpleGraph do
end
def whit_called(name)
- x = @depgraph.vertices.find { |v| v.is_a?(@whit) && v.name =~ /#{name}/ }
+ x = @depgraph.vertices.find { |v| v.is_a?(@whit) && v.name =~ /#{Regexp.escape(name)}/ }
x.should_not be_nil
def x.to_s
"Whit[#{name}]"
@@ -586,11 +590,11 @@ describe Puppet::SimpleGraph do
end
def admissible_sentinal_of(x)
- @depgraph.vertex?(x) ? x : whit_called("admissible_#{x.name}")
+ @depgraph.vertex?(x) ? x : whit_called("admissible_#{x.ref}")
end
def completed_sentinal_of(x)
- @depgraph.vertex?(x) ? x : whit_called("completed_#{x.name}")
+ @depgraph.vertex?(x) ? x : whit_called("completed_#{x.ref}")
end
before do
@@ -618,7 +622,7 @@ describe Puppet::SimpleGraph do
# 0) completed_X depends on admissible_X
#
it "every container's completed sentinal should depend on its admissible sentinal" do
- containers.each { |container|
+ containers.each { |container|
@depgraph.path_between(admissible_sentinal_of(container),completed_sentinal_of(container)).should be
}
end
@@ -626,7 +630,7 @@ describe Puppet::SimpleGraph do
# 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|
+ containers.each { |container|
contents_of(container).each { |leaf|
@depgraph.should be_edge(admissible_sentinal_of(container),admissible_sentinal_of(leaf))
}
@@ -636,7 +640,7 @@ describe Puppet::SimpleGraph do
# 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|
+ containers.each { |container|
contents_of(container).each { |leaf|
@depgraph.should be_edge(completed_sentinal_of(leaf),completed_sentinal_of(container))
}
diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb
index 2624c2234..d14da538e 100755
--- a/spec/unit/ssl/host_spec.rb
+++ b/spec/unit/ssl/host_spec.rb
@@ -681,16 +681,14 @@ describe Puppet::SSL::Host 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)
+ expect { @host.wait_for_cert(0) }.to exit_with 1
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)
+ expect { @host.wait_for_cert(0) }.to exit_with 1
end
it "should sleep for the specified amount of time if no certificate is found after generating its certificate request" do
diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb
index 820807638..0c572fd95 100755
--- a/spec/unit/status_spec.rb
+++ b/spec/unit/status_spec.rb
@@ -15,6 +15,10 @@ describe Puppet::Status do
Puppet::Status.new.status.to_pson.should == '{"is_alive":true}'
end
+ it "should render to a pson hash" do
+ PSON::pretty_generate(Puppet::Status.new).should =~ /"is_alive":\s*true/
+ end
+
it "should accept a hash from pson" do
status = Puppet::Status.new( { "is_alive" => false } )
status.status.should == { "is_alive" => false }
diff --git a/spec/unit/type/whit_spec.rb b/spec/unit/type/whit_spec.rb
index 4d0949900..9a076d1f1 100755
--- a/spec/unit/type/whit_spec.rb
+++ b/spec/unit/type/whit_spec.rb
@@ -5,6 +5,6 @@ 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)"
+ whit.to_s.should == "Foo::Bar"
end
end
diff --git a/spec/unit/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb
index 81612ee5c..d60bbb198 100755
--- a/spec/unit/util/command_line_spec.rb
+++ b/spec/unit/util/command_line_spec.rb
@@ -1,7 +1,7 @@
#!/usr/bin/env rspec
require 'spec_helper'
-
+require 'puppet/face'
require 'puppet/util/command_line'
describe Puppet::Util::CommandLine do
@@ -98,11 +98,9 @@ describe Puppet::Util::CommandLine do
Puppet::Util.expects(:which).with('puppet-whatever').returns(nil)
commandline.expects(:system).never
- text = Puppet::Face[:help, :current].help
- commandline.expects(:puts).with { |x| x =~ /Unknown Puppet subcommand/ }
- commandline.expects(:puts).with text
-
- commandline.execute
+ expect {
+ commandline.execute
+ }.to have_printed(/Unknown Puppet subcommand 'whatever'/)
end
end
end