This adds two REST API:
* /instrumentation_listener(s)/<instrumentation_name>

GET /instrumentation_listener/<instrumentation_name>
Gives details about a given instrumentation listener

GET /instrumentation_listeners/unused
Lists all instrumentation listeners loaded in the system

PUT /instrumentation_listener/name
Allows to enable or disable a given instrumentation listener (supports
only pson). The body should be 
{"name":"instrumentation_name","pattern":<pattern>,"enabled":true}

* /instrumentation_data/<instrumentation_name>
Only GET is supported. This returns the accumulated data of the given
instrumentation name if the listener supports data aggregation.
This is the only way so far to get access to the instrumentation data.
The data format is specific to the given listener.

Note: actual default auth.conf doesn't allow to interact with these
two indirections.

Signed-off-by: Brice Figureau <[email protected]>
---
 lib/puppet/application.rb                          |    3 +
 lib/puppet/indirector/instrumentation_data.rb      |    3 +
 .../indirector/instrumentation_data/local.rb       |   19 +++++++
 lib/puppet/indirector/instrumentation_data/rest.rb |    5 ++
 lib/puppet/indirector/instrumentation_listener.rb  |    3 +
 .../indirector/instrumentation_listener/local.rb   |   20 +++++++
 .../indirector/instrumentation_listener/rest.rb    |    5 ++
 lib/puppet/util/instrumentation.rb                 |    5 ++-
 lib/puppet/util/instrumentation/data.rb            |   28 ++++++++++
 lib/puppet/util/instrumentation/listener.rb        |   20 +++++++-
 spec/unit/application_spec.rb                      |    6 ++
 .../indirector/instrumentation_data/local_spec.rb  |   52 ++++++++++++++++++
 .../indirector/instrumentation_data/rest_spec.rb   |   12 ++++
 .../instrumentation_listener/local_spec.rb         |   58 ++++++++++++++++++++
 .../instrumentation_listener/rest_spec.rb          |   12 ++++
 spec/unit/util/instrumentation/data_spec.rb        |   37 +++++++++++++
 spec/unit/util/instrumentation/listener_spec.rb    |   30 ++++++++++-
 17 files changed, 315 insertions(+), 3 deletions(-)
 create mode 100644 lib/puppet/indirector/instrumentation_data.rb
 create mode 100644 lib/puppet/indirector/instrumentation_data/local.rb
 create mode 100644 lib/puppet/indirector/instrumentation_data/rest.rb
 create mode 100644 lib/puppet/indirector/instrumentation_listener.rb
 create mode 100644 lib/puppet/indirector/instrumentation_listener/local.rb
 create mode 100644 lib/puppet/indirector/instrumentation_listener/rest.rb
 create mode 100644 lib/puppet/util/instrumentation/data.rb
 create mode 100644 spec/unit/indirector/instrumentation_data/local_spec.rb
 create mode 100644 spec/unit/indirector/instrumentation_data/rest_spec.rb
 create mode 100644 spec/unit/indirector/instrumentation_listener/local_spec.rb
 create mode 100644 spec/unit/indirector/instrumentation_listener/rest_spec.rb
 create mode 100644 spec/unit/util/instrumentation/data_spec.rb

diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb
index c3d7355..7e69167 100644
--- a/lib/puppet/application.rb
+++ b/lib/puppet/application.rb
@@ -262,12 +262,15 @@ class Application
   end
 
   def initialize(command_line = nil)
+
     require 'puppet/util/command_line'
     @command_line = command_line || Puppet::Util::CommandLine.new
     set_run_mode self.class.run_mode
     @options = {}
 
     require 'puppet'
+    require 'puppet/util/instrumentation'
+    Puppet::Util::Instrumentation.init
   end
 
   # WARNING: This is a totally scary, frightening, and nasty internal API.  We
diff --git a/lib/puppet/indirector/instrumentation_data.rb 
b/lib/puppet/indirector/instrumentation_data.rb
new file mode 100644
index 0000000..f1bea33
--- /dev/null
+++ b/lib/puppet/indirector/instrumentation_data.rb
@@ -0,0 +1,3 @@
+# A stub class, so our constants work.
+class Puppet::Indirector::InstrumentationData
+end
diff --git a/lib/puppet/indirector/instrumentation_data/local.rb 
b/lib/puppet/indirector/instrumentation_data/local.rb
new file mode 100644
index 0000000..6510d7f
--- /dev/null
+++ b/lib/puppet/indirector/instrumentation_data/local.rb
@@ -0,0 +1,19 @@
+require 'puppet/indirector/instrumentation_data'
+
+class Puppet::Indirector::InstrumentationData::Local < Puppet::Indirector::Code
+  def find(request)
+    model.new(request.key)
+  end
+
+  def search(request)
+    raise Puppet::DevError, "You cannot search for instrumentation data"
+  end
+
+  def save(request)
+    raise Puppet::DevError, "You cannot save instrumentation data"
+  end
+
+  def destroy(request)
+    raise Puppet::DevError, "You cannot remove instrumentation data"
+  end
+end
diff --git a/lib/puppet/indirector/instrumentation_data/rest.rb 
b/lib/puppet/indirector/instrumentation_data/rest.rb
new file mode 100644
index 0000000..28b2117
--- /dev/null
+++ b/lib/puppet/indirector/instrumentation_data/rest.rb
@@ -0,0 +1,5 @@
+require 'puppet/indirector/rest'
+require 'puppet/indirector/instrumentation_data'
+
+class Puppet::Indirector::InstrumentationData::Rest < Puppet::Indirector::REST
+end
diff --git a/lib/puppet/indirector/instrumentation_listener.rb 
b/lib/puppet/indirector/instrumentation_listener.rb
new file mode 100644
index 0000000..6aaa71c
--- /dev/null
+++ b/lib/puppet/indirector/instrumentation_listener.rb
@@ -0,0 +1,3 @@
+# A stub class, so our constants work.
+class Puppet::Indirector::InstrumentationListener
+end
diff --git a/lib/puppet/indirector/instrumentation_listener/local.rb 
b/lib/puppet/indirector/instrumentation_listener/local.rb
new file mode 100644
index 0000000..5b643ad
--- /dev/null
+++ b/lib/puppet/indirector/instrumentation_listener/local.rb
@@ -0,0 +1,20 @@
+require 'puppet/indirector/instrumentation_listener'
+
+class Puppet::Indirector::InstrumentationListener::Local < 
Puppet::Indirector::Code
+  def find(request)
+    Puppet::Util::Instrumentation[request.key]
+  end
+
+  def search(request)
+    Puppet::Util::Instrumentation.listeners
+  end
+
+  def save(request)
+    res = request.instance
+    Puppet::Util::Instrumentation[res.name] = res
+  end
+
+  def destroy(request)
+    raise Puppet::DevError, "You cannot remove an Instrumentation Listener"
+  end
+end
diff --git a/lib/puppet/indirector/instrumentation_listener/rest.rb 
b/lib/puppet/indirector/instrumentation_listener/rest.rb
new file mode 100644
index 0000000..0bc8122
--- /dev/null
+++ b/lib/puppet/indirector/instrumentation_listener/rest.rb
@@ -0,0 +1,5 @@
+require 'puppet/indirector/instrumentation_listener'
+require 'puppet/indirector/rest'
+
+class Puppet::Indirector::InstrumentationListener::Rest < 
Puppet::Indirector::REST
+end
diff --git a/lib/puppet/util/instrumentation.rb 
b/lib/puppet/util/instrumentation.rb
index ad5c258..90cd856 100644
--- a/lib/puppet/util/instrumentation.rb
+++ b/lib/puppet/util/instrumentation.rb
@@ -7,7 +7,10 @@ class Puppet::Util::Instrumentation
   extend Puppet::Util::InstanceLoader
   extend MonitorMixin
 
-  require 'puppet/util/instrumentation/listener'
+  # we're using a ruby lazy autoloader to prevent a loop when requiring 
listeners
+  # since this class sets up an indirection, but this one is used in 
Indirection
+  autoload :Listener, 'puppet/util/instrumentation/listener'
+  autoload :Data, 'puppet/util/instrumentation/data'
 
   # Set up autoloading and retrieving of instrumentation listeners.
   instance_load :listener, 'puppet/util/instrumentation/listeners'
diff --git a/lib/puppet/util/instrumentation/data.rb 
b/lib/puppet/util/instrumentation/data.rb
new file mode 100644
index 0000000..f539c08
--- /dev/null
+++ b/lib/puppet/util/instrumentation/data.rb
@@ -0,0 +1,28 @@
+require 'puppet/indirector'
+
+# This is just a transport class to be used through the instrumentation_data
+# indirection. All the data resides in the real underlying listeners which this
+# class delegates to.
+class Puppet::Util::Instrumentation::Data
+  extend Puppet::Indirector
+
+  indirects :instrumentation_data, :terminus_class => :local
+
+  attr_reader :listener
+
+  def initialize(listener_name)
+    @listener = Puppet::Util::Instrumentation[listener_name]
+  end
+
+  def name
+    @listener.name
+  end
+
+  def to_pson
+    { :name => name }.merge(@listener.data).to_pson
+  end
+
+  def self.from_pson(data)
+    raise "Instrumentation Listeners data are read only"
+  end
+end
diff --git a/lib/puppet/util/instrumentation/listener.rb 
b/lib/puppet/util/instrumentation/listener.rb
index bcea6a8..805045b 100644
--- a/lib/puppet/util/instrumentation/listener.rb
+++ b/lib/puppet/util/instrumentation/listener.rb
@@ -1,6 +1,11 @@
-require 'puppet/util/instrumentation'
+require 'puppet/indirector'
+require 'puppet/util/instrumentation/data'
 
 class Puppet::Util::Instrumentation::Listener
+  extend Puppet::Indirector
+
+  indirects :instrumentation_listener, :terminus_class => :local
+
   attr_reader :pattern, :listener
   attr_accessor :enabled
 
@@ -32,4 +37,17 @@ class Puppet::Util::Instrumentation::Listener
   def name
     @listener.name
   end
+
+  def data
+    { :data => @listener.data }
+  end
+
+  def to_pson
+    { :name => name, :pattern => pattern, :enabled => enabled? }.to_pson
+  end
+
+  def self.from_pson(data)
+    result = Puppet::Util::Instrumentation[data["name"]]
+    self.new(result.listener, result.pattern, data["enabled"])
+  end
 end
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index 5a52c2d..132aacd 100755
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -9,6 +9,7 @@ require 'getoptlong'
 describe Puppet::Application do
 
   before do
+    Puppet::Util::Instrumentation.stubs(:init)
     @app = Class.new(Puppet::Application).new
     @appclass = @app.class
 
@@ -113,6 +114,11 @@ describe Puppet::Application do
     @app.run_command
   end
 
+  it "should initialize the Puppet Instrumentation layer on creation" do
+    Puppet::Util::Instrumentation.expects(:init)
+    Class.new(Puppet::Application).new
+  end
+
   describe 'when invoking clear!' do
     before :each do
       Puppet::Application.run_status = :stop_requested
diff --git a/spec/unit/indirector/instrumentation_data/local_spec.rb 
b/spec/unit/indirector/instrumentation_data/local_spec.rb
new file mode 100644
index 0000000..31b4ee6
--- /dev/null
+++ b/spec/unit/indirector/instrumentation_data/local_spec.rb
@@ -0,0 +1,52 @@
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/util/instrumentation/listener'
+require 'puppet/indirector/instrumentation_data/local'
+
+describe Puppet::Indirector::InstrumentationData::Local do
+  it "should be a subclass of the Code terminus" do
+    Puppet::Indirector::InstrumentationData::Local.superclass.should 
equal(Puppet::Indirector::Code)
+  end
+
+  it "should be registered with the configuration store indirection" do
+    indirection = 
Puppet::Indirector::Indirection.instance(:instrumentation_data)
+    Puppet::Indirector::InstrumentationData::Local.indirection.should 
equal(indirection)
+  end
+
+  it "should have its name set to :local" do
+    Puppet::Indirector::InstrumentationData::Local.name.should == :local
+  end
+end
+
+describe Puppet::Indirector::InstrumentationData::Local do
+  before :each do
+    Puppet::Util::Instrumentation.stubs(:listener)
+    @data = Puppet::Indirector::InstrumentationData::Local.new
+    @name = "me"
+    @request = stub 'request', :key => @name
+  end
+
+  describe "when finding instrumentation data" do
+    it "should return a Instrumentation Data instance matching the key" do
+    end
+  end
+
+  describe "when searching listeners" do
+    it "should raise an error" do
+      lambda { @data.search(@request) }.should raise_error(Puppet::DevError)
+    end
+  end
+
+  describe "when saving listeners" do
+    it "should raise an error" do
+      lambda { @data.save(@request) }.should raise_error(Puppet::DevError)
+    end
+  end
+
+  describe "when destroying listeners" do
+    it "should raise an error" do
+      lambda { @data.destroy(@reques) }.should raise_error(Puppet::DevError)
+    end
+  end
+end
diff --git a/spec/unit/indirector/instrumentation_data/rest_spec.rb 
b/spec/unit/indirector/instrumentation_data/rest_spec.rb
new file mode 100644
index 0000000..93101c2
--- /dev/null
+++ b/spec/unit/indirector/instrumentation_data/rest_spec.rb
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? 
require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+require 'puppet/util/instrumentation/data'
+require 'puppet/indirector/instrumentation_data/rest'
+
+describe Puppet::Indirector::InstrumentationData::Rest do
+  it "should be a subclass of Puppet::Indirector::REST" do
+    Puppet::Indirector::InstrumentationData::Rest.superclass.should 
equal(Puppet::Indirector::REST)
+  end
+end
diff --git a/spec/unit/indirector/instrumentation_listener/local_spec.rb 
b/spec/unit/indirector/instrumentation_listener/local_spec.rb
new file mode 100644
index 0000000..88ab580
--- /dev/null
+++ b/spec/unit/indirector/instrumentation_listener/local_spec.rb
@@ -0,0 +1,58 @@
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/util/instrumentation/listener'
+require 'puppet/indirector/instrumentation_listener/local'
+
+describe Puppet::Indirector::InstrumentationListener::Local do
+  it "should be a subclass of the Code terminus" do
+    Puppet::Indirector::InstrumentationListener::Local.superclass.should 
equal(Puppet::Indirector::Code)
+  end
+
+  it "should be registered with the configuration store indirection" do
+    indirection = 
Puppet::Indirector::Indirection.instance(:instrumentation_listener)
+    Puppet::Indirector::InstrumentationListener::Local.indirection.should 
equal(indirection)
+  end
+
+  it "should have its name set to :local" do
+    Puppet::Indirector::InstrumentationListener::Local.name.should == :local
+  end
+end
+
+describe Puppet::Indirector::InstrumentationListener::Local do
+  before :each do
+    Puppet::Util::Instrumentation.stubs(:listener)
+    @listener = Puppet::Indirector::InstrumentationListener::Local.new
+    @name = "me"
+    @request = stub 'request', :key => @name
+  end
+
+  describe "when finding listeners" do
+    it "should return a Instrumentation Listener instance matching the key" do
+      Puppet::Util::Instrumentation.expects(:[]).with("me").returns(:instance)
+      @listener.find(@request).should == :instance
+    end
+  end
+
+  describe "when searching listeners" do
+    it "should return a list of all loaded Instrumentation Listenesrs 
irregardless of the given key" do
+      Puppet::Util::Instrumentation.expects(:listeners).returns([:instance1, 
:instance2])
+      @listener.search(@request).should == [:instance1, :instance2]
+    end
+  end
+
+  describe "when saving listeners" do
+    it "should set the new listener to the global listener list" do
+      newlistener = stub 'listener', :name => @name
+      @request.stubs(:instance).returns(newlistener)
+      Puppet::Util::Instrumentation.expects(:[]=).with("me", newlistener)
+      @listener.save(@request)
+    end
+  end
+
+  describe "when destroying listeners" do
+    it "should raise an error" do
+      lambda { @listener.destroy(stub 'instance') }.should 
raise_error(Puppet::DevError)
+    end
+  end
+end
diff --git a/spec/unit/indirector/instrumentation_listener/rest_spec.rb 
b/spec/unit/indirector/instrumentation_listener/rest_spec.rb
new file mode 100644
index 0000000..7508ad1
--- /dev/null
+++ b/spec/unit/indirector/instrumentation_listener/rest_spec.rb
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? 
require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+require 'puppet/util/instrumentation/listener'
+require 'puppet/indirector/instrumentation_listener/rest'
+
+describe Puppet::Indirector::InstrumentationListener::Rest do
+  it "should be a subclass of Puppet::Indirector::REST" do
+    Puppet::Indirector::InstrumentationListener::Rest.superclass.should 
equal(Puppet::Indirector::REST)
+  end
+end
diff --git a/spec/unit/util/instrumentation/data_spec.rb 
b/spec/unit/util/instrumentation/data_spec.rb
new file mode 100644
index 0000000..7deb5be
--- /dev/null
+++ b/spec/unit/util/instrumentation/data_spec.rb
@@ -0,0 +1,37 @@
+Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? 
require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
+
+require 'puppet/util/instrumentation/data'
+
+describe Puppet::Util::Instrumentation::Data do
+  Puppet::Util::Instrumentation::Data
+
+  before(:each) do
+    @listener = stub 'listener', :name => "name"
+    Puppet::Util::Instrumentation.stubs(:[]).with("name").returns(@listener)
+  end
+
+  it "should indirect instrumentation_data" do
+    Puppet::Util::Instrumentation::Data.indirection.name.should == 
:instrumentation_data
+  end
+
+  it "should lookup the corresponding listener" do
+    Puppet::Util::Instrumentation.expects(:[]).with("name").returns(@listener)
+    Puppet::Util::Instrumentation::Data.new("name")
+  end
+
+  it "should transform listener data to pson when serialized as pson" do
+    data = Puppet::Util::Instrumentation::Data.new("name")
+    @listener.expects(:data).returns({ :this_is_data  => "here also" })
+    data.to_pson
+  end
+
+  it "should return pson data" do
+    data = Puppet::Util::Instrumentation::Data.new("name")
+    @listener.stubs(:data).returns({ :this_is_data  => "here also" })
+    data.to_pson.should == "{\"name\":\"name\",\"this_is_data\":\"here also\"}"
+  end
+
+  it "should raise an error when deserializing from pson" do
+    lambda { Puppet::Util::Instrumentation::Data.from_pson({}) }.should 
raise_error
+  end
+end
\ No newline at end of file
diff --git a/spec/unit/util/instrumentation/listener_spec.rb 
b/spec/unit/util/instrumentation/listener_spec.rb
index b855be8..d99422d 100644
--- a/spec/unit/util/instrumentation/listener_spec.rb
+++ b/spec/unit/util/instrumentation/listener_spec.rb
@@ -7,11 +7,15 @@ describe Puppet::Util::Instrumentation::Listener do
   Listener = Puppet::Util::Instrumentation::Listener
 
   before(:each) do
-    @delegate = stub 'listener', :notify => nil
+    @delegate = stub 'listener', :notify => nil, :name => 'listener'
     @listener = Listener.new(@delegate)
     @listener.enabled = true
   end
 
+  it "should indirect instrumentation_listener" do
+    Listener.indirection.name.should == :instrumentation_listener
+  end
+
   it "should raise an error if delegate doesn't support notify" do
     lambda { Listener.new(Object.new) }.should raise_error
   end
@@ -52,4 +56,28 @@ describe Puppet::Util::Instrumentation::Listener do
     @listener.name.should == "myname"
   end
 
+  it "should delegate data fetching to the underlying listener" do
+    @delegate.expects(:data).returns(:data)
+    @listener.data.should == {:data => :data }
+  end
+
+  describe "when serializing to pson" do
+    it "should return a pson object containing pattern, name and status" do
+      @listener.to_pson.should == 
"{\"enabled\":true,\"pattern\":null,\"name\":\"listener\"}"
+    end
+  end
+
+  describe "when deserializing from pson" do
+    it "should lookup the archetype listener from the instrumentation layer" do
+      
Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener)
+      Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener"})
+    end
+
+    it "should create a new listener shell instance delegating to the 
archetypal listener" do
+      
Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener)
+      @listener.stubs(:listener).returns(@delegate)
+      Puppet::Util::Instrumentation::Listener.expects(:new).with(@delegate, 
nil, true)
+      Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener", 
"enabled" => true})
+    end
+  end
 end
\ No newline at end of file
-- 
1.7.2.1

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to