The following patch will be combined with the original 2/2 patch. This also
cleans-up embedded hard-tab, although not shown due to white space
suppression.
- Cameron Thomas
diff --git a/spec/unit/provider/service/windows_spec.rb
b/spec/unit/provider/service/windows_spec.rb
index 4ef2a3a..be2f33c 100755
--- a/spec/unit/provider/service/windows_spec.rb
+++ b/spec/unit/provider/service/windows_spec.rb
@@ -6,11 +6,11 @@
require 'spec_helper'
require 'ostruct'
-require 'win32/service'
+require 'win32/service' if Puppet.features.microsoft_windows?
provider_class = Puppet::Type.type(:service).provider(:windows)
-describe provider_class do
+describe provider_class, :if => Puppet.features.microsoft_windows? do
before :each do
@provider = Puppet::Type.type(:service).provider(:windows)
@@ -108,6 +108,9 @@ describe provider_class do
resource.provider.enabled?.should == false
end
+ # We need to guard this section explicitly since rspec will always
+ # construct all examples, even if it isn't going to run them.
+ if Puppet.features.microsoft_windows?
[Win32::Service::SERVICE_AUTO_START,
Win32::Service::SERVICE_BOOT_START,
Win32::Service::SERVICE_SYSTEM_START].each do |start_type_const|
start_type =
Win32::Service.get_start_type(start_type_const)
it "should report a service with a startup type of
'#{start_type}' as true" do
@@ -124,3 +127,4 @@ describe provider_class do
end
end
end
+end
On Fri, Jul 22, 2011 at 3:42 PM, Cameron Thomas <[email protected]>wrote:
> Unfortunately this patch went out prior to integration into master, and the
> spec test broke the general (non-Windows) spec tests. The correction is
> in-progress, and will be issued once verified.
>
> Mea culpa -
>
> Cameron Thomas
>
>
> On Fri, Jul 22, 2011 at 3:22 PM, Cameron Thomas <[email protected]>wrote:
>
>> This provider allows us to query the system state through "puppet
>> resource", and manage the ensure, and enabled properties of services on
>> Windows.
>>
>> This also adds support for a new enabled value of 'manual' on Windows
>> only. With this we support the three major start types for services on
>> Windows, with the following mapping of enabled to start type:
>>
>> true => Automatic
>> false => Disabled
>> manual => Manual (Demand)
>>
>> We use the win32-service gem to provide access to the Windows APIs for
>> our operations. This does add a new gem requirement for running Puppet
>> on Windows, but we were already requiring some gems from the same suite
>> that win32-service is a part of.
>>
>> When referring to a service, the simple service name must be used,
>> instead of the display name. For example, "snmptrap", instead of
>> "SNMP Trap".
>>
>> All system services are reported in 'puppet resource service',
>> including those started prior to run level 3 (system, device drivers,
>> etc.). These services should probably not be managed, without careful
>> thought and planning.
>>
>> This currently does not support being able to move a service from
>> {enabled => false, ensure => stopped} to {enabled => true, ensure =>
>> running} (or enabled => manual) in a single Puppet run, since Puppet
>> currently always tries to sync ensure before any other property.
>> Because of this, the puppet run will fail every time, and the service
>> must first be managed as {ensure => stopped, enabled => true} (or
>> enabled => manual), before it can be managed as running and automatic
>> start or manual start.
>>
>> Reviewed by: Jacob Helwig <[email protected]>
>>
>> Signed-off-by: Cameron Thomas <[email protected]>
>> ---
>> Local-branch: feature/master/8272-windows_service_support
>> lib/puppet/feature/base.rb | 3 +-
>> lib/puppet/provider/service/windows.rb | 101 ++++++++++++++++++++++
>> lib/puppet/type/service.rb | 10 ++
>> spec/unit/provider/service/windows_spec.rb | 126
>> ++++++++++++++++++++++++++++
>> spec/unit/type/service_spec.rb | 15 ++++
>> 5 files changed, 254 insertions(+), 1 deletions(-)
>> create mode 100644 lib/puppet/provider/service/windows.rb
>> create mode 100755 spec/unit/provider/service/windows_spec.rb
>>
>> diff --git a/lib/puppet/feature/base.rb b/lib/puppet/feature/base.rb
>> index 9ed3dee..b4b1313 100644
>> --- a/lib/puppet/feature/base.rb
>> +++ b/lib/puppet/feature/base.rb
>> @@ -48,8 +48,9 @@ Puppet.features.add(:microsoft_windows) do
>> require 'sys/admin'
>> require 'win32/process'
>> require 'win32/dir'
>> + require 'win32/service'
>> rescue LoadError => err
>> - warn "Cannot run on Microsoft Windows without the sys-admin,
>> win32-process & win32-dir gems: #{err}" unless Puppet.features.posix?
>> + warn "Cannot run on Microsoft Windows without the sys-admin,
>> win32-process, win32-dir & win32-service gems: #{err}" unless
>> Puppet.features.posix?
>> end
>> end
>>
>> diff --git a/lib/puppet/provider/service/windows.rb
>> b/lib/puppet/provider/service/windows.rb
>> new file mode 100644
>> index 0000000..09754ff
>> --- /dev/null
>> +++ b/lib/puppet/provider/service/windows.rb
>> @@ -0,0 +1,101 @@
>> +# Windows Service Control Manager (SCM) provider
>> +
>> +require 'win32/service' if Puppet.features.microsoft_windows?
>> +
>> +Puppet::Type.type(:service).provide :windows do
>> +
>> + desc "Support for Windows Service Control Manager (SCM).
>> +
>> + Services are controlled according to win32-service gem.
>> +
>> + * All SCM operations (start/stop/enable/disable/query) are supported.
>> +
>> + * Control of service groups (dependencies) is not yet supported."
>> +
>> + defaultfor :operatingsystem => :windows
>> + confine :operatingsystem => :windows
>> +
>> + has_feature :refreshable
>> +
>> + def enable
>> + w32ss = Win32::Service.configure( 'service_name' => @resource[:name],
>> 'start_type' => Win32::Service::SERVICE_AUTO_START )
>> + raise Puppet::Error.new("Win32 service enable of #{@resource[:name]}
>> failed" ) if( w32ss.nil? )
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot enable #{@resource[:name]}, error
>> was: #{detail}" )
>> + end
>> +
>> + def disable
>> + w32ss = Win32::Service.configure( 'service_name' => @resource[:name],
>> 'start_type' => Win32::Service::SERVICE_DISABLED )
>> + raise Puppet::Error.new("Win32 service disable of #{@resource[:name]}
>> failed" ) if( w32ss.nil? )
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot disable #{@resource[:name]}, error
>> was: #{detail}" )
>> + end
>> +
>> + def manual_start
>> + w32ss = Win32::Service.configure( 'service_name' => @resource[:name],
>> 'start_type' => Win32::Service::SERVICE_DEMAND_START )
>> + raise Puppet::Error.new("Win32 service manual enable of
>> #{@resource[:name]} failed" ) if( w32ss.nil? )
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot enable #{@resource[:name]} for manual
>> start, error was: #{detail}" )
>> + end
>> +
>> + def enabled?
>> + w32ss = Win32::Service.config_info( @resource[:name] )
>> + raise Puppet::Error.new("Win32 service query of #{@resource[:name]}
>> failed" ) unless( !w32ss.nil? && w32ss.instance_of?(
>> Struct::ServiceConfigInfo ) )
>> + Puppet.debug("Service #{@resource[:name]} start type is
>> #{w32ss.start_type}")
>> + case w32ss.start_type
>> + when
>> Win32::Service.get_start_type(Win32::Service::SERVICE_AUTO_START),
>> +
>> Win32::Service.get_start_type(Win32::Service::SERVICE_BOOT_START),
>> +
>> Win32::Service.get_start_type(Win32::Service::SERVICE_SYSTEM_START)
>> + true
>> + when
>> Win32::Service.get_start_type(Win32::Service::SERVICE_DEMAND_START)
>> + :manual
>> + when
>> Win32::Service.get_start_type(Win32::Service::SERVICE_DISABLED)
>> + false
>> + else
>> + raise Puppet::Error.new("Unknown start type:
>> #{w32ss.start_type}")
>> + end
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot get start type for
>> #{@resource[:name]}, error was: #{detail}" )
>> + end
>> +
>> + def start
>> + Win32::Service.start( @resource[:name] )
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was:
>> #{detail}" )
>> + end
>> +
>> + def stop
>> + Win32::Service.stop( @resource[:name] )
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was:
>> #{detail}" )
>> + end
>> +
>> + def restart
>> + self.stop
>> + self.start
>> + end
>> +
>> + def status
>> + w32ss = Win32::Service.status( @resource[:name] )
>> + raise Puppet::Error.new("Win32 service query of #{@resource[:name]}
>> failed" ) unless( !w32ss.nil? && w32ss.instance_of?( Struct::ServiceStatus )
>> )
>> + state = case w32ss.current_state
>> + when "stopped", "pause pending", "stop pending", "paused" then
>> :stopped
>> + when "running", "continue pending", "start pending" then
>> :running
>> + else
>> + raise Puppet::Error.new("Unknown service state
>> '#{w32ss.current_state}' for service '#{@resource[:name]}'")
>> + end
>> + Puppet.debug("Service #{@resource[:name]} is #{w32ss.current_state}")
>> + return state
>> + rescue Win32::Service::Error => detail
>> + raise Puppet::Error.new("Cannot get status of #{@resource[:name]},
>> error was: #{detail}" )
>> + end
>> +
>> + # returns all providers for all existing services and startup state
>> + def self.instances
>> + srvcs = []
>> + Win32::Service.services.collect{ |s|
>> + srvcs << new(:name => s.service_name)
>> + }
>> + srvcs
>> + end
>> +end
>> diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb
>> index 5a2c69b..53ff728 100644
>> --- a/lib/puppet/type/service.rb
>> +++ b/lib/puppet/type/service.rb
>> @@ -49,9 +49,19 @@ module Puppet
>> provider.disable
>> end
>>
>> + newvalue(:manual, :event => :service_manual_start) do
>> + provider.manual_start
>> + end
>> +
>> def retrieve
>> provider.enabled?
>> end
>> +
>> + validate do |value|
>> + if value == :manual and !Puppet.features.microsoft_windows?
>> + raise Puppet::Error.new("Setting enable to manual is only
>> supported on Microsoft Windows.")
>> + end
>> + end
>> end
>>
>> # Handle whether the service should actually be running right now.
>> diff --git a/spec/unit/provider/service/windows_spec.rb
>> b/spec/unit/provider/service/windows_spec.rb
>> new file mode 100755
>> index 0000000..4ef2a3a
>> --- /dev/null
>> +++ b/spec/unit/provider/service/windows_spec.rb
>> @@ -0,0 +1,126 @@
>> +#!/usr/bin/env rspec
>> +#
>> +# Unit testing for the Windows service Provider
>> +#
>> +
>> +require 'spec_helper'
>> +
>> +require 'ostruct'
>> +require 'win32/service'
>> +
>> +provider_class = Puppet::Type.type(:service).provider(:windows)
>> +
>> +describe provider_class do
>> +
>> + before :each do
>> + @provider = Puppet::Type.type(:service).provider(:windows)
>> +
>> Puppet::Type.type(:service).stubs(:provider).returns(@provider)
>> + end
>> +
>> + describe ".instances" do
>> + it "should enumerate all services" do
>> + list_of_services = ['snmptrap', 'svchost',
>> 'sshd'].map {|s| OpenStruct.new(:service_name => s)}
>> +
>> Win32::Service.expects(:services).returns(list_of_services)
>> +
>> + provider_class.instances.map {|provider|
>> provider.name}.should =~ ['snmptrap', 'svchost', 'sshd']
>> + end
>> + end
>> +
>> + describe "#start" do
>> + it "should call out to the Win32::Service API to start the
>> service" do
>> + Win32::Service.expects(:start).with('snmptrap')
>> +
>> + resource = Puppet::Type.type(:service).new(:name
>> => 'snmptrap')
>> + resource.provider.start
>> + end
>> +
>> + it "should handle when Win32::Service.start raises a
>> Win32::Service::Error" do
>> +
>> Win32::Service.expects(:start).with('snmptrap').raises(
>> + Win32::Service::Error.new("The service
>> cannot be started, either because it is disabled or because it has no
>> enabled devices associated with it.")
>> + )
>> +
>> + resource = Puppet::Type.type(:service).new(:name
>> => 'snmptrap')
>> + expect { resource.provider.start }.to raise_error(
>> + Puppet::Error,
>> + /Cannot start snmptrap, error was: The
>> service cannot be started, either/
>> + )
>> + end
>> + end
>> +
>> + describe "#stop" do
>> + it "should stop a running service"
>> + it "should not try to stop an already stopped service"
>> + end
>> +
>> + describe "#status" do
>> + ['stopped', 'paused', 'stop pending', 'pause
>> pending'].each do |state|
>> + it "should report a #{state} service as stopped"
>> do
>> +
>> Win32::Service.expects(:status).with('snmptrap').returns(
>> + stub(
>> + 'struct_service_status',
>> + :instance_of? =>
>> Struct::ServiceStatus,
>> + :current_state => state
>> + )
>> + )
>> + resource =
>> Puppet::Type.type(:service).new(:name => 'snmptrap')
>> +
>> + resource.provider.status.should ==
>> :stopped
>> + end
>> + end
>> +
>> + ["running", "continue pending", "start pending" ].each do
>> |state|
>> + it "should report a #{state} service as running"
>> do
>> +
>> Win32::Service.expects(:status).with('snmptrap').returns(
>> + stub(
>> + 'struct_service_status',
>> + :instance_of? =>
>> Struct::ServiceStatus,
>> + :current_state => state
>> + )
>> + )
>> + resource =
>> Puppet::Type.type(:service).new(:name => 'snmptrap')
>> + resource.provider.status.should ==
>> :running
>> + end
>> + end
>> + end
>> +
>> + describe "#enabled?" do
>> + it "should report a service with a startup type of manual
>> as manual" do
>> +
>> Win32::Service.expects(:config_info).with('snmptrap').returns(
>> + stub(
>> + 'struct_config_info',
>> + :instance_of? =>
>> Struct::ServiceConfigInfo,
>> + :start_type =>
>> Win32::Service.get_start_type(Win32::Service::SERVICE_DEMAND_START)
>> + )
>> + )
>> + resource = Puppet::Type.type(:service).new(:name
>> => 'snmptrap')
>> + resource.provider.enabled?.should == :manual
>> + end
>> +
>> + it "should report a service with a startup type of
>> disabled as false" do
>> +
>> Win32::Service.expects(:config_info).with('snmptrap').returns(
>> + stub(
>> + 'struct_config_info',
>> + :instance_of? =>
>> Struct::ServiceConfigInfo,
>> + :start_type =>
>> Win32::Service.get_start_type(Win32::Service::SERVICE_DISABLED)
>> + )
>> + )
>> + resource = Puppet::Type.type(:service).new(:name
>> => 'snmptrap')
>> + resource.provider.enabled?.should == false
>> + end
>> +
>> + [Win32::Service::SERVICE_AUTO_START,
>> Win32::Service::SERVICE_BOOT_START,
>> Win32::Service::SERVICE_SYSTEM_START].each do |start_type_const|
>> + start_type =
>> Win32::Service.get_start_type(start_type_const)
>> + it "should report a service with a startup type of
>> '#{start_type}' as true" do
>> +
>> Win32::Service.expects(:config_info).with('snmptrap').returns(
>> + stub(
>> + 'struct_config_info',
>> + :instance_of? =>
>> Struct::ServiceConfigInfo,
>> + :start_type =>
>> start_type
>> + )
>> + )
>> + resource =
>> Puppet::Type.type(:service).new(:name => 'snmptrap')
>> + resource.provider.enabled?.should == true
>> + end
>> + end
>> + end
>> +end
>> diff --git a/spec/unit/type/service_spec.rb
>> b/spec/unit/type/service_spec.rb
>> index 40270e7..ab006a4 100755
>> --- a/spec/unit/type/service_spec.rb
>> +++ b/spec/unit/type/service_spec.rb
>> @@ -57,6 +57,21 @@ describe Puppet::Type.type(:service), "when validating
>> attribute values" do
>> Puppet::Type.type(:service).new(:name => "yay", :enable => :false)
>> end
>>
>> + it "should support :manual as a value to :enable on Windows" do
>> + Puppet.features.stubs(:microsoft_windows?).returns true
>> +
>> + Puppet::Type.type(:service).new(:name => "yay", :enable => :manual)
>> + end
>> +
>> + it "should not support :manual as a value to :enable when not on
>> Windows" do
>> + Puppet.features.stubs(:microsoft_windows?).returns false
>> +
>> + expect { Puppet::Type.type(:service).new(:name => "yay", :enable =>
>> :manual) }.to raise_error(
>> + Puppet::Error,
>> + /Setting enable to manual is only supported on Microsoft Windows\./
>> + )
>> + end
>> +
>> it "should support :true as a value to :hasstatus" do
>> Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :true)
>> end
>> --
>> 1.7.5.4
>>
>>
>
>
> --
> Join us for PuppetConf <http://bit.ly/puppetconfsig>, September 22nd and
> 23rd in Portland, OR. |
> bit.ly/puppetconfsig
>
--
Join us for PuppetConf <http://bit.ly/puppetconfsig>, September 22nd and
23rd in Portland, OR. |
bit.ly/puppetconfsig
--
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.