diff --git a/lib/puppet/application/doc.rb b/lib/puppet/application/doc.rb index 99a2ad346..866619231 100644 --- a/lib/puppet/application/doc.rb +++ b/lib/puppet/application/doc.rb @@ -1,280 +1,280 @@ require 'puppet/application' class Puppet::Application::Doc < Puppet::Application run_mode :master attr_accessor :unknown_args, :manifest def preinit {:references => [], :mode => :text, :format => :to_markdown }.each do |name,value| options[name] = value end @unknown_args = [] @manifest = false end option("--all","-a") option("--outputdir OUTPUTDIR","-o") option("--verbose","-v") option("--debug","-d") option("--charset CHARSET") option("--format FORMAT", "-f") do |arg| method = "to_#{arg}" require 'puppet/util/reference' if Puppet::Util::Reference.method_defined?(method) options[:format] = method else raise "Invalid output format #{arg}" end end option("--mode MODE", "-m") do |arg| require 'puppet/util/reference' if Puppet::Util::Reference.modes.include?(arg) or arg.intern==:rdoc options[:mode] = arg.intern else raise "Invalid output mode #{arg}" end end option("--list", "-l") do |arg| require 'puppet/util/reference' puts Puppet::Util::Reference.references.collect { |r| Puppet::Util::Reference.reference(r).doc }.join("\n") exit(0) end option("--reference REFERENCE", "-r") do |arg| options[:references] << arg.intern end def help <<-'HELP' puppet-doc(8) -- Generate Puppet documentation and references ======== SYNOPSIS -------- Generates a reference for all Puppet types. Largely meant for internal Puppet Labs use. WARNING: RDoc support is only available under Ruby 1.8.7 and earlier. USAGE ----- puppet doc [-a|--all] [-h|--help] [-o|--outputdir ] [-m|--mode text|pdf|rdoc] [-r|--reference ] [--charset ] [] DESCRIPTION ----------- If mode is not 'rdoc', then this command generates a Markdown document describing all installed Puppet types or all allowable arguments to puppet executables. It is largely meant for internal use and is used to generate the reference document available on the Puppet Labs web site. In 'rdoc' mode, this command generates an html RDoc hierarchy describing the manifests that are in 'manifestdir' and 'modulepath' configuration directives. The generated documentation directory is doc by default but can be changed with the 'outputdir' option. If the command is run with the name of a manifest file as an argument, puppet doc will output a single manifest's documentation on stdout. WARNING: RDoc support is only available under Ruby 1.8.7 and earlier. The internal API used to support manifest documentation has changed radically in newer versions, and support is not yet available for using those versions of RDoc. OPTIONS ------- * --all: Output the docs for all of the reference types. In 'rdoc' mode, this also outputs documentation for all resources. * --help: Print this help message * --outputdir: Used only in 'rdoc' mode. The directory to which the rdoc output should be written. * --mode: Determine the output mode. Valid modes are 'text', 'pdf' and 'rdoc'. The 'pdf' mode creates PDF formatted files in the /tmp directory. The default mode is 'text'. * --reference: Build a particular reference. Get a list of references by running 'puppet doc --list'. * --charset: Used only in 'rdoc' mode. It sets the charset used in the html files produced. * --manifestdir: Used only in 'rdoc' mode. The directory to scan for stand-alone manifests. If not supplied, puppet doc will use the manifestdir from puppet.conf. * --modulepath: Used only in 'rdoc' mode. The directory or directories to scan for modules. If not supplied, puppet doc will use the modulepath from puppet.conf. * --environment: Used only in 'rdoc' mode. The configuration environment from which to read the modulepath and manifestdir settings, when reading said settings from puppet.conf. EXAMPLE ------- $ puppet doc -r type > /tmp/type_reference.markdown or $ puppet doc --outputdir /tmp/rdoc --mode rdoc /path/to/manifests or $ puppet doc /etc/puppet/manifests/site.pp or $ puppet doc -m pdf -r configuration AUTHOR ------ Luke Kanies COPYRIGHT --------- Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def handle_unknown( opt, arg ) @unknown_args << {:opt => opt, :arg => arg } true end def run_command return[:rdoc].include?(options[:mode]) ? send(options[:mode]) : other end def rdoc exit_code = 0 files = [] unless @manifest env = Puppet.lookup(:environments).get(Puppet[:environment]) files += env.modulepath - files << ::File.dirname(env[:manifest]) + files << ::File.dirname(env.manifest) end files += command_line.args Puppet.info "scanning: #{files.inspect}" Puppet.settings[:document_all] = options[:all] || false begin require 'puppet/util/rdoc' if @manifest Puppet::Util::RDoc.manifestdoc(files) else options[:outputdir] = "doc" unless options[:outputdir] Puppet::Util::RDoc.rdoc(options[:outputdir], files, options[:charset]) end rescue => detail Puppet.log_exception(detail, "Could not generate documentation: #{detail}") exit_code = 1 end exit exit_code end def other text = "" with_contents = options[:references].length <= 1 exit_code = 0 require 'puppet/util/reference' options[:references].sort { |a,b| a.to_s <=> b.to_s }.each do |name| raise "Could not find reference #{name}" unless section = Puppet::Util::Reference.reference(name) begin # Add the per-section text, but with no ToC text += section.send(options[:format], with_contents) rescue => detail Puppet.log_exception(detail, "Could not generate reference #{name}: #{detail}") exit_code = 1 next end end text += Puppet::Util::Reference.footer unless with_contents # We've only got one reference if options[:mode] == :pdf Puppet::Util::Reference.pdf(text) else puts text end exit exit_code end def setup # sole manifest documentation if command_line.args.size > 0 options[:mode] = :rdoc @manifest = true end if options[:mode] == :rdoc setup_rdoc else setup_reference end setup_logging end def setup_reference if options[:all] # Don't add dynamic references to the "all" list. require 'puppet/util/reference' options[:references] = Puppet::Util::Reference.references.reject do |ref| Puppet::Util::Reference.reference(ref).dynamic? end end options[:references] << :type if options[:references].empty? end def setup_rdoc(dummy_argument=:work_arround_for_ruby_GC_bug) # consume the unknown options # and feed them as settings if @unknown_args.size > 0 @unknown_args.each do |option| # force absolute path for modulepath when passed on commandline if option[:opt]=="--modulepath" or option[:opt] == "--manifestdir" option[:arg] = option[:arg].split(::File::PATH_SEPARATOR).collect { |p| ::File.expand_path(p) }.join(::File::PATH_SEPARATOR) end Puppet.settings.handlearg(option[:opt], option[:arg]) end end end def setup_logging # Handle the logging settings. if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info else Puppet::Util::Log.level = :warning end Puppet::Util::Log.newdestination(:console) end end diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb index 2950388e2..a3835bfa0 100644 --- a/lib/puppet/face/parser.rb +++ b/lib/puppet/face/parser.rb @@ -1,67 +1,68 @@ require 'puppet/face' require 'puppet/parser' Puppet::Face.define(:parser, '0.0.1') do 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." arguments "[] [ ...]" returns "Nothing, or the first syntax error encountered." 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 examples <<-'EOT' Validate the default site manifest at /etc/puppet/manifests/site.pp: $ puppet parser validate Validate two arbitrary manifest files: $ puppet parser validate init.pp vhost.pp Validate from STDIN: $ cat init.pp | puppet parser validate EOT when_invoked do |*args| args.pop files = args if files.empty? if not STDIN.tty? Puppet[:code] = STDIN.read validate_manifest else - files << Puppet[:manifest] - Puppet.notice "No manifest specified. Validating the default manifest #{Puppet[:manifest]}" + manifest = Puppet.lookup(:current_environment).manifest + files << manifest + Puppet.notice "No manifest specified. Validating the default manifest #{manifest}" end end missing_files = [] files.each do |file| missing_files << file if ! Puppet::FileSystem.exist?(file) validate_manifest(file) end raise Puppet::Error, "One or more file(s) specified did not exist:\n#{missing_files.collect {|f| " " * 3 + f + "\n"}}" if ! missing_files.empty? nil end end # @api private def validate_manifest(manifest = nil) configured_environment = Puppet.lookup(:environments).get(Puppet[:environment]) validation_environment = manifest ? configured_environment.override_with(:manifest => manifest) : configured_environment validation_environment.known_resource_types.clear rescue => detail Puppet.log_exception(detail) exit(1) end end diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb index d8e924c43..58bfdd54a 100755 --- a/spec/unit/application/doc_spec.rb +++ b/spec/unit/application/doc_spec.rb @@ -1,333 +1,333 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/application/doc' require 'puppet/util/reference' require 'puppet/util/rdoc' describe Puppet::Application::Doc do before :each do @doc = Puppet::Application[:doc] @doc.stubs(:puts) @doc.preinit Puppet::Util::Log.stubs(:newdestination) end it "should declare an other command" do @doc.should respond_to(:other) end it "should declare a rdoc command" do @doc.should respond_to(:rdoc) end it "should declare a fallback for unknown options" do @doc.should respond_to(:handle_unknown) end it "should declare a preinit block" do @doc.should respond_to(:preinit) end describe "in preinit" do it "should set references to []" do @doc.preinit @doc.options[:references].should == [] end it "should init mode to text" do @doc.preinit @doc.options[:mode].should == :text end it "should init format to to_markdown" do @doc.preinit @doc.options[:format].should == :to_markdown end end describe "when handling options" do [:all, :outputdir, :verbose, :debug, :charset].each do |option| it "should declare handle_#{option} method" do @doc.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @doc.options.expects(:[]=).with(option, 'arg') @doc.send("handle_#{option}".to_sym, 'arg') end end it "should store the format if valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(true) @doc.handle_format('format') @doc.options[:format].should == 'to_format' end it "should raise an error if the format is not valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(false) expect { @doc.handle_format('format') }.to raise_error(RuntimeError, /Invalid output format/) end it "should store the mode if valid" do Puppet::Util::Reference.stubs(:modes).returns(stub('mode', :include? => true)) @doc.handle_mode('mode') @doc.options[:mode].should == :mode end it "should store the mode if :rdoc" do Puppet::Util::Reference.modes.stubs(:include?).with('rdoc').returns(false) @doc.handle_mode('rdoc') @doc.options[:mode].should == :rdoc end it "should raise an error if the mode is not valid" do Puppet::Util::Reference.modes.stubs(:include?).with('unknown').returns(false) expect { @doc.handle_mode('unknown') }.to raise_error(RuntimeError, /Invalid output mode/) end it "should list all references on list and exit" do reference = stubs 'reference' ref = stubs 'ref' Puppet::Util::Reference.stubs(:references).returns([reference]) Puppet::Util::Reference.expects(:reference).with(reference).returns(ref) ref.expects(:doc) expect { @doc.handle_list(nil) }.to exit_with 0 end it "should add reference to references list with --reference" do @doc.options[:references] = [:ref1] @doc.handle_reference('ref2') @doc.options[:references].should == [:ref1,:ref2] end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) @doc.command_line.stubs(:args).returns([]) end it "should default to rdoc mode if there are command line arguments" do @doc.command_line.stubs(:args).returns(["1"]) @doc.stubs(:setup_rdoc) @doc.setup @doc.options[:mode].should == :rdoc end it "should call setup_rdoc in rdoc mode" do @doc.options[:mode] = :rdoc @doc.expects(:setup_rdoc) @doc.setup end it "should call setup_reference if not rdoc" do @doc.options[:mode] = :test @doc.expects(:setup_reference) @doc.setup end describe "configuring logging" do before :each do Puppet::Util::Log.stubs(:newdestination) end describe "with --debug" do before do @doc.options[:debug] = true end it "should set log level to debug" do @doc.setup Puppet::Util::Log.level.should == :debug end it "should set log destination to console" do Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup end end describe "with --verbose" do before do @doc.options[:verbose] = true end it "should set log level to info" do @doc.setup Puppet::Util::Log.level.should == :info end it "should set log destination to console" do Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup end end describe "without --debug or --verbose" do before do @doc.options[:debug] = false @doc.options[:verbose] = false end it "should set log level to warning" do @doc.setup Puppet::Util::Log.level.should == :warning end it "should set log destination to console" do Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup end end end describe "in non-rdoc mode" do it "should get all non-dynamic reference if --all" do @doc.options[:all] = true static = stub 'static', :dynamic? => false dynamic = stub 'dynamic', :dynamic? => true Puppet::Util::Reference.stubs(:reference).with(:static).returns(static) Puppet::Util::Reference.stubs(:reference).with(:dynamic).returns(dynamic) Puppet::Util::Reference.stubs(:references).returns([:static,:dynamic]) @doc.setup_reference @doc.options[:references].should == [:static] end it "should default to :type if no references" do @doc.setup_reference @doc.options[:references].should == [:type] end end describe "in rdoc mode" do describe "when there are unknown args" do it "should expand --modulepath if any" do @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] Puppet.settings.stubs(:handlearg) @doc.setup_rdoc @doc.unknown_args[0][:arg].should == File.expand_path('path') end it "should expand --manifestdir if any" do @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] Puppet.settings.stubs(:handlearg) @doc.setup_rdoc @doc.unknown_args[0][:arg].should == File.expand_path('path') end it "should give them to Puppet.settings" do @doc.unknown_args = [ { :opt => :option, :arg => :argument } ] Puppet.settings.expects(:handlearg).with(:option,:argument) @doc.setup_rdoc end end it "should operate in master run_mode" do @doc.class.run_mode.name.should == :master @doc.setup_rdoc end end end describe "when running" do describe "in rdoc mode" do let(:modules) { File.expand_path("modules") } let(:manifests) { File.expand_path("manifests") } before :each do @doc.manifest = false Puppet.stubs(:info) Puppet[:trace] = false @env = stub 'env' @env.stubs(:modulepath).returns([modules]) - @env.stubs(:[]).with(:manifest).returns('manifests/site.pp') + @env.stubs(:manifest).returns('manifests/site.pp') Puppet::Node::Environment.stubs(:new).returns(@env) Puppet[:modulepath] = modules Puppet[:manifestdir] = manifests @doc.options[:all] = false @doc.options[:outputdir] = 'doc' @doc.options[:charset] = nil Puppet.settings.stubs(:define_settings) Puppet::Util::RDoc.stubs(:rdoc) @doc.command_line.stubs(:args).returns([]) end it "should set document_all on --all" do @doc.options[:all] = true Puppet.settings.expects(:[]=).with(:document_all, true) 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) 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[:charset] = 'utf-8' Puppet::Util::RDoc.expects(:rdoc).with('doc', [modules, 'manifests'], "utf-8") 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[:outputdir] = false Puppet::Util::RDoc.expects(:rdoc).with('doc', [modules, 'manifests'], nil) 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) expect { @doc.rdoc }.to exit_with 0 end it "should get modulepath and manifestdir values from the environment" do @env.expects(:modulepath).returns(['envmodules1','envmodules2']) - @env.expects(:[]).with(:manifest).returns('envmanifests/site.pp') + @env.expects(:manifest).returns('envmanifests/site.pp') Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil) expect { @doc.rdoc }.to exit_with 0 end end describe "in the other modes" do it "should get reference in given format" do reference = stub 'reference' @doc.options[:mode] = :none @doc.options[:references] = [:ref] Puppet::Util::Reference.expects(:reference).with(:ref).returns(reference) @doc.options[:format] = :format @doc.stubs(:exit) reference.expects(:send).with { |format,contents| format == :format }.returns('doc') @doc.other end end end end diff --git a/spec/unit/face/parser_spec.rb b/spec/unit/face/parser_spec.rb index 3bf24bcb0..037c53d7d 100644 --- a/spec/unit/face/parser_spec.rb +++ b/spec/unit/face/parser_spec.rb @@ -1,70 +1,73 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/face' describe Puppet::Face[:parser, :current] do include PuppetSpec::Files let(:parser) { Puppet::Face[:parser, :current] } context "from an interactive terminal" do before :each do from_an_interactive_terminal end it "validates the configured site manifest when no files are given" do - Puppet[:manifest] = file_containing('site.pp', "{ invalid =>") + manifest = file_containing('site.pp', "{ invalid =>") - expect { parser.validate() }.to exit_with(1) + configured_environment = Puppet::Node::Environment.create(:default, [], manifest) + Puppet.override(:current_environment => configured_environment) do + expect { parser.validate() }.to exit_with(1) + end end it "validates the given file" do manifest = file_containing('site.pp', "{ invalid =>") expect { parser.validate(manifest) }.to exit_with(1) end it "runs error free when there are no validation errors" do manifest = file_containing('site.pp', "notify { valid: }") parser.validate(manifest) end it "reports missing files" do expect do parser.validate("missing.pp") end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m) end it "parses supplied manifest files in the context of a directory environment" do manifest = file_containing('test.pp', "{ invalid =>") env_loader = Puppet::Environments::Static.new( Puppet::Node::Environment.create(:special, [], '') ) Puppet.override(:environments => env_loader) do Puppet[:environment] = 'special' expect { parser.validate(manifest) }.to exit_with(1) end expect(@logs.join).to match(/environment special.*Syntax error at '\{'/) end end it "validates the contents of STDIN when no files given and STDIN is not a tty" do from_a_piped_input_of("{ invalid =>") expect { parser.validate() }.to exit_with(1) end def from_an_interactive_terminal STDIN.stubs(:tty?).returns(true) end def from_a_piped_input_of(contents) STDIN.stubs(:tty?).returns(false) STDIN.stubs(:read).returns(contents) end end