summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Bonds <scott@ggr.com>2014-10-11 10:17:07 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2015-01-25 10:43:19 -0800
commit6b732127edbf1b63ae9748a80cc869ad4230c9b2 (patch)
tree95ee761e3527b23b48f232d8c6a3202261d8c7c6
parent27b0b69b3cd2d2ec84a7ff37ac46453ae942f938 (diff)
downloadchef-6b732127edbf1b63ae9748a80cc869ad4230c9b2.tar.gz
add service provider for OpenBSD
-rw-r--r--lib/chef/platform/provider_mapping.rb3
-rw-r--r--lib/chef/provider/service/openbsd.rb466
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/service.rb11
-rw-r--r--spec/unit/provider/service/openbsd_service_spec.rb521
5 files changed, 1001 insertions, 1 deletions
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 271e72f761..3c7ecf038c 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -366,7 +366,8 @@ class Chef
:openbsd => {
:default => {
:group => Chef::Provider::Group::Usermod,
- :package => Chef::Provider::Package::Openbsd
+ :package => Chef::Provider::Package::Openbsd,
+ :service => Chef::Provider::Service::Openbsd
}
},
:hpux => {
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
new file mode 100644
index 0000000000..a587525a7d
--- /dev/null
+++ b/lib/chef/provider/service/openbsd.rb
@@ -0,0 +1,466 @@
+#
+# Author:: Scott Bonds (<scott@ggr.com>)
+# Copyright:: Copyright (c) 2014 Scott Bonds
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/mixin/command'
+require 'chef/mixin/shell_out'
+require 'chef/provider/service/init'
+require 'chef/resource/service'
+
+class Chef
+ class Provider
+ class Service
+ class Openbsd < Chef::Provider::Service::Init
+
+ include Chef::Mixin::ShellOut
+
+ def initialize(new_resource, run_context)
+ super
+ @init_command = nil
+ if ::File.exist?("/etc/rc.d/#{new_resource.service_name}")
+ @init_command = "/etc/rc.d/#{new_resource.service_name}"
+ end
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ @rcd_script_found = true
+
+ # Determine if we're talking about /etc/rc.d or /usr/local/etc/rc.d
+ if ::File.exists?("/etc/rc.d/#{current_resource.service_name}")
+ @init_command = "/etc/rc.d/#{current_resource.service_name}"
+ elsif ::File.exists?("/usr/local/etc/rc.d/#{current_resource.service_name}")
+ @init_command = "/usr/local/etc/rc.d/#{current_resource.service_name}"
+ else
+ @rcd_script_found = false
+ return
+ end
+ Chef::Log.debug("#{@current_resource} found at #{@init_command}")
+ determine_current_status!
+ determine_enabled_status!
+
+ @current_resource
+ end
+
+ def define_resource_requirements
+ shared_resource_requirements
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { @rcd_script_found }
+ a.failure_message Chef::Exceptions::Service, "#{@new_resource}: unable to locate the rc.d script"
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { @enabled_state_found }
+ # for consistency with original behavior, this will not fail in non-whyrun mode;
+ # rather it will silently set enabled state=>false
+ a.whyrun "Unable to determine enabled/disabled state, assuming this will be correct for an actual run. Assuming disabled."
+ end
+
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { @rcd_script_found && builtin_service_enable_variable_name != nil }
+ a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{@init_command} and rcvar"
+ # No recovery in whyrun mode - the init file is present but not correct.
+ end
+ end
+
+ def start_service
+ if @new_resource.start_command
+ super
+ else
+ shell_out_with_systems_locale!("#{@init_command} start")
+ end
+ end
+
+ def stop_service
+ if @new_resource.stop_command
+ super
+ else
+ shell_out_with_systems_locale!("#{@init_command} stop")
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ super
+ elsif @new_resource.supports[:restart]
+ shell_out_with_systems_locale!("#{@init_command} restart")
+ else
+ stop_service
+ sleep 1
+ start_service
+ end
+ end
+
+ def enable_service()
+ set_service_enable(true)
+ end
+
+ def disable_service()
+ set_service_enable(false)
+ end
+
+ protected
+
+ def determine_current_status!
+ if !@new_resource.status_command && @new_resource.supports[:status]
+ Chef::Log.debug("#{@new_resource} supports status, running")
+ begin
+ if shell_out("#{default_init_command} check").exitstatus == 0
+ @current_resource.running true
+ Chef::Log.debug("#{@new_resource} is running")
+ end
+ # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
+ # Temporarily catching different types of exceptions here until we get Shellout fixed.
+ # TODO: Remove the line before one we get the ShellOut fix.
+ rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError
+ @status_load_success = false
+ @current_resource.running false
+ nil
+ end
+ else
+ super
+ end
+ end
+
+ private
+
+ # The variable name used in /etc/rc.conf.local for enabling this service
+ def builtin_service_enable_variable_name
+ if @rcd_script_found
+ ::File.open(@init_command) do |rcscript|
+ rcscript.each_line do |line|
+ if line =~ /^# \$OpenBSD: (\w+)[(.rc),]?/
+ return $1 + "_flags"
+ end
+ end
+ end
+ end
+ # Fallback allows us to keep running in whyrun mode when
+ # the script does not exist.
+ @new_resource.service_name
+ end
+
+ def set_service_enable(enable)
+ var_name = builtin_service_enable_variable_name
+ if is_builtin
+ # a builtin service is enabled when either <service>_flags is
+ # set equal to something other than 'NO', can be blank or it can be
+ # a set of options that should be passed to the startup script
+ old_value = new_value = nil
+ if enable
+ if files['/etc/rc.conf.local'] && var_name
+ files['/etc/rc.conf.local'].split("\n").each do |line|
+ if line =~ /^#{Regexp.escape(var_name)}=(.*)/
+ old_value = $1
+ break
+ end
+ end
+ end
+ if files['/etc/rc.conf'] && var_name
+ files['/etc/rc.conf'].split("\n").each do |line|
+ if line =~ /^#{Regexp.escape(var_name)}=(.*)/
+ old_value = $1
+ break
+ end
+ end
+ end
+ if old_value && old_value =~ /"?[Nn][Oo]"?/
+ new_value = ''
+ else
+ new_value = old_value
+ Chef::Log.debug("service is already enabled and has parameters, skipping")
+ end
+ end
+ #Chef::Log.debug("SCOTT enable me-1!")
+ # This is run immediately so the service can be started at any time
+ # after the :enable action, during this Chef run.
+ contents = configurate(
+ setting: builtin_service_enable_variable_name,
+ value: new_value,
+ remove: !enable
+ )
+ else
+ #Chef::Log.debug("SCOTT enable me-2!")
+ #Chef::Log.debug("SCOTT service_name is #{@new_resource.service_name}")
+ service_name = @new_resource.service_name.clone
+ service_after = @new_resource.after ? @new_resource.after.clone : nil
+ contents = configurate(
+ setting: 'pkg_scripts',
+ format: :space_delimited_list,
+ value: service_name,
+ after: service_after,
+ remove: !enable
+ )
+ end
+ end
+
+ def is_builtin
+ result = false
+ var_name = builtin_service_enable_variable_name
+ if files['/etc/rc.conf'] && var_name
+ files['/etc/rc.conf'].split("\n").each do |line|
+ case line
+ when /^#{Regexp.escape(var_name)}=(.*)/
+ result = true
+ end
+ end
+ end
+ #Chef::Log.debug("SCOTT: builtin? #{result}")
+ result
+ end
+
+ def determine_enabled_status!
+ result = false # Default to disabled if the service doesn't currently exist at all
+ if is_builtin
+ var_name = builtin_service_enable_variable_name
+ if files['/etc/rc.conf.local'] && var_name
+ files['/etc/rc.conf.local'].split("\n").each do |line|
+ case line
+ when /^#{Regexp.escape(var_name)}=(.*)/
+ @enabled_state_found = true
+ if $1 =~ /"?[Nn][Oo]"?/
+ result = false
+ else
+ result = true
+ end
+ break
+ end
+ end
+ end
+ if files['/etc/rc.conf'] && var_name && !@enabled_state_found
+ files['/etc/rc.conf'].split("\n").each do |line|
+ case line
+ when /^#{Regexp.escape(var_name)}=(.*)/
+ @enabled_state_found = true
+ if $1 =~ /"?[Nn][Oo]"?/
+ result = false
+ else
+ result = true
+ end
+ break
+ end
+ end
+ end
+ else
+ var_name = @new_resource.service_name
+ if files['/etc/rc.conf'] && var_name
+ files['/etc/rc.conf'].split("\n").each do |line|
+ if line =~ /pkg_scripts="(.*)"/
+ @enabled_state_found = true
+ if $1.include?(var_name)
+ result = true
+ end
+ break
+ end
+ end
+ end
+ end
+ #Chef::Log.debug("SCOTT: enabled? #{result}")
+
+ current_resource.enabled result
+ end
+
+ # this is used for sorting lists of dependencies in configurate
+ class TsortableHash < Hash
+ include TSort
+ alias tsort_each_node each_key
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+ end
+
+ def afters
+ @@afters ||= {}
+ end
+
+ def afters=(new_value)
+ @@afters = new_value
+ end
+
+ def files
+ return @@files if defined? @@files
+ @@files = {}
+ ['/etc/rc.conf', '/etc/rc.conf.local'].each do |file|
+ if ::File.exists? file
+ @@files[file] = ::File.read(file)
+ end
+ end
+ @@files
+ end
+
+ def files=(new_value)
+ @@files = new_value
+ end
+
+ def configurate(options)
+ file = '/etc/rc.conf.local'
+
+ Chef::Log.debug("Configurate: options[:setting] #{options[:setting]}")
+ Chef::Log.debug("Configurate: options[:value] #{options[:value]}")
+ Chef::Log.debug("Configurate: options[:after] #{options[:after]}")
+ Chef::Log.debug("Configurate: options[:remove] #{options[:remove]}")
+ Chef::Log.debug("Configurate: options[:prefix] #{options[:prefix]}")
+
+ options[:comment] ||= '#'
+ options[:equals] ||= '='
+
+ # Store which values need to come 'after' which other values for a given
+ # file + setting. For example, on OpenBSD the '/etc/rc.conf.local' file
+ # has a setting called 'pkg_scripts' which specifies the order that
+ # (addon) services should start up. In some cases, you need a certain
+ # service to start only *after* some service it depends on has started.
+ afters["#{file}##{options[:setting]}"] = TsortableHash.new if !afters["#{file}##{options[:setting]}"]
+ Chef::Log.debug "AFTERS: #{afters}"
+ if options[:after]
+ if !afters["#{file}##{options[:setting]}"][options[:after]]
+ raise KeyError, 'Cannot configurate a value before the value it comes after'
+ end
+ afters["#{file}##{options[:setting]}"][options[:value]] = [options[:after]]
+ else
+ afters["#{file}##{options[:setting]}"][options[:value]] = []
+ end
+
+ old_contents = ''
+ new_contents = ''
+ found = false
+
+ old_contents = files[file]
+ if old_contents
+ old_contents.each_line do |old_line|
+ new_line = ''
+ if options[:setting]
+ m = old_line.match(Regexp.new("(.*?)#{(options[:prefix] + '(\s*)') if options[:prefix]}#{options[:setting]}(\s*)#{options[:equals]}(\s*)(.*)"))
+ if m
+ next if found # delete duplicate lines defining this same setting
+
+ line_prefix = m[1]
+ if options[:prefix]
+ modifier = 1
+ s_prefix_suffix = m[2]
+ else
+ modifier = 0
+ end
+ equals_prefix = m[2+modifier]
+ equals_suffix = m[3+modifier]
+ line_value = m[4+modifier].split(options[:comment])[0].strip
+ line_suffix = m[4+modifier][line_value.size..-1]
+ # puts "ov: #{line_value}"
+ # puts "ss: #{line_suffix}"
+
+ # If the prefix includes anything besides a options[:comment] symbol or a
+ # specified prefix, or the suffix doesn't start with a options[:comment]
+ # symbol, the line is probably documentation
+ if (
+ line_prefix &&
+ !line_prefix.strip.empty? &&
+ options[:comment] && line_prefix.strip != options[:comment] &&
+ options[:prefix] && line_prefix.strip != options[:prefix]
+ ) ||
+ (
+ line_suffix &&
+ !line_suffix.strip.empty? &&
+ options[:comment] && line_suffix.strip[0] != options[:comment]
+ )
+ new_contents << old_line
+ next
+ end
+
+ # Return the setting's current value if no new value was passed
+ return line_value if !options[:value] && !options[:remove]
+ found = true
+
+ case options[:format]
+ when :space_delimited_list
+ list_items = line_value.gsub('"', '').split(' ').map{|a| a.strip}
+ list_items << options[:value]
+ list_items = list_items.uniq
+ if options[:remove]
+ list_items.delete_if {|a| a == options[:value]}
+ end
+ if afters["#{file}##{options[:setting]}"]
+ list_items.each do |item|
+ afters["#{file}##{options[:setting]}"][item] = [] if !afters["#{file}##{options[:setting]}"][item]
+ end
+ #Chef::Log.debug "afters: #{@@afters}"
+ new_value = "\"#{afters["#{file}##{options[:setting]}"].tsort.join(' ')}\""
+ else
+ #Chef::Log.debug "no afters found"
+ new_value = "\"#{list_items.sort.join(' ')}\""
+ end
+ else
+ if options[:value] == ''
+ new_value = '""' # print an empty string instead of leaving the value blank
+ else
+ new_value = "#{options[:value]}"
+ end
+ end
+ new_line = "#{line_prefix.sub(options[:comment], '')}"
+ new_line += "#{options[:prefix]}#{s_prefix_suffix}" if options[:prefix]
+ new_line += "#{options[:setting]}#{equals_prefix}#{options[:equals]}#{equals_suffix}#{new_value}#{line_suffix}\n"
+ else
+ new_line = old_line
+ end
+ else
+ if old_line.gsub(/\s{2,}/, ' ').include?(options[:value].gsub(/\s{2,}/, ' '))
+ next if found
+ found = true
+ end
+ new_line = old_line
+ end
+ new_contents << new_line unless found && options[:remove]
+ # if old_line != new_line
+ # puts "old: #{old_line}"
+ # puts "new: #{new_line}"
+ # end
+ end
+ end
+
+ if !found && !options[:remove]
+ if options[:setting]
+ case options[:format]
+ when :space_delimited_list
+ new_value = "\"#{options[:value]}\""
+ else
+ if options[:value] == ''
+ new_value = '""' # print an empty string instead of leaving the value blank
+ else
+ new_value = "#{options[:value]}"
+ end
+ end
+ new_line = "#{options[:prefix] + ' ' if options[:prefix]}#{options[:setting]}#{options[:equals]}#{new_value}\n"
+ else
+ new_line = "#{options[:value]}\n"
+ end
+ # puts "new: #{new_line}"
+ new_contents << "\n" if old_contents && !old_contents.empty? && old_contents[-1] != "\n"
+ new_contents << new_line
+ end
+
+ new_contents = new_contents.gsub(/\n{2,}/, "\n\n") # remove extra lines
+ #Chef::Log.debug 'new_contents:'
+
+ if old_contents != new_contents
+ files[file] = new_contents
+ ::File.write('/etc/rc.conf.local', new_contents)
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 38c0e6fc9a..796a0f8fa6 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -81,6 +81,7 @@ require 'chef/provider/service/gentoo'
require 'chef/provider/service/init'
require 'chef/provider/service/invokercd'
require 'chef/provider/service/debian'
+require 'chef/provider/service/openbsd'
require 'chef/provider/service/redhat'
require 'chef/provider/service/insserv'
require 'chef/provider/service/simple'
diff --git a/lib/chef/resource/service.rb b/lib/chef/resource/service.rb
index 36df7c859a..8b2490c3e4 100644
--- a/lib/chef/resource/service.rb
+++ b/lib/chef/resource/service.rb
@@ -42,6 +42,7 @@ class Chef
@reload_command = nil
@init_command = nil
@priority = nil
+ @after = nil
@timeout = nil
@action = "nothing"
@supports = { :restart => false, :reload => false, :status => false }
@@ -158,6 +159,16 @@ class Chef
)
end
+ # after only applies to non-builtin (pkg_scripts enabled) OpenBSD
+ # services
+ def after(arg=nil)
+ set_or_return(
+ :after,
+ arg,
+ :kind_of => String
+ )
+ end
+
# timeout only applies to the windows service manager
def timeout(arg=nil)
set_or_return(
diff --git a/spec/unit/provider/service/openbsd_service_spec.rb b/spec/unit/provider/service/openbsd_service_spec.rb
new file mode 100644
index 0000000000..55fd904671
--- /dev/null
+++ b/spec/unit/provider/service/openbsd_service_spec.rb
@@ -0,0 +1,521 @@
+#
+# Author:: Bryan McLellan (btm@loftninjas.org)
+# Author:: Scott Bonds (scott@ggr.com)
+# Copyright:: Copyright (c) 2009 Bryan McLellan
+# Copyright:: Copyright (c) 2014 Scott Bonds
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+class Chef::Provider::Service::Openbsd
+ public :builtin_service_enable_variable_name
+ public :determine_enabled_status!
+ public :determine_current_status!
+ public :files
+ public :files=
+ public :afters=
+end
+
+describe Chef::Provider::Service::Openbsd do
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic_attrs[:command] = {:ps => "ps -ax"}
+ node
+ end
+
+ let(:new_resource) do
+ new_resource = Chef::Resource::Service.new("sndiod")
+ new_resource.pattern("sndiod")
+ new_resource.supports({:status => false})
+ new_resource
+ end
+
+ let(:current_resource) do
+ current_resource = Chef::Resource::Service.new("sndiod")
+ current_resource
+ end
+
+ let(:provider) do
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ provider = Chef::Provider::Service::Openbsd.new(new_resource,run_context)
+ provider.action = :start
+ provider
+ end
+
+ before do
+ allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ end
+
+ def stub_etc_rcd_script
+ allow(::File).to receive(:exist?).and_return(false)
+ expect(::File).to receive(:exist?).with("/etc/rc.d/#{new_resource.service_name}").and_return(true)
+ end
+
+ def run_load_current_resource
+ stub_etc_rcd_script
+ provider.load_current_resource
+ end
+
+ describe Chef::Provider::Service::Openbsd, "initialize" do
+ it "should find /etc/rc.d init scripts" do
+ stub_etc_rcd_script
+ expect(provider.init_command).to eql "/etc/rc.d/sndiod"
+ end
+
+ it "should set init_command to nil if it can't find anything" do
+ allow(::File).to receive(:exist?).and_return(false)
+ expect(provider.init_command).to be nil
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "determine_current_status!" do
+ before do
+ stub_etc_rcd_script
+ provider.current_resource = current_resource
+ current_resource.service_name(new_resource.service_name)
+ end
+
+ context "when a status command has been specified" do
+ let(:status) { double(:stdout => "", :exitstatus => 0) }
+
+ before do
+ new_resource.status_command("/bin/chefhasmonkeypants status")
+ end
+
+ it "should run the services status command if one has been specified" do
+ expect(provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(status)
+ provider.determine_current_status!
+ end
+ end
+
+ context "when the service supports status" do
+ let(:status) { double(:stdout => "", :exitstatus => 0) }
+
+ before do
+ new_resource.supports({:status => true})
+ end
+
+ it "should run '/etc/rc.d/service_name status'" do
+ expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status)
+ provider.determine_current_status!
+ end
+
+ it "should set running to true if the status command returns 0" do
+ expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status)
+ provider.determine_current_status!
+ expect(current_resource.running).to be true
+ end
+
+ it "should set running to false if the status command returns anything except 0" do
+ expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ provider.determine_current_status!
+ expect(current_resource.running).to be false
+ end
+ end
+
+ context "when we have a 'ps' attribute" do
+ let(:stdout) do
+ StringIO.new(<<-PS_SAMPLE)
+413 ?? Ss 0:02.51 /usr/sbin/syslogd -s
+539 ?? Is 0:00.14 /usr/sbin/sshd
+545 ?? Ss 0:17.53 sendmail: accepting connections (sendmail)
+PS_SAMPLE
+ end
+ let(:status) { double(:stdout => stdout, :exitstatus => 0) }
+
+ before do
+ node.automatic_attrs[:command] = {:ps => "ps -ax"}
+ end
+
+ it "should shell_out! the node's ps command" do
+ expect(provider).to receive(:shell_out!).with(node[:command][:ps]).and_return(status)
+ provider.determine_current_status!
+ end
+
+ it "should read stdout of the ps command" do
+ allow(provider).to receive(:shell_out!).and_return(status)
+ expect(stdout).to receive(:each_line).and_return(true)
+ provider.determine_current_status!
+ end
+
+ context "when the regex matches the output" do
+ let(:stdout) do
+ StringIO.new(<<-PS_SAMPLE)
+555 ?? Ss 0:05.16 /usr/sbin/cron -s
+ 9881 ?? S<s 0:06.67 /usr/bin/sndiod
+ PS_SAMPLE
+ end
+
+ it "should set running to true" do
+ allow(provider).to receive(:shell_out!).and_return(status)
+ provider.determine_current_status!
+ expect(current_resource.running).to be_true
+ end
+ end
+
+ it "should set running to false if the regex doesn't match" do
+ allow(provider).to receive(:shell_out!).and_return(status)
+ provider.determine_current_status!
+ expect(current_resource.running).to be_false
+ end
+
+ it "should set running to nil if ps fails" do
+ allow(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ provider.determine_current_status!
+ expect(current_resource.running).to be_nil
+ expect(provider.status_load_success).to be_nil
+ end
+
+ context "when ps command is nil" do
+ before do
+ node.automatic_attrs[:command] = {:ps => nil}
+ end
+
+ it "should set running to nil" do
+ pending "superclass raises no conversion of nil to string which seems broken"
+ provider.determine_current_status!
+ expect(current_resource.running).to be_nil
+ end
+ end
+
+ context "when ps is empty string" do
+ before do
+ node.automatic_attrs[:command] = {:ps => ""}
+ end
+
+ it "should set running to nil" do
+ provider.determine_current_status!
+ expect(current_resource.running).to be_nil
+ end
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "determine_enabled_status!" do
+ before do
+ stub_etc_rcd_script
+ provider.current_resource = current_resource
+ current_resource.service_name(new_resource.service_name)
+
+ allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable")
+ end
+
+ context "when the service is not builtin" do
+ before do
+ expect(::File).to receive(:exist?).with("/etc/rc.conf").and_return(false)
+ end
+ pending
+ end
+
+ context "when the service is builtin" do
+ before do
+ provider.files = {
+ '/etc/rc.conf' => "#{provider.builtin_service_enable_variable_name}=NO",
+ '/etc/rc.conf.local' => lines.join("\n")
+ }
+ end
+
+ %w{YES Yes yes yEs YeS}.each do |setting|
+ context "when the enable variable is set to #{setting}" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}="#{setting}"} ] }
+ it "sets enabled to true" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be true
+ end
+ end
+ end
+
+ %w{No NO no nO None NONE none nOnE}.each do |setting|
+ context "when the enable variable is set to #{setting}" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}="#{setting}"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+ end
+
+ context "when the enable variable is garbage" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_enable="alskdjflasdkjflakdfj"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable partial matches (left) some other service and we are disabled" do
+ let(:lines) { [
+ %Q{thing_#{provider.builtin_service_enable_variable_name}="YES"},
+ %Q{#{provider.builtin_service_enable_variable_name}="NO"},
+ ] }
+ it "sets enabled based on the exact match (false)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable partial matches (right) some other service and we are disabled" do
+ let(:lines) { [
+ %Q{#{provider.builtin_service_enable_variable_name}_thing="YES"},
+ %Q{#{provider.builtin_service_enable_variable_name}},
+ ] }
+ it "sets enabled based on the exact match (false)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable partial matches (left) some other disabled service and we are enabled" do
+ let(:lines) { [
+ %Q{thing_#{provider.builtin_service_enable_variable_name}="NO"},
+ %Q{#{provider.builtin_service_enable_variable_name}="YES"},
+ ] }
+ it "sets enabled based on the exact match (true)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be true
+ end
+ end
+
+ context "when the enable variable partial matches (right) some other disabled service and we are enabled" do
+ let(:lines) { [
+ %Q{#{provider.builtin_service_enable_variable_name}_thing="NO"},
+ %Q{#{provider.builtin_service_enable_variable_name}="YES"},
+ ] }
+ it "sets enabled based on the exact match (true)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be true
+ end
+ end
+
+ context "when the enable variable only partial matches (left) some other enabled service" do
+ let(:lines) { [ %Q{thing_#{provider.builtin_service_enable_variable_name}_enable="YES"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable only partial matches (right) some other enabled service" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_thing_enable="YES"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when nothing matches" do
+ let(:lines) { [] }
+ it "sets enabled to true" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "load_current_resource" do
+ before(:each) do
+ stub_etc_rcd_script
+ expect(provider).to receive(:determine_current_status!)
+ current_resource.running(false)
+ allow(provider).to receive(:service_enable_variable_name).and_return "#{new_resource.service_name}_enable"
+ end
+
+ it "should create a current resource with the name of the new resource" do
+ expect(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ provider.load_current_resource
+ end
+
+ it "should set the current resources service name to the new resources service name" do
+ provider.load_current_resource
+ expect(current_resource.service_name).to eq(new_resource.service_name)
+ end
+
+ it "should return the current resource" do
+ expect(provider.load_current_resource).to eql(current_resource)
+ end
+
+ end
+
+ context "when testing actions" do
+ before(:each) do
+ stub_etc_rcd_script
+ expect(provider).to receive(:determine_current_status!)
+ current_resource.running(false)
+ expect(provider).to receive(:determine_enabled_status!)
+ current_resource.enabled(false)
+ provider.load_current_resource
+ end
+
+ describe Chef::Provider::Service::Openbsd, "start_service" do
+ it "should call the start command if one is specified" do
+ new_resource.start_command("/etc/rc.d/chef startyousillysally")
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally")
+ provider.start_service()
+ end
+
+ it "should call '/usr/local/etc/rc.d/service_name faststart' if no start command is specified" do
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} start")
+ provider.start_service()
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "stop_service" do
+ it "should call the stop command if one is specified" do
+ new_resource.stop_command("/etc/init.d/chef itoldyoutostop")
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop")
+ provider.stop_service()
+ end
+
+ it "should call '/usr/local/etc/rc.d/service_name faststop' if no stop command is specified" do
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} stop")
+ provider.stop_service()
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "restart_service" do
+ it "should call 'restart' on the service_name if the resource supports it" do
+ new_resource.supports({:restart => true})
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} restart")
+ provider.restart_service()
+ end
+
+ it "should call the restart_command if one has been specified" do
+ new_resource.restart_command("/etc/init.d/chef restartinafire")
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef restartinafire")
+ provider.restart_service()
+ end
+
+ it "otherwise it should call stop and start" do
+ expect(provider).to receive(:stop_service)
+ expect(provider).to receive(:start_service)
+ provider.restart_service()
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "define_resource_requirements" do
+ before do
+ provider.current_resource = current_resource
+ end
+
+ context "when the init script is not found" do
+ before do
+ provider.init_command = nil
+ allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable")
+ end
+
+ [ "start", "reload", "restart", "enable" ].each do |action|
+ it "should raise an exception when the action is #{action}" do
+ provider.define_resource_requirements
+ provider.action = action
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
+
+ [ "stop", "disable" ].each do |action|
+ it "should not raise an error when the action is #{action}" do
+ provider.define_resource_requirements
+ provider.action = action
+ expect { provider.process_resource_requirements }.not_to raise_error
+ end
+ end
+ end
+
+ context "when the init script is found, but the service_enable_variable_name is nil" do
+ before do
+ provider.init_command = nil
+ allow(provider).to receive(:service_enable_variable_name).and_return(nil)
+ end
+
+ [ "start", "reload", "restart", "enable" ].each do |action|
+ it "should raise an exception when the action is #{action}" do
+ provider.action = action
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
+
+ [ "stop", "disable" ].each do |action|
+ it "should not raise an error when the action is #{action}" do
+ provider.action = action
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "enable_service" do
+ before do
+ provider.current_resource = current_resource
+ provider.afters = {}
+ end
+
+ it "should enable the service if it is not enabled" do
+ expect(provider).to receive(:is_builtin).and_return(true)
+ provider.files = {'/etc/rc.conf' => "#{provider.builtin_service_enable_variable_name}_flags=NO\n"}
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', "#{provider.builtin_service_enable_variable_name}=\n")
+ provider.enable_service
+ end
+
+ it "should not partial match an already enabled service" do
+ provider.files = {'/etc/rc.conf.local' => "pkg_scripts=\"#{new_resource.service_name}_thing\"\n"}
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', "pkg_scripts=\"#{new_resource.service_name} #{new_resource.service_name}_thing\"\n")
+ provider.enable_service
+ end
+
+ it "should enable the service if it is not enabled and not already specified in the rc.conf file" do
+ provider.files = {}
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', "pkg_scripts=\"#{new_resource.service_name}\"\n")
+ provider.enable_service
+ end
+
+ it "should not enable the service if it is already enabled" do
+ provider.files = {'/etc/rc.conf.local' => "pkg_scripts=\"#{new_resource.service_name}\"\n"}
+ expect(::File).not_to receive(:write)
+ provider.enable_service
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "disable_service" do
+ before do
+ provider.current_resource = current_resource
+ expect(provider).to receive(:is_builtin).and_return(true)
+ allow(::File).to receive(:exists?).with('/etc/rc.conf').and_return(true)
+ allow(::File).to receive(:exists?).with('/etc/rc.conf.local').and_return(true)
+ end
+
+ it "should disable the service if it is not disabled" do
+ provider.files = {'/etc/rc.conf.local' => "#{provider.builtin_service_enable_variable_name}=\"YES\"\n"}
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', '')
+ provider.disable_service()
+ end
+
+ it "should not disable an enabled service that partially matches" do
+ provider.files = {'/etc/rc.conf.local' => "#{provider.builtin_service_enable_variable_name}_thing=\"YES\""}
+ expect(::File).not_to receive(:write)
+ provider.disable_service()
+ end
+
+ it "should not disable the service if it is already disabled" do
+ provider.files = {'/etc/rc.conf.local' => ""}
+ expect(::File).not_to receive(:write)
+ provider.disable_service
+ end
+ end
+end