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.