This is the second attempt of a daemontools (only for the moment)
provider for the service type. I added a rspec unit type, and tried
to take into account all of Luke's previous comments.

The development of this provider is hosted at:
http://github.com/masterzen/puppet/tree/feature%2Fdaemontools
which is based on 0.24.x

Comments are (more than) welcome as usual.
Next revision will bring a runit version.

Here is the patch description:
This provider manages daemons running supervised under D.J.Bernstein
daemontools.
It tries to detect the service directory, with by order of preference:
 * /service
 * /etc/service
 * /var/lib/svscan

The daemon directory should be placed in a directory that can be
by default in:
 * /var/lib/service
 * /etc
or this can be overriden in the service resource parameters:
service {
 "myservice":
   provider => "daemontools", path => "/path/to/daemons";
}

This provider supports out of the box:
 * start/stop (mapped to enable/disable)
 * enable/disable
 * restart
 * status

Signed-off-by: Brice Figureau <[EMAIL PROTECTED]>
---
 lib/puppet/provider/service/daemontools.rb |  152 ++++++++++++++++++++++++++++
 spec/unit/provider/service/daemontools.rb  |  124 ++++++++++++++++++++++
 2 files changed, 276 insertions(+), 0 deletions(-)
 create mode 100644 lib/puppet/provider/service/daemontools.rb
 create mode 100644 spec/unit/provider/service/daemontools.rb

diff --git a/lib/puppet/provider/service/daemontools.rb 
b/lib/puppet/provider/service/daemontools.rb
new file mode 100644
index 0000000..dda27d0
--- /dev/null
+++ b/lib/puppet/provider/service/daemontools.rb
@@ -0,0 +1,152 @@
+# Daemontools service management
+#
+# author Brice Figureau <[EMAIL PROTECTED]>
+Puppet::Type.type(:service).provide :daemontools, :parent => :base do
+    desc "Daemontools service management.
+    This provider manages daemons running supervised by D.J.Bernstein 
daemontools.
+    It tries to detect the service directory, with by order of preference:
+     * /service
+     * /etc/service
+     * /var/lib/svscan
+    The daemon directory should be placed in a directory that can be 
+    by default in:
+     * /var/lib/service
+     * /etc
+    or this can be overriden in the service resource parameters:
+    service {
+     \"myservice\":
+       provider => \"daemontools\", path => \"/path/to/daemons\";
+    }
+
+    This provider supports out of the box:
+     * start/stop (mapped to enable/disable)
+     * enable/disable
+     * restart
+     * status"
+
+    commands :svc  => "/usr/bin/svc"
+    commands :svstat => "/usr/bin/svstat"
+
+    class << self
+        attr_writer :defpath
+        
+        # this is necessary to autodetect a valid resource
+        # default path, since there is no standard for such directory.
+        def defpath
+            unless defined?(@defpath) and @defpath
+                ["/var/lib/service", "/etc"].each do |path|
+                    if FileTest.exist?(path)
+                        @defpath = path
+                        break
+                    end
+                end
+                raise "Could not find the daemon directory (tested 
[/var/lib/service,/etc])" unless @defpath
+            end
+            @defpath
+        end
+    end
+
+    attr_writer :servicedir
+
+    # returns all providers for all existing services in @defpath
+    # ie enabled or not
+    def self.instances
+        path = self.daemondir
+        unless FileTest.directory?(path)
+            Puppet.notice "Service path %s does not exist" % path
+            next
+        end
+
+        Dir.entries(path).reject { |e|
+            fullpath = File.join(path, e)
+            e =~ /^\./ or ! FileTest.directory?(fullpath)
+        }.collect do |name|
+            new(:name => name, :path => path)
+        end
+    end
+
+    # find the service dir on this node
+    def servicedir
+      unless defined?(@servicedir) and @servicedir
+        ["/service", "/etc/service","/var/lib/svscan"].each do |path|
+            if FileTest.exist?(path)
+                @servicedir = path
+                break
+            end
+        end
+        raise "Could not find service directory" unless @servicedir
+      end
+      @servicedir
+    end
+
+    # returns the daemon dir on this node
+    def self.daemondir
+        self.class.defpath
+    end
+    
+    # returns the full path of this service when enabled
+    # (ie in the service directory)
+    def service
+        File.join(self.servicedir, resource[:name])
+    end
+
+    # returns the full path to the current daemon directory
+    # note that this path can be overriden in the resource
+    # definition
+    def daemon
+        File.join(resource[:path], resource[:name])
+    end
+    
+    def restartcmd
+        [ command(:svc), "-t", self.service]
+    end
+
+    # The start command does nothing, service are automatically started
+    # when enabled by svscan. But, forces an enable if necessary
+    def start
+        # to start make sure the sevice is enabled
+        self.enable
+        # start is then automatic
+    end
+
+    def status
+        begin
+            output = svstat self.service
+            return :running if output =~ /\bup\b/
+        rescue Puppet::ExecutionFailure => detail
+            raise Puppet::Error.new( "Could not get status for service %s: %s" 
% [ resource.ref, detail] )
+        end
+        return :stopped
+    end
+
+    # unfortunately it is not possible
+    # to stop without disabling the service
+    def stop
+        self.disable
+    end
+
+    # disable by stopping the service
+    # and removing the symlink so that svscan
+    # doesn't restart our service behind our back
+    def disable
+        # should stop the service
+        # stop the log subservice if any
+        log = File.join(self.service, "log")
+        texecute("stop log", [ command(:svc) , '-dx', log] ) if 
FileTest.directory?(log)
+        
+        # stop the main resource
+        texecute("stop", [command(:svc), '-dx', self.service] )
+
+        # unlink the daemon symlink to disable it
+        File.unlink(self.service) if FileTest.symlink?(self.service)
+    end
+
+    def enabled?
+        FileTest.symlink?(self.service)
+    end
+
+    def enable
+        File.symlink(self.daemon, self.service) if ! 
FileTest.symlink?(self.service)
+    end
+end
+
diff --git a/spec/unit/provider/service/daemontools.rb 
b/spec/unit/provider/service/daemontools.rb
new file mode 100644
index 0000000..29e9dd5
--- /dev/null
+++ b/spec/unit/provider/service/daemontools.rb
@@ -0,0 +1,124 @@
+#!/usr/bin/env ruby
+# 
+# Unit testing for the Daemontools service Provider
+#
+# author Brice Figureau
+#
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:service).provider(:daemontools)
+
+describe provider_class do
+
+    before(:each) do
+        # Create a mock resource
+        @resource = stub 'resource'
+
+        @provider = provider_class.new
+        @servicedir = "/etc/service"
+        @[EMAIL PROTECTED]
+        @daemondir = "/var/lib/service"
+        @[EMAIL PROTECTED]
+
+        # A catch all; no parameters set
+        @resource.stubs(:[]).returns(nil)
+
+        # But set name, source and path (because we won't run
+        # the thing that will fetch the resource path from the provider)
+        @resource.stubs(:[]).with(:name).returns "myservice"
+        @resource.stubs(:[]).with(:ensure).returns :enabled
+        @resource.stubs(:[]).with(:path).returns @daemondir
+        @resource.stubs(:ref).returns "Service[myservice]"
+
+        @provider.stubs(:resource).returns @resource
+    end
+    
+    it "should have a restartcmd method" do
+        @provider.should respond_to(:restartcmd)
+    end
+
+    it "should have a start method" do
+        @provider.should respond_to(:start)
+    end
+
+    it "should have a stop method" do
+        @provider.should respond_to(:stop)
+    end
+
+    it "should have an enabled? method" do
+        @provider.should respond_to(:enabled?)
+    end
+
+    it "should have an enable method" do
+        @provider.should respond_to(:enable)
+    end
+
+    it "should have a disable method" do
+        @provider.should respond_to(:disable)
+    end
+
+    describe "when starting" do
+        it "should call enable" do
+            @provider.expects(:enable)
+            @provider.start
+        end
+    end
+
+    describe "when stopping" do
+        it "should call disable" do
+            @provider.expects(:disable)
+            @provider.stop
+        end
+    end
+
+    describe "when enabling" do
+        it "should create a symlink between daemon dir and service dir" do
+            FileTest.stubs(:symlink?).returns(false)
+            File.expects(:symlink).with(File.join(@daemondir,"myservice"), 
File.join(@servicedir,"myservice")).returns(0)
+            @provider.enable
+        end
+    end
+
+    describe "when disabling" do
+        it "should stop and then remove the symlink between daemon dir and 
service dir" do
+            FileTest.stubs(:directory?).returns(false)
+            FileTest.stubs(:symlink?).returns(true)
+            
File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0)
+            @provider.stubs(:texecute).returns("")
+            @provider.disable
+        end
+    end
+
+    describe "when disabling" do
+        it "should also call 'svc -dx /etc/service/myservice'" do
+            FileTest.stubs(:directory?).returns(false)
+            FileTest.stubs(:symlink?).returns(true)
+            
File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0)
+            @provider.expects(:texecute).with("stop",  [nil, '-dx', 
File.join(@servicedir,"myservice")]).returns ""
+            @provider.disable
+        end
+    end
+
+    describe "when checking status" do
+        it "should call the external command 'svstat /etc/service/myservice'" 
do
+            @provider.expects(:svstat).with(File.join(@servicedir,"myservice"))
+            @provider.status
+        end
+    end
+
+    describe "when checking status" do
+        it "and svstat fails, properly raise a Puppet::Error" do
+            
@provider.expects(:svstat).with(File.join(@servicedir,"myservice")).raises(Puppet::ExecutionFailure,
 "failure")
+            lambda { @provider.status }.should raise_error(Puppet::Error, 
'Could not get status for service Service[myservice]: failure')
+        end
+        it "and svstat returns up, then return :running" do
+            
@provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice:
 up (pid 454) 954326 seconds")
+            @provider.status.should == :running
+        end
+        it "and svstat returns not running, then return :stopped" do
+            
@provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice:
 supervise not running")
+            @provider.status.should  == :stopped
+        end
+    end
+
+ end
-- 
1.5.6.5


--~--~---------~--~----~------------~-------~--~----~
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