[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Daniel Pittman daniel at puppetlabs.com
Tue May 10 08:11:37 UTC 2011


The following commit has been merged in the experimental branch:
commit 0f24db1c3fd0aa6601ba032aedbe25be36010954
Merge: af792351a62399f8bbeba0d2425f2b88eabb3dff 79f4774182046d2fdf392c1eb27ee78505659199
Author: Daniel Pittman <daniel at puppetlabs.com>
Date:   Thu Apr 7 17:58:07 2011 -0700

    Merge puppet-interfaces into puppet.
    
    This joins the two repositories, including full history, into a single run, as
    well as landing the interfaces work on the next branch ready for release.

diff --combined README.strings
index 0000000,28289ee..28289ee
mode 000000,100644..100644
--- a/README.strings
+++ b/README.strings
@@@ -1,0 -1,115 +1,115 @@@
+ Puppet Strings
+ =================
+ A set of executables that provide complete CLI access to Puppet's
+ core data types.  They also provide String classes for
+ each of the core data types, which are extensible via plugins.
+ 
+ For instance, you can create a new action for catalogs at
+ lib/puppet/string/catalog/$action.rb.
+ 
+ This is a Puppet module and should work fine if you install it
+ in Puppet's module path.
+ 
+ **Note that this only works with Puppet 2.6.next (and thus will work
+ with 2.6.5), because there is otherwise a bug in finding Puppet applications.
+ You also have to either install the lib files into your Puppet libdir, or
+ you need to add this lib directory to your RUBYLIB.**
+ 
+ This is meant to be tested and iterated upon, with the plan that it will be
+ merged into Puppet core once we're satisfied with it.
+ 
+ Usage
+ -----
+ The general usage is:
+ 
+     $ puppet <string> <verb> <name>
+ 
+ So, e.g.:
+ 
+     $ puppet facts find myhost.domain.com
+     $ puppet node destroy myhost
+ 
+ You can use it to list all known data types and the available terminus classes:
+ 
+     $ puppet string list
+     catalog                       : active_record, compiler, queue, rest, yaml
+     certificate                   : ca, file, rest
+     certificate_request           : ca, file, rest
+     certificate_revocation_list   : ca, file, rest
+     file_bucket_file              : file, rest
+     inventory                     : yaml
+     key                           : ca, file
+     node                          : active_record, exec, ldap, memory, plain, rest, yaml
+     report                        : processor, rest, yaml
+     resource                      : ral, rest
+     resource_type                 : parser, rest
+     status                        : local, rest
+ 
+ But most interestingly, you can use it for two main purposes:
+ 
+ * As a client for any Puppet REST server, such as catalogs, facts, reports, etc.
+ * As a local CLI for any local Puppet data
+ 
+ A simple case is looking at the local facts:
+ 
+     $ puppet facts find localhost
+ 
+ If you're on the server, you can look in that server's fact collection:
+ 
+     $ puppet facts --mode master --vardir /tmp/foo --terminus yaml find localhost
+ 
+ Note that we're setting both the vardir and the 'mode', which switches from the default 'agent' mode to server mode (requires a patch in my branch).
+ 
+ If you'd prefer the data be outputted in json instead of yaml, well, you can do that, too:
+ 
+     $ puppet find --mode master facts --vardir /tmp/foo --terminus yaml --format pson localhost
+ 
+ To test using it as an endpoint for compiling and retrieving catalogs from a remote server, (from my commit), try this:
+ 
+     # Terminal 1
+     $ sbin/puppetmasterd --trace --confdir /tmp/foo --vardir /tmp/foo --debug --manifest ~/bin/test.pp --certname localhost --no-daemonize
+     
+     # Terminal 2
+     $ sbin/puppetd --trace --debug --confdir /tmp/foo --vardir /tmp/foo --certname localhost --server localhost --test --report
+     
+     # Terminal 3, actual testing
+     $ puppet catalog find localhost --certname localhost --server localhost --mode master --confdir /tmp/foo --vardir /tmp/foo --trace --terminus rest
+ 
+ This compiles a test catalog (assuming that ~/bin/test.pp exists) and returns it.  With the right auth setup, you can also get facts:
+ 
+     $ puppet facts find localhost --certname localhost --server localhost --mode master --confdir /tmp/foo --vardir /tmp/foo --trace --terminus rest
+ 
+ Or use IRB to do the same thing:
+ 
+     $ irb
+     >> require 'puppet/string'
+     => true
+     >> string = Puppet::String[:facts, '1.0.0']
+     => #<Puppet::String::Facts:0x1024a1390 @format=:yaml>
+     >> facts = string.find("myhost")
+ 
+ Like I said, a prototype, but I'd love it if people would play it with some and make some recommendations.
+ 
+ Extending
+ ---------
+ Like most parts of Puppet, these are easy to extend.  Just drop a new action into a given string's directory.  E.g.:
+ 
+     $ cat lib/puppet/string/catalog/select.rb 
+     # Select and show a list of resources of a given type.
+     Puppet::String.define(:catalog, '1.0.0') do
+       action :select do
+         invoke do |host,type|
+           catalog = Puppet::Resource::Catalog.indirection.find(host)
+ 
+           catalog.resources.reject { |res| res.type != type }.each { |res| puts res }
+         end
+       end
+     end
+     $ puppet catalog select localhost Class
+     Class[main]
+     Class[Settings]
+     $
+ 
+ Notice that this gets loaded automatically when you try to use it.  So, if you have a simple command you've written, such as for cleaning up nodes or diffing catalogs, you an port it to this framework and it should fit cleanly.
+ 
+ Also note that strings are versioned.  These version numbers are interpreted according to Semantic Versioning (http://semver.org).
diff --combined lib/puppet/application/faces_base.rb
index 0000000,6d66ee8..288b500
mode 000000,100644..100644
--- a/lib/puppet/application/faces_base.rb
+++ b/lib/puppet/application/faces_base.rb
@@@ -1,0 -1,150 +1,150 @@@
+ require 'puppet/application'
+ require 'puppet/faces'
+ 
+ class Puppet::Application::FacesBase < Puppet::Application
+   should_parse_config
+   run_mode :agent
+ 
+   option("--debug", "-d") do |arg|
+     Puppet::Util::Log.level = :debug
+   end
+ 
+   option("--verbose", "-v") do
+     Puppet::Util::Log.level = :info
+   end
+ 
+   option("--format FORMAT") do |arg|
+     @format = arg.to_sym
+   end
+ 
+   option("--mode RUNMODE", "-r") do |arg|
+     raise "Invalid run mode #{arg}; supported modes are user, agent, master" unless %w{user agent master}.include?(arg)
+     self.class.run_mode(arg.to_sym)
+     set_run_mode self.class.run_mode
+   end
+ 
+ 
+   attr_accessor :face, :action, :type, :arguments, :format
+   attr_writer :exit_code
+ 
+   # This allows you to set the exit code if you don't want to just exit
+   # immediately but you need to indicate a failure.
+   def exit_code
+     @exit_code || 0
+   end
+ 
+   # Override this if you need custom rendering.
+   def render(result)
+     render_method = Puppet::Network::FormatHandler.format(format).render_method
+     if render_method == "to_pson"
+       jj result
+       exit(0)
+     else
+       result.send(render_method)
+     end
+   end
+ 
+   def preinit
+     super
 -    trap(:INT) do
++    Signal.trap(:INT) do
+       $stderr.puts "Cancelling Face"
+       exit(0)
+     end
+ 
+     # We need to parse enough of the command line out early, to identify what
+     # the action is, so that we can obtain the full set of options to parse.
+ 
+     # TODO: These should be configurable versions, through a global
+     # '--version' option, but we don't implement that yet... --daniel 2011-03-29
+     @type   = self.class.name.to_s.sub(/.+:/, '').downcase.to_sym
+     @face   = Puppet::Faces[@type, :current]
+     @format = @face.default_format
+ 
+     # Now, walk the command line and identify the action.  We skip over
+     # arguments based on introspecting the action and all, and find the first
+     # non-option word to use as the action.
+     action = nil
+     index  = -1
+     until @action or (index += 1) >= command_line.args.length do
+       item = command_line.args[index]
+       if item =~ /^-/ then
+         option = @face.options.find do |name|
+           item =~ /^-+#{name.to_s.gsub(/[-_]/, '[-_]')}(?:[ =].*)?$/
+         end
+         if option then
+           option = @face.get_option(option)
+           # If we have an inline argument, just carry on.  We don't need to
+           # care about optional vs mandatory in that case because we do a real
+           # parse later, and that will totally take care of raising the error
+           # when we get there. --daniel 2011-04-04
+           if option.takes_argument? and !item.index('=') then
+             index += 1 unless
+               (option.optional_argument? and command_line.args[index + 1] =~ /^-/)
+           end
+         elsif option = find_global_settings_argument(item) then
+           unless Puppet.settings.boolean? option.name then
+             # As far as I can tell, we treat non-bool options as always having
+             # a mandatory argument. --daniel 2011-04-05
+             index += 1          # ...so skip the argument.
+           end
+         else
+           raise ArgumentError, "Unknown option #{item.sub(/=.*$/, '').inspect}"
+         end
+       else
+         action = @face.get_action(item.to_sym)
+         if action.nil? then
+           raise ArgumentError, "#{@face} does not have an #{item.inspect} action!"
+         end
+         @action = action
+       end
+     end
+ 
+     @action or raise ArgumentError, "No action given on the command line!"
+ 
+     # Finally, we can interact with the default option code to build behaviour
+     # around the full set of options we now know we support.
+     @action.options.each do |option|
+       option = @action.get_option(option) # make it the object.
+       self.class.option(*option.optparse) # ...and make the CLI parse it.
+     end
+   end
+ 
+   def find_global_settings_argument(item)
+     Puppet.settings.each do |name, object|
+       object.optparse_args.each do |arg|
+         next unless arg =~ /^-/
+         # sadly, we have to emulate some of optparse here...
+         pattern = /^#{arg.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/
+         pattern.match item and return object
+       end
+     end
+     return nil                  # nothing found.
+   end
+ 
+   def setup
+     Puppet::Util::Log.newdestination :console
+ 
+     @arguments = command_line.args
+ 
+     # Note: because of our definition of where the action is set, we end up
+     # with it *always* being the first word of the remaining set of command
+     # line arguments.  So, strip that off when we construct the arguments to
+     # pass down to the face action. --daniel 2011-04-04
+     @arguments.delete_at(0)
+ 
+     # We copy all of the app options to the end of the call; This allows each
+     # action to read in the options.  This replaces the older model where we
+     # would invoke the action with options set as global state in the
+     # interface object.  --daniel 2011-03-28
+     @arguments << options
+   end
+ 
+ 
+   def main
+     # Call the method associated with the provided action (e.g., 'find').
+     if result = @face.send(@action.name, *arguments)
+       puts render(result)
+     end
+     exit(exit_code)
+   end
+ end
diff --combined lib/puppet/faces/catalog.rb
index 0000000,2e2168a..3353d5d
mode 000000,100644..100644
--- a/lib/puppet/faces/catalog.rb
+++ b/lib/puppet/faces/catalog.rb
@@@ -1,0 -1,40 +1,40 @@@
+ require 'puppet/faces/indirector'
+ 
+ Puppet::Faces::Indirector.define(:catalog, '0.0.1') do
+   action(:apply) do
+     when_invoked do |catalog, options|
+       report = Puppet::Transaction::Report.new("apply")
+       report.configuration_version = catalog.version
+ 
+       Puppet::Util::Log.newdestination(report)
+ 
+       begin
+         benchmark(:notice, "Finished catalog run") do
+           catalog.apply(:report => report)
+         end
+       rescue => detail
+         puts detail.backtrace if Puppet[:trace]
+         Puppet.err "Failed to apply catalog: #{detail}"
+       end
+ 
+       report.finalize_report
+       report
+     end
+   end
+ 
+   action(:download) do
+     when_invoked do |certname, facts, options|
 -      Puppet::Resource::Catalog.terminus_class = :rest
++      Puppet::Resource::Catalog.indirection.terminus_class = :rest
+       facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))}
+       catalog = nil
+       retrieval_duration = thinmark do
+         catalog = Puppet::Faces[:catalog, '0.0.1'].find(certname, facts_to_upload)
+       end
+       catalog = catalog.to_ral
+       catalog.finalize
+       catalog.retrieval_duration = retrieval_duration
+       catalog.write_class_file
+       catalog
+     end
+   end
+ end
diff --combined spec/lib/puppet/faces/huzzah.rb
index 0000000,0000000..7350044
new file mode 100644
--- /dev/null
+++ b/spec/lib/puppet/faces/huzzah.rb
@@@ -1,0 -1,0 +1,4 @@@
++require 'puppet/faces'
++Puppet::Faces.define(:huzzah, '2.0.1') do
++  action :bar do "is where beer comes from" end
++end
diff --combined spec/spec_helper.rb
index 1347042,615c9d3..fc63c6d
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@@ -1,46 -1,21 +1,48 @@@
 -require 'pathname'
 -dir = Pathname.new(__FILE__).parent
 -$LOAD_PATH.unshift(dir, dir + 'lib', dir + '../lib')
 +unless defined?(SPEC_HELPER_IS_LOADED)
 +SPEC_HELPER_IS_LOADED = 1
 +
 +dir = File.expand_path(File.dirname(__FILE__))
 +
 +$LOAD_PATH.unshift("#{dir}/")
 +$LOAD_PATH.unshift("#{dir}/lib") # a spec-specific test lib dir
 +$LOAD_PATH.unshift("#{dir}/../lib")
 +
 +# Don't want puppet getting the command line arguments for rake or autotest
 +ARGV.clear
  
 -require 'mocha'
  require 'puppet'
 -require 'puppet/faces'
 -require 'rspec'
 +require 'mocha'
 +gem 'rspec', '>=2.0.0'
 +
 +# So everyone else doesn't have to include this base constant.
 +module PuppetSpec
 +  FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
 +end
 +
 +require 'pathname'
+ require 'tmpdir'
+ 
 +require 'lib/puppet_spec/verbose'
 +require 'lib/puppet_spec/files'
 +require 'lib/puppet_spec/fixtures'
 +require 'monkey_patches/alias_should_to_must'
 +require 'monkey_patches/publicize_methods'
 +
  Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour|
 -  require behaviour.relative_path_from(dir)
 +  require behaviour.relative_path_from(Pathname.new(dir))
  end
  
  RSpec.configure do |config|
 +  include PuppetSpec::Fixtures
 +
    config.mock_with :mocha
  
    config.before :each do
 +    # these globals are set by Application
 +    $puppet_application_mode = nil
 +    $puppet_application_name = nil
 +    Signal.stubs(:trap)
 +
      # Set the confdir and vardir to gibberish so that tests
      # have to be correctly mocked.
      Puppet[:confdir] = "/dev/null"
@@@ -50,20 -25,35 +52,21 @@@
      Puppet.settings[:bindaddress] = "127.0.0.1"
  
      @logs = []
 -    Puppet::Util::Log.newdestination(@logs)
 -
 -    @load_path_scratch_dir = Dir.mktmpdir
 -    $LOAD_PATH.push @load_path_scratch_dir
 -    FileUtils.mkdir_p(File.join @load_path_scratch_dir, 'puppet', 'faces')
 +    Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))
    end
  
    config.after :each do
      Puppet.settings.clear
 +    Puppet::Node::Environment.clear
 +    Puppet::Util::Storage.clear
 +    Puppet::Util::ExecutionStub.reset
 +
 +    PuppetSpec::Files.cleanup
  
      @logs.clear
      Puppet::Util::Log.close_all
    end
  end
  
 -# We need this because the RAL uses 'should' as a method.  This
 -# allows us the same behaviour but with a different method name.
 -class Object
 -  alias :must :should
++# close of the "don't evaluate twice" mess.
  end
diff --combined spec/unit/faces/catalog_spec.rb
index 0000000,7197219..e0a771d
mode 000000,100755..100755
--- a/spec/unit/faces/catalog_spec.rb
+++ b/spec/unit/faces/catalog_spec.rb
@@@ -1,0 -1,3 +1,4 @@@
++require 'puppet/faces'
+ describe Puppet::Faces[:catalog, '0.0.1'] do
+   it "should actually have some testing..."
+ end
diff --combined spec/unit/interface/face_collection_spec.rb
index 0000000,de6d29c..bf3801e
mode 000000,100755..100755
--- a/spec/unit/interface/face_collection_spec.rb
+++ b/spec/unit/interface/face_collection_spec.rb
@@@ -1,0 -1,184 +1,175 @@@
+ #!/usr/bin/env ruby
 -
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
++
+ require 'tmpdir'
++require 'puppet/interface/face_collection'
+ 
+ describe Puppet::Interface::FaceCollection do
+   # To avoid cross-pollution we have to save and restore both the hash
+   # containing all the interface data, and the array used by require.  Restoring
+   # both means that we don't leak side-effects across the code. --daniel 2011-04-06
+   before :each do
+     @original_faces    = subject.instance_variable_get("@faces").dup
+     @original_required = $".dup
+     subject.instance_variable_get("@faces").clear
+   end
+ 
+   after :each do
+     subject.instance_variable_set("@faces", @original_faces)
+     $".clear ; @original_required.each do |item| $" << item end
+   end
+ 
+   describe "::faces" do
+     it "REVISIT: should have some tests here, if we describe it"
+   end
+ 
+   describe "::validate_version" do
+     it 'should permit three number versions' do
+       subject.validate_version('10.10.10').should == true
+     end
+ 
+     it 'should permit versions with appended descriptions' do
+       subject.validate_version('10.10.10beta').should == true
+     end
+ 
+     it 'should not permit versions with more than three numbers' do
+       subject.validate_version('1.2.3.4').should == false
+     end
+ 
+     it 'should not permit versions with only two numbers' do
+       subject.validate_version('10.10').should == false
+     end
+ 
+     it 'should not permit versions with only one number' do
+       subject.validate_version('123').should == false
+     end
+ 
+     it 'should not permit versions with text in any position but at the end' do
+       subject.validate_version('v1.1.1').should == false
+     end
+   end
+ 
+   describe "::[]" do
+     before :each do
+       subject.instance_variable_get("@faces")[:foo]['0.0.1'] = 10
+     end
+ 
+     before :each do
+       @dir = Dir.mktmpdir
+       @lib = FileUtils.mkdir_p(File.join @dir, 'puppet', 'faces')
+       $LOAD_PATH.push(@dir)
+     end
+ 
+     after :each do
+       FileUtils.remove_entry_secure @dir
+       $LOAD_PATH.pop
+     end
+ 
+     it "should return the faces with the given name" do
+       subject["foo", '0.0.1'].should == 10
+     end
+ 
+     it "should attempt to load the faces if it isn't found" do
+       subject.expects(:require).with('puppet/faces/bar')
+       subject["bar", '0.0.1']
+     end
+ 
+     it "should attempt to load the default faces for the specified version :current" do
+       subject.expects(:require).never # except...
+       subject.expects(:require).with('puppet/faces/fozzie')
+       subject['fozzie', :current]
+     end
+   end
+ 
+   describe "::face?" do
+     before :each do
+       subject.instance_variable_get("@faces")[:foo]['0.0.1'] = 10
+     end
+ 
+     it "should return true if the faces specified is registered" do
+       subject.face?("foo", '0.0.1').should == true
+     end
+ 
+     it "should attempt to require the faces if it is not registered" do
+       subject.expects(:require).with do |file|
+         subject.instance_variable_get("@faces")[:bar]['0.0.1'] = true
+         file == 'puppet/faces/bar'
+       end
+       subject.face?("bar", '0.0.1').should == true
+     end
+ 
+     it "should return true if requiring the faces registered it" do
+       subject.stubs(:require).with do
+         subject.instance_variable_get("@faces")[:bar]['0.0.1'] = 20
+       end
+     end
+ 
+     it "should return false if the faces is not registered" do
+       subject.stubs(:require).returns(true)
+       subject.face?("bar", '0.0.1').should be_false
+     end
+ 
+     it "should return false if the faces file itself is missing" do
+       subject.stubs(:require).
+         raises(LoadError, 'no such file to load -- puppet/faces/bar')
+       subject.face?("bar", '0.0.1').should be_false
+     end
+ 
+     it "should register the version loaded by `:current` as `:current`" do
+       subject.expects(:require).with do |file|
+         subject.instance_variable_get("@faces")[:huzzah]['2.0.1'] = :huzzah_faces
+         file == 'puppet/faces/huzzah'
+       end
+       subject.face?("huzzah", :current)
+       subject.instance_variable_get("@faces")[:huzzah][:current].should == :huzzah_faces
+     end
+ 
+     context "with something on disk" do
 -      before :each do
 -        write_scratch_faces :huzzah do |fh|
 -          fh.puts <<EOF
 -Puppet::Faces.define(:huzzah, '2.0.1') do
 -  action :bar do "is where beer comes from" end
 -end
 -EOF
 -        end
 -      end
 -
+       it "should register the version loaded from `puppet/faces/{name}` as `:current`" do
+         subject.should be_face "huzzah", '2.0.1'
+         subject.should be_face "huzzah", :current
+         Puppet::Faces[:huzzah, '2.0.1'].should == Puppet::Faces[:huzzah, :current]
+       end
+ 
+       it "should index :current when the code was pre-required" do
+         subject.instance_variable_get("@faces")[:huzzah].should_not be_key :current
+         require 'puppet/faces/huzzah'
+         subject.face?(:huzzah, :current).should be_true
+       end
+     end
+   end
+ 
+   describe "::register" do
+     it "should store the faces by name" do
+       faces = Puppet::Faces.new(:my_faces, '0.0.1')
+       subject.register(faces)
+       subject.instance_variable_get("@faces").should == {:my_faces => {'0.0.1' => faces}}
+     end
+   end
+ 
+   describe "::underscorize" do
+     faulty = [1, "#foo", "$bar", "sturm und drang", :"sturm und drang"]
+     valid  = {
+       "Foo"      => :foo,
+       :Foo       => :foo,
+       "foo_bar"  => :foo_bar,
+       :foo_bar   => :foo_bar,
+       "foo-bar"  => :foo_bar,
+       :"foo-bar" => :foo_bar,
+     }
+ 
+     valid.each do |input, expect|
+       it "should map #{input.inspect} to #{expect.inspect}" do
+         result = subject.underscorize(input)
+         result.should == expect
+       end
+     end
+ 
+     faulty.each do |input|
+       it "should fail when presented with #{input.inspect} (#{input.class})" do
+         expect { subject.underscorize(input) }.
+           should raise_error ArgumentError, /not a valid face name/
+       end
+     end
+   end
+ end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list