summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--lib/puppet/provider/service/runit.rb93
-rw-r--r--spec/unit/provider/service/runit.rb117
3 files changed, 212 insertions, 0 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 11107ebfb..8ce2c3a8d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,6 @@
0.24.x
+ Added daemontools and runit providers for service type
+
Added simple rake task for running unit tests
Added spec Rake task
diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb
new file mode 100644
index 000000000..230fa75d9
--- /dev/null
+++ b/lib/puppet/provider/service/runit.rb
@@ -0,0 +1,93 @@
+# Daemontools service management
+#
+# author Brice Figureau <brice-puppet@daysofwonder.com>
+Puppet::Type.type(:service).provide :runit, :parent => :daemontools do
+ desc "Runit service management.
+ This provider manages daemons running supervised by Runit.
+ It tries to detect the service directory, with by order of preference:
+ * /service
+ * /var/service
+ * /etc/service
+ The daemon directory should be placed in a directory that can be
+ by default in:
+ * /etc/sv
+ or this can be overriden in the service resource parameters:
+ service {
+ \"myservice\":
+ provider => \"runit\", path => \"/path/to/daemons\";
+ }
+
+ This provider supports out of the box:
+ * start/stop
+ * enable/disable
+ * restart
+ * status"
+
+ commands :sv => "/usr/bin/sv"
+
+ class << self
+ # 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
+ ["/etc/sv", "/var/lib/service"].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
+
+ # find the service dir on this node
+ def servicedir
+ unless defined?(@servicedir) and @servicedir
+ ["/service", "/etc/service","/var/service"].each do |path|
+ if FileTest.exist?(path)
+ @servicedir = path
+ break
+ end
+ end
+ raise "Could not find service directory" unless @servicedir
+ end
+ @servicedir
+ end
+
+ def restartcmd
+ [ command(:sv), "restart", self.service]
+ end
+
+ def status
+ begin
+ output = sv "status", self.daemon
+ return :running if output =~ /^run: /
+ rescue Puppet::ExecutionFailure => detail
+ unless detail.message =~ /(warning: |runsv not running$)/
+ raise Puppet::Error.new( "Could not get status for service %s: %s" % [ resource.ref, detail] )
+ end
+ end
+ return :stopped
+ end
+
+ # relay to the stopcmd
+ def stop
+ ucommand( :stop )
+ end
+
+ def stopcmd
+ [ command(:sv), "stop", self.service]
+ end
+
+ # disable by removing the symlink so that runit
+ # doesn't restart our service behind our back
+ # note that runit doesn't need to perform a stop
+ # before a disable
+ def disable
+ # unlink the daemon symlink to disable it
+ File.unlink(self.service) if FileTest.symlink?(self.service)
+ end
+end
+
diff --git a/spec/unit/provider/service/runit.rb b/spec/unit/provider/service/runit.rb
new file mode 100644
index 000000000..8eb53849b
--- /dev/null
+++ b/spec/unit/provider/service/runit.rb
@@ -0,0 +1,117 @@
+#!/usr/bin/env ruby
+#
+# Unit testing for the Runit service Provider
+#
+# author Brice Figureau
+#
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:service).provider(:runit)
+
+describe provider_class do
+
+ before(:each) do
+ # Create a mock resource
+ @resource = stub 'resource'
+
+ @provider = provider_class.new
+ @servicedir = "/etc/service"
+ @provider.servicedir=@servicedir
+ @daemondir = "/etc/sv"
+ @provider.class.defpath=@daemondir
+
+ # 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 execute external command 'sv stop /etc/service/myservice'" do
+ @provider.expects(:ucommand).with(:stop).returns("")
+ @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 remove the '/etc/service/myservice' symlink" do
+ FileTest.stubs(:directory?).returns(false)
+ FileTest.stubs(:symlink?).returns(true)
+ File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0)
+ @provider.disable
+ end
+ end
+
+ describe "when checking status" do
+ it "should call the external command 'sv status /etc/sv/myservice'" do
+ @provider.expects(:sv).with('status',File.join(@daemondir,"myservice"))
+ @provider.status
+ end
+ end
+
+ describe "when checking status" do
+ it "and sv status fails, properly raise a Puppet::Error" do
+ @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).raises(Puppet::ExecutionFailure, "fail: /etc/sv/myservice: file not found")
+ lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found')
+ end
+ it "and sv status returns up, then return :running" do
+ @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("run: /etc/sv/myservice: (pid 9029) 6s")
+ @provider.status.should == :running
+ end
+ it "and sv status returns not running, then return :stopped" do
+ @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("fail: /etc/sv/myservice: runsv not running")
+ @provider.status.should == :stopped
+ end
+ it "and sv status returns a warning, then return :stopped" do
+ @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist")
+ @provider.status.should == :stopped
+ end
+ end
+
+ end