summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Martin <max@puppetlabs.com>2011-06-02 15:46:17 -0700
committerMax Martin <max@puppetlabs.com>2011-06-02 15:46:17 -0700
commit4ad88017d3b8b8000325f5165520a6c21b48c469 (patch)
treecd126ce5723d8f2b390fda275f2e82e1dd8620d6
parent284113707fa07c6abcbb765dcd6d8c24e1b6b5fa (diff)
parentc295ae699ff78f3d7e4922d00dcf5d9e92c790b8 (diff)
downloadpuppet-4ad88017d3b8b8000325f5165520a6c21b48c469.tar.gz
puppet-4ad88017d3b8b8000325f5165520a6c21b48c469.tar.xz
puppet-4ad88017d3b8b8000325f5165520a6c21b48c469.zip
Merge branch '2.7rc' into 2.7.x
* 2.7rc: (24 commits) (#7746) Fix bootstrap issues from #7717 fix. (#7683) Use ronn, when available, to render the output. (#7683) Add a 'man' face and subcommand to Puppet. maint: remove obsolete work-around code from help face. (#7699) Don't duplicate inherited action names on faces. (#7177) Deprecate implicit 'puppet apply' for 2.7.0 (#7717) Layout cleanup for subcommand extraction. #7211: Test unknown options don't shadow unknown actions. #7211: nasty logic error with global Face options taking arguments. #7211: more helpful error messages in various cases. (#7708) Delete extended documentation from configuration reference (#7707) Document signals in puppet agent and puppet master help add puppet master polling step for ticket 7117 (#5318) Always notice changes to manifests when compiling. (#7557) Remove Faces Application maint: Fix order dependent spec failure for face indirection (#7690) Don't blow up when listing terminuses available for faces maint: Dedup the loadpath so we don't have to walk it multiple times Maint: Fix ellipses for short descriptions (#7563) DRY: Remove indirector boilerplate from individual faces ... Conflicts (resolved manually): acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb lib/puppet/application/faces.rb lib/puppet/face/help/man.erb lib/puppet/indirector/face.rb spec/shared_behaviours/documentation_on_faces.rb
-rw-r--r--acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb11
-rw-r--r--lib/puppet/application/agent.rb12
-rw-r--r--lib/puppet/application/face_base.rb28
-rw-r--r--lib/puppet/application/faces.rb122
-rw-r--r--lib/puppet/application/help.rb3
-rw-r--r--lib/puppet/application/man.rb4
-rw-r--r--lib/puppet/application/master.rb10
-rw-r--r--lib/puppet/face/help/man.erb1
-rw-r--r--lib/puppet/face/man.rb95
-rw-r--r--lib/puppet/indirector/face.rb4
-rw-r--r--lib/puppet/interface/action_manager.rb5
-rw-r--r--lib/puppet/reference/configuration.rb127
-rw-r--r--lib/puppet/util/command_line.rb91
-rwxr-xr-xspec/lib/puppet/face/basetest.rb5
-rw-r--r--spec/shared_behaviours/documentation_on_faces.rb4
-rwxr-xr-xspec/unit/application/face_base_spec.rb36
-rwxr-xr-xspec/unit/application/faces_spec.rb14
-rwxr-xr-xspec/unit/interface/action_manager_spec.rb50
18 files changed, 347 insertions, 275 deletions
diff --git a/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb b/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb
index 79f56c07a..07b427306 100644
--- a/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb
+++ b/acceptance/tests/ticket_7117_broke_env_criteria_authconf.rb
@@ -29,7 +29,16 @@ time2 = Time.new
elapsed = time2 - time1
Log.notify "Slept for #{elapsed} seconds waiting for Puppet Master to become ready"
-
+step "Verify Puppet Master is ready to accept connections"
+host=agents.first
+time1 = Time.new
+until
+ on(host, "curl -k https://#{master}:8140") do
+ sleep 1
+ end
+time2 = Time.new
+elapsed = time2 - time1
+Log.notify "Slept for #{elapsed} seconds waiting for Puppet Master to become ready"
# Run test on Agents
step "Agent: agent --test"
diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb
index 19849c57a..06a158fb3 100644
--- a/lib/puppet/application/agent.rb
+++ b/lib/puppet/application/agent.rb
@@ -281,6 +281,18 @@ EXAMPLE
$ puppet agent --server puppet.domain.com
+DIAGNOSTICS
+-----------
+
+Puppet agent accepts the following signals:
+
+* SIGHUP:
+ Restart the puppet agent daemon.
+* SIGINT and SIGTERM:
+ Shut down the puppet agent daemon.
+* SIGUSR1:
+ Immediately retrieve and apply configurations from the puppet master.
+
AUTHOR
------
Luke Kanies
diff --git a/lib/puppet/application/face_base.rb b/lib/puppet/application/face_base.rb
index 7a5ce3400..ea5ba4aaf 100644
--- a/lib/puppet/application/face_base.rb
+++ b/lib/puppet/application/face_base.rb
@@ -66,9 +66,9 @@ class Puppet::Application::FaceBase < Puppet::Application
# Now, walk the command line and identify the action. We skip over
# arguments based on introspecting the action and all, and find the first
# non-option word to use as the action.
- action = nil
- index = -1
- until @action or (index += 1) >= command_line.args.length do
+ action_name = nil
+ index = -1
+ until action_name or (index += 1) >= command_line.args.length do
item = command_line.args[index]
if item =~ /^-/ then
option = @face.options.find do |name|
@@ -91,12 +91,16 @@ class Puppet::Application::FaceBase < Puppet::Application
index += 1 # ...so skip the argument.
end
elsif option = find_application_argument(item) then
- index += 1 if (option[:argument] and option[:optional])
+ index += 1 if (option[:argument] and not option[:optional])
else
raise OptionParser::InvalidOption.new(item.sub(/=.*$/, ''))
end
else
- @action = @face.get_action(item.to_sym)
+ # Stash away the requested action name for later, and try to fetch the
+ # action object it represents; if this is an invalid action name that
+ # will be nil, and handled later.
+ action_name = item.to_sym
+ @action = @face.get_action(action_name)
end
end
@@ -104,8 +108,18 @@ class Puppet::Application::FaceBase < Puppet::Application
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)
+ # REVISIT: ...and this horror thanks to our log setup, which doesn't
+ # initialize destinations until the setup method, which we will never
+ # reach. We could also just print here, but that is actually a little
+ # uglier and nastier in the long term, in which we should do log setup
+ # earlier if at all possible. --daniel 2011-05-31
+ Puppet::Util::Log.newdestination(:console)
+
+ face = @face.name
+ action = action_name.nil? ? 'default' : "'#{action_name}'"
+ msg = "'#{face}' has no #{action} action. See `puppet help #{face}`."
+ Puppet.err(msg)
+
exit false
end
end
diff --git a/lib/puppet/application/faces.rb b/lib/puppet/application/faces.rb
deleted file mode 100644
index 3145da821..000000000
--- a/lib/puppet/application/faces.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-require 'puppet/application'
-require 'puppet/face'
-
-class Puppet::Application::Faces < Puppet::Application
-
- should_parse_config
- run_mode :agent
-
- option("--debug", "-d") do |arg|
- Puppet::Util::Log.level = :debug
- end
-
- option("--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}
- end
- faces.each do |name|
- str = "#{name}:\n"
- if arguments.include?("terminuses")
- begin
- terms = Puppet::Indirector::Face.terminus_classes(name.to_sym)
- str << "\tTerminuses: #{terms.join(", ")}\n"
- rescue => detail
- puts detail.backtrace if Puppet[:trace]
- $stderr.puts "Could not load terminuses for #{name}: #{detail}"
- end
- end
-
- if arguments.include?("actions")
- begin
- actions = actions(name.to_sym)
- str << "\tActions: #{actions.join(", ")}\n"
- rescue => detail
- puts detail.backtrace if Puppet[:trace]
- $stderr.puts "Could not load actions for #{name}: #{detail}"
- end
- end
-
- print str
- end
- end
-
- attr_accessor :name, :arguments
-
- def main
- list(*arguments)
- end
-
- def setup
- Puppet::Util::Log.newdestination :console
-
- load_applications # Call this to load all of the apps
-
- @arguments = command_line.args
- @arguments ||= []
- end
-
- def faces
- Puppet::Face.faces
- end
-
- def actions(indirection)
- return [] unless face = Puppet::Face[indirection, '0.0.1']
- face.load_actions
- return face.actions.sort { |a, b| a.to_s <=> b.to_s }
- end
-
- def load_applications
- command_line.available_subcommands.each do |app|
- command_line.require_application app
- end
- end
-end
-
diff --git a/lib/puppet/application/help.rb b/lib/puppet/application/help.rb
index 4829a2036..66baa462e 100644
--- a/lib/puppet/application/help.rb
+++ b/lib/puppet/application/help.rb
@@ -1,7 +1,4 @@
require 'puppet/application/face_base'
class Puppet::Application::Help < Puppet::Application::FaceBase
- # Meh. Disable the default behaviour, which is to inspect the
- # string and return that – not so helpful. --daniel 2011-04-11
- def render(result) result end
end
diff --git a/lib/puppet/application/man.rb b/lib/puppet/application/man.rb
new file mode 100644
index 000000000..1ecc4d691
--- /dev/null
+++ b/lib/puppet/application/man.rb
@@ -0,0 +1,4 @@
+require 'puppet/application/face_base'
+
+class Puppet::Application::Man < Puppet::Application::FaceBase
+end
diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb
index a90829ae0..18425c8bc 100644
--- a/lib/puppet/application/master.rb
+++ b/lib/puppet/application/master.rb
@@ -102,6 +102,16 @@ EXAMPLE
-------
puppet master
+DIAGNOSTICS
+-----------
+
+When running as a standalone daemon, puppet master accepts the
+following signals:
+
+* SIGHUP:
+ Restart the puppet master server.
+* SIGINT and SIGTERM:
+ Shut down the puppet master server.
AUTHOR
------
diff --git a/lib/puppet/face/help/man.erb b/lib/puppet/face/help/man.erb
index 899d0166c..6f21fe413 100644
--- a/lib/puppet/face/help/man.erb
+++ b/lib/puppet/face/help/man.erb
@@ -130,4 +130,3 @@ COPYRIGHT AND LICENSE
---------------------
<%= face.copyright %>
<%= face.license %>
-
diff --git a/lib/puppet/face/man.rb b/lib/puppet/face/man.rb
new file mode 100644
index 000000000..38b9202eb
--- /dev/null
+++ b/lib/puppet/face/man.rb
@@ -0,0 +1,95 @@
+require 'puppet/face'
+require 'puppet/util'
+require 'pathname'
+require 'erb'
+
+Puppet::Face.define(:man, '0.0.1') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Display Puppet subcommand manual pages."
+
+ description <<-EOT
+ The man face, when invoked from the command line, tries very hard to
+ behave nicely for interactive use. If possible, it delegates to the
+ ronn(1) command to format the output as a real manual page.
+
+ If ronn(1) is not available, it will use the first of `$MANPAGER`,
+ `$PAGER`, `less`, `most`, or `more` to paginate the (human-readable)
+ input text for the manual page.
+
+ We do try hard to ensure that this behaves correctly when used as
+ part of a pipeline. (Well, we delegate to tools that do the right
+ thing, which is more or less the same.)
+ EOT
+
+ notes <<-EOT
+ We strongly encourage you to install the `ronn` gem on your system,
+ or otherwise make it available, so that we can display well structured
+ output from this face.
+ EOT
+
+ action(:man) do
+ summary "Display the manual page for a face."
+ arguments "<face>"
+ returns "The man data, in markdown format, suitable for consumption by Ronn."
+ examples <<-'EOT'
+ Get the manual page for a face:
+
+ $ puppet man facts
+ EOT
+
+ default
+ when_invoked do |name, options|
+ if legacy_applications.include? name then
+ return Puppet::Application[name].help
+ end
+
+ face = Puppet::Face[name.to_sym, :current]
+
+ file = (Pathname(__FILE__).dirname + "help" + 'man.erb')
+ erb = ERB.new(file.read, nil, '-')
+ erb.filename = file.to_s
+
+ # Run the ERB template in our current binding, including all the local
+ # variables we established just above. --daniel 2011-04-11
+ return erb.result(binding)
+ end
+
+
+ when_rendering :console do |text|
+ # OK, if we have Ronn on the path we can delegate to it and override the
+ # normal output process. Otherwise delegate to a pager on the raw text,
+ # otherwise we finally just delegate to our parent. Oh, well.
+ ENV['LESS'] ||= 'FRSX' # emulate git...
+
+ ronn = Puppet::Util.which('ronn')
+ pager = [ENV['MANPAGER'], ENV['PAGER'], 'less', 'most', 'more'].
+ detect {|x| x and x.length > 0 and Puppet::Util.which(x) }
+
+ if ronn then
+ # ronn is a stupid about pager selection, we can be smarter. :)
+ if pager then ENV['PAGER'] = pager end
+
+ args = "--man --manual='Puppet Manual' --organization='Puppet Labs, LLC'"
+ IO.popen("#{ronn} #{args}", 'w') do |fh| fh.write text end
+
+ '' # suppress local output, neh?
+ elsif pager then
+ IO.popen(pager, 'w') do |fh| fh.write text end
+ ''
+ else
+ text
+ end
+ end
+ end
+
+ def legacy_applications
+ # The list of applications, less those that are duplicated as a face.
+ Puppet::Util::CommandLine.available_subcommands.reject do |appname|
+ Puppet::Face.face? appname.to_sym, :current or
+ # ...this is a nasty way to exclude non-applications. :(
+ %w{face_base indirection_base}.include? appname
+ end
+ end
+end
diff --git a/lib/puppet/indirector/face.rb b/lib/puppet/indirector/face.rb
index f6c36e31d..756306a2f 100644
--- a/lib/puppet/indirector/face.rb
+++ b/lib/puppet/indirector/face.rb
@@ -9,12 +9,12 @@ class Puppet::Indirector::Face < Puppet::Face
(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.
diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb
index c5eb8e08a..fbf588d7d 100644
--- a/lib/puppet/interface/action_manager.rb
+++ b/lib/puppet/interface/action_manager.rb
@@ -34,7 +34,10 @@ module Puppet::Interface::ActionManager
elsif self.class.respond_to?(:actions)
result += self.class.actions
end
- result.sort
+ # We need to uniq the result, because we duplicate actions when they are
+ # fetched to ensure that they have the correct bindings; they shadow the
+ # parent, and uniq implements that. --daniel 2011-06-01
+ result.uniq.sort
end
def get_action(name)
diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb
index 6581427ff..18efb6fe7 100644
--- a/lib/puppet/reference/configuration.rb
+++ b/lib/puppet/reference/configuration.rb
@@ -41,104 +41,29 @@ config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc
return str
end
-config.header = "
-## Specifying Configuration Parameters
-
-### On The Command-Line
-
-Every Puppet executable (with the exception of `puppetdoc`) accepts all of
-the parameters below, but not all of the arguments make sense for every executable.
-
-I have tried to be as thorough as possible in the descriptions of the
-arguments, so it should be obvious whether an argument is appropriate or not.
-
-These parameters can be supplied to the executables either as command-line
-options or in the configuration file. For instance, the command-line
-invocation below would set the configuration directory to `/private/puppet`:
-
- $ puppet agent --confdir=/private/puppet
-
-Note that boolean options are turned on and off with a slightly different
-syntax on the command line:
-
- $ puppet agent --storeconfigs
-
- $ puppet agent --no-storeconfigs
-
-The invocations above will enable and disable, respectively, the storage of
-the client configuration.
-
-### Configuration Files
-
-As mentioned above, the configuration parameters can also be stored in a
-configuration file, located in the configuration directory. As root, the
-default configuration directory is `/etc/puppet`, and as a regular user, the
-default configuration directory is `~user/.puppet`. As of 0.23.0, all
-executables look for `puppet.conf` in their configuration directory
-(although they previously looked for separate files). For example,
-`puppet.conf` is located at `/etc/puppet/puppet.conf` as `root` and
-`~user/.puppet/puppet.conf` as a regular user by default.
-
-All executables will set any parameters set within the `[main]` section,
-and each executable will also use one of the `[master]`, `[agent]`.
-
-#### File Format
-
-The file follows INI-style formatting. Here is an example of a very simple
-`puppet.conf` file:
-
- [main]
- confdir = /private/puppet
- storeconfigs = true
-
-Note that boolean parameters must be explicitly specified as `true` or
-`false` as seen above.
-
-If you need to change file or directory parameters (e.g., reset the mode or owner), do
-so within curly braces on the same line:
-
- [main]
- vardir = /new/vardir {owner = root, mode = 644}
-
-If you're starting out with a fresh configuration, you may wish to let
-the executable generate a template configuration file for you by invoking
-the executable in question with the `--genconfig` command. The executable
-will print a template configuration to standard output, which can be
-redirected to a file like so:
-
- $ puppet agent --genconfig > /etc/puppet/puppet.conf
-
-Note that this invocation will replace the contents of any pre-existing
-`puppet.conf` file, so make a backup of your present config if it contains
-valuable information.
-
-Like the `--genconfig` argument, the executables also accept a `--genmanifest`
-argument, which will generate a manifest that can be used to manage all of
-Puppet's directories and files and prints it to standard output. This can
-likewise be redirected to a file:
-
- $ puppet agent --genmanifest > /etc/puppet/manifests/site.pp
-
-Puppet can also create user and group accounts for itself (one `puppet` group
-and one `puppet` user) if it is invoked as `root` with the `--mkusers` argument:
-
- $ puppet master --mkusers
-
-## Signals
-
-The `puppet agent` and `puppet master` executables catch some signals for special
-handling. Both daemons catch (`SIGHUP`), which forces the server to restart
-tself. Predictably, interrupt and terminate (`SIGINT` and `SIGTERM`) will shut
-down the server, whether it be an instance of `puppet agent` or `puppet master`.
-
-Sending the `SIGUSR1` signal to an instance of `puppet agent` will cause it to
-immediately begin a new configuration transaction with the server. This
-signal has no effect on `puppet master`.
-
-## Configuration Parameter Reference
-
-Below is a list of all documented parameters. Not all of them are valid with all
-Puppet executables, but the executables will ignore any inappropriate values.
-
-"
-
+config.header = <<EOT
+## Configuration Settings
+
+* Each of these settings can be specified in `puppet.conf` or on the
+ command line.
+* When using boolean settings on the command line, use `--setting` and
+ `--no-setting` instead of `--setting (true|false)`.
+* Settings can be interpolated as `$variables` in other settings; `$environment`
+ is special, in that puppet master will interpolate each agent node's
+ environment instead of its own.
+* Multiple values should be specified as comma-separated lists; multiple
+ directories should be separated with the system path separator (usually
+ a colon).
+* Settings that take a single file or directory can optionally set the owner,
+ group, and mode for their value: `rundir = $vardir/run { owner = puppet,
+ group = puppet, mode = 644 }`
+* The Puppet executables will ignore any setting that isn't relevant to
+ their function.
+
+See the [configuration guide][confguide] for more details.
+
+[confguide]: http://docs.puppetlabs.com/guides/configuring.html
+
+* * *
+
+EOT
diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb
index 8190f8ac1..244e2c2c9 100644
--- a/lib/puppet/util/command_line.rb
+++ b/lib/puppet/util/command_line.rb
@@ -59,6 +59,13 @@ module Puppet
require_application subcommand_name
app = Puppet::Application.find(subcommand_name).new(self)
Puppet::Plugins.on_application_initialization(:appliation_object => self)
+
+ # See the note in 'warn_later' down below. --daniel 2011-06-01
+ if $delayed_deprecation_warning_for_p_u_cl.is_a? String then
+ Puppet.deprecation_warning($delayed_deprecation_warning_for_p_u_cl)
+ $delayed_deprecation_warning_for_p_u_cl = true
+ end
+
app.run
elsif execute_external_subcommand then
# Logically, we shouldn't get here, but we do, so whatever. We just
@@ -93,16 +100,90 @@ module Puppet
if zero == 'puppet'
case argv.first
- when nil; [ stdin.tty? ? nil : "apply", argv] # ttys get usage info
- when "--help", "-h"; [nil, argv] # help should give you usage, not the help for `puppet apply`
- when /^-|\.pp$|\.rb$/; ["apply", argv]
- else [ argv.first, argv[1..-1] ]
+ when nil then
+ if stdin.tty? then
+ [nil, argv] # ttys get usage info
+ else
+ # Killed for 2.7.0 --daniel 2011-06-01
+ warn_later <<EOM
+Implicit invocation of 'puppet apply' by redirection into 'puppet' is deprecated,
+and will be removed in the 2.8 series. Please invoke 'puppet apply' directly
+in the future.
+EOM
+ ["apply", argv]
+ end
+ when "--help", "-h" then
+ # help should give you usage, not the help for `puppet apply`
+ [nil, argv]
+ when /^-|\.pp$|\.rb$/ then
+ # Killed for 2.7.0 --daniel 2011-06-01
+ warn_later <<EOM
+Implicit invocation of 'puppet apply' by passing files (or flags) directly
+to 'puppet' is deprecated, and will be removed in the 2.8 series. Please
+invoke 'puppet apply' directly in the future.
+EOM
+ ["apply", argv]
+ else
+ [argv.first, argv[1..-1]]
end
else
- [ zero, argv ]
+ [zero, argv]
end
end
+ # So, this is more than a little bit of a horror. You see, the process
+ # of bootstrapping Puppet is ... complex. This file, like many of our
+ # early initialization files, has an incestuous relationship between the
+ # order of files loaded, code executed at load time, and code executed
+ # in other files at runtime.
+ #
+ # When we construct this object we have not yet actually loaded the
+ # global puppet object, so we can't use any methods in it. That
+ # includes all the logging stuff, which is used by the deprecation
+ # warning subsystem.
+ #
+ # On the other hand, we can't just load the logging system, because that
+ # depends on the top level Puppet module being bootstrapped. It doesn't
+ # actually load the stuff it uses, though, for hysterical raisins.
+ #
+ # Finally, we can't actually just load the top level Puppet module.
+ # This one is precious: it turns out that some of the code loaded in the
+ # top level Puppet module has a dependency on the run mode values.
+ #
+ # Run mode is set correctly *only* when the application is loaded, and
+ # if it is wrong when the top level code is brought in we end up with
+ # the wrong settings scattered through some of the defaults.
+ #
+ # Which means that we have a dependency cycle that runs:
+ # 1. The binary creates an instance of P::U::CL.
+ # 2. That identifies the application to load.
+ # 3. It does, then instantiates the application.
+ # 4. That sets the run-mode.
+ # 5. That then loads the top level Puppet module.
+ # 6. Finally, we get to where we can use the top level stuff
+ #
+ # So, essentially, we see a dependency between runtime code in this
+ # file, run-time code in the application, and load-time code in the top
+ # level module.
+ #
+ # Which leads me to our current horrible hack: we stash away the message
+ # we wanted to log about deprecation, then send it to our logging system
+ # once we have done enough bootstrapping that it will, y'know, actually
+ # work.
+ #
+ # I would have liked to fix this, but that is going to be a whole pile
+ # of work digging through and decrufting all the global state from the
+ # local state, and working out what depends on what else in the product.
+ #
+ # Oh, and we use a global because we have *two* instances of a P::U::CL
+ # object during the startup sequence. I don't know why.
+ #
+ # Maybe, one day, when I am not behind a deadline to ship code.
+ # --daniel 2011-06-01
+ def warn_later(text)
+ return if $delayed_deprecation_warning_for_p_u_cl
+ $delayed_deprecation_warning_for_p_u_cl = text
+ end
end
end
end
diff --git a/spec/lib/puppet/face/basetest.rb b/spec/lib/puppet/face/basetest.rb
index 9398f5b2d..e5c9a9dc5 100755
--- a/spec/lib/puppet/face/basetest.rb
+++ b/spec/lib/puppet/face/basetest.rb
@@ -38,4 +38,9 @@ Puppet::Face.define(:basetest, '0.0.1') do
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
+
+ action :count_args do
+ summary "return the count of arguments given"
+ when_invoked do |*args| args.length - 1 end
+ end
end
diff --git a/spec/shared_behaviours/documentation_on_faces.rb b/spec/shared_behaviours/documentation_on_faces.rb
index 1106d4cca..204b173a5 100644
--- a/spec/shared_behaviours/documentation_on_faces.rb
+++ b/spec/shared_behaviours/documentation_on_faces.rb
@@ -115,11 +115,11 @@ shared_examples_for "documentation on faces" do
subject.short_description.should == (line * 5).chomp + ' [...]'
end
-
+
it "should trim a very very long only paragraph even if it is followed by a new paragraph" do
line = "this is a very, very, very long long line full of text\n"
subject.description = line * 20
-
+
subject.short_description.should == (line * 5).chomp + ' [...]'
end
end
diff --git a/spec/unit/application/face_base_spec.rb b/spec/unit/application/face_base_spec.rb
index 3318e061e..0a4a86be6 100755
--- a/spec/unit/application/face_base_spec.rb
+++ b/spec/unit/application/face_base_spec.rb
@@ -52,6 +52,12 @@ describe Puppet::Application::FaceBase do
end
end
+ it "should stop if the first thing found is not an action" do
+ app.command_line.stubs(:args).returns %w{banana count_args}
+ expect { app.run }.to exit_with 1
+ @logs.first.message.should =~ /has no 'banana' action/
+ end
+
it "should use the default action if not given any arguments" do
app.command_line.stubs(:args).returns []
action = stub(:options => [], :render_as => nil)
@@ -77,7 +83,29 @@ describe Puppet::Application::FaceBase do
Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil)
app.stubs(:main)
expect { app.run }.to exit_with 1
- @logs.first.message.should =~ /does not have a default action/
+ @logs.first.message.should =~ /has no 'bar' action./
+ end
+
+ [%w{something_I_cannot_do},
+ %w{something_I_cannot_do argument}].each do |input|
+ it "should report unknown actions nicely" do
+ app.command_line.stubs(:args).returns input
+ Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil)
+ app.stubs(:main)
+ expect { app.run }.to exit_with 1
+ @logs.first.message.should =~ /has no 'something_I_cannot_do' action/
+ end
+ end
+
+ [%w{something_I_cannot_do --unknown-option},
+ %w{something_I_cannot_do argument --unknown-option}].each do |input|
+ it "should report unknown actions even if there are unknown options" do
+ app.command_line.stubs(:args).returns input
+ Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil)
+ app.stubs(:main)
+ expect { app.run }.to exit_with 1
+ @logs.first.message.should =~ /has no 'something_I_cannot_do' action/
+ end
end
it "should report a sensible error when options with = fail" do
@@ -149,7 +177,7 @@ describe Puppet::Application::FaceBase do
end
it "should handle application-level options", :'fails_on_ruby_1.9.2' => true do
- app.command_line.stubs(:args).returns %w{basetest --verbose return_true}
+ app.command_line.stubs(:args).returns %w{--verbose return_true}
app.preinit
app.parse_options
app.face.name.should == :basetest
@@ -304,7 +332,7 @@ EOT
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}
+ app.command_line.stubs(:args).returns %w{--render-as interpretive-dance return_true}
# 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
@@ -315,7 +343,7 @@ EOT
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}
+ app.command_line.stubs(:args).returns %w{count_args a b c --render-as yaml}
expect {
expect { app.run }.to exit_with 0
}.to have_printed(/--- 3/)
diff --git a/spec/unit/application/faces_spec.rb b/spec/unit/application/faces_spec.rb
deleted file mode 100755
index cc159b6a5..000000000
--- a/spec/unit/application/faces_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env rspec
-require 'spec_helper'
-require 'puppet/application/faces'
-
-describe Puppet::Application::Faces do
- it "should be an application" do
- Puppet::Application::Faces.superclass.should equal(Puppet::Application)
- end
-
- it "should always call 'list'" do
- subject.expects(:list)
- subject.main
- end
-end
diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb
index e357a5fa1..3a84e4f83 100755
--- a/spec/unit/interface/action_manager_spec.rb
+++ b/spec/unit/interface/action_manager_spec.rb
@@ -186,23 +186,49 @@ describe Puppet::Interface::ActionManager do
@instance.should be_action(:foo)
end
- it "should list actions defined in superclasses" do
- @subclass = Class.new(@klass)
- @instance = @subclass.new
+ context "with actions defined in superclass" do
+ before :each do
+ @subclass = Class.new(@klass)
+ @instance = @subclass.new
+
+ @klass.action(:parent) do
+ when_invoked { |options| "a" }
+ end
+ @subclass.action(:sub) do
+ when_invoked { |options| "a" }
+ end
+ @instance.action(:instance) do
+ when_invoked { |options| "a" }
+ end
+ end
+
+ it "should list actions defined in superclasses" do
+ @instance.should be_action(:parent)
+ @instance.should be_action(:sub)
+ @instance.should be_action(:instance)
+ end
- @klass.action(:parent) do
- when_invoked { |options| "a" }
+ it "should list inherited actions" do
+ @instance.actions.should =~ [:instance, :parent, :sub]
end
- @subclass.action(:sub) do
- when_invoked { |options| "a" }
+
+ it "should not duplicate instance actions after fetching them (#7699)" do
+ @instance.actions.should =~ [:instance, :parent, :sub]
+ @instance.get_action(:instance)
+ @instance.actions.should =~ [:instance, :parent, :sub]
end
- @instance.action(:instance) do
- when_invoked { |options| "a" }
+
+ it "should not duplicate subclass actions after fetching them (#7699)" do
+ @instance.actions.should =~ [:instance, :parent, :sub]
+ @instance.get_action(:sub)
+ @instance.actions.should =~ [:instance, :parent, :sub]
end
- @instance.should be_action(:parent)
- @instance.should be_action(:sub)
- @instance.should be_action(:instance)
+ it "should not duplicate superclass actions after fetching them (#7699)" do
+ @instance.actions.should =~ [:instance, :parent, :sub]
+ @instance.get_action(:parent)
+ @instance.actions.should =~ [:instance, :parent, :sub]
+ end
end
it "should create an instance method when an action is defined in a superclass" do