diff options
author | Max Martin <max@puppetlabs.com> | 2011-06-02 15:46:17 -0700 |
---|---|---|
committer | Max Martin <max@puppetlabs.com> | 2011-06-02 15:46:17 -0700 |
commit | 4ad88017d3b8b8000325f5165520a6c21b48c469 (patch) | |
tree | cd126ce5723d8f2b390fda275f2e82e1dd8620d6 | |
parent | 284113707fa07c6abcbb765dcd6d8c24e1b6b5fa (diff) | |
parent | c295ae699ff78f3d7e4922d00dcf5d9e92c790b8 (diff) | |
download | puppet-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.rb | 11 | ||||
-rw-r--r-- | lib/puppet/application/agent.rb | 12 | ||||
-rw-r--r-- | lib/puppet/application/face_base.rb | 28 | ||||
-rw-r--r-- | lib/puppet/application/faces.rb | 122 | ||||
-rw-r--r-- | lib/puppet/application/help.rb | 3 | ||||
-rw-r--r-- | lib/puppet/application/man.rb | 4 | ||||
-rw-r--r-- | lib/puppet/application/master.rb | 10 | ||||
-rw-r--r-- | lib/puppet/face/help/man.erb | 1 | ||||
-rw-r--r-- | lib/puppet/face/man.rb | 95 | ||||
-rw-r--r-- | lib/puppet/indirector/face.rb | 4 | ||||
-rw-r--r-- | lib/puppet/interface/action_manager.rb | 5 | ||||
-rw-r--r-- | lib/puppet/reference/configuration.rb | 127 | ||||
-rw-r--r-- | lib/puppet/util/command_line.rb | 91 | ||||
-rwxr-xr-x | spec/lib/puppet/face/basetest.rb | 5 | ||||
-rw-r--r-- | spec/shared_behaviours/documentation_on_faces.rb | 4 | ||||
-rwxr-xr-x | spec/unit/application/face_base_spec.rb | 36 | ||||
-rwxr-xr-x | spec/unit/application/faces_spec.rb | 14 | ||||
-rwxr-xr-x | spec/unit/interface/action_manager_spec.rb | 50 |
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 |