Skip to content

Commit 9470d29

Browse files
committed
Merge branch 'CHEF-1164'
2 parents b64ca97 + 870b560 commit 9470d29

6 files changed

Lines changed: 305 additions & 8 deletions

File tree

chef/lib/chef/platform.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@
3535
class Chef
3636
class Platform
3737

38-
3938
class << self
4039
attr_writer :platforms
41-
40+
4241
def platforms
4342
@platforms ||= {
4443
:mac_os_x => {
@@ -128,6 +127,21 @@ def platforms
128127
:mdadm => Chef::Provider::Mdadm
129128
}
130129
},
130+
:mswin => {
131+
:default => {
132+
:service => Chef::Provider::Service::Windows
133+
}
134+
},
135+
:mingw32 => {
136+
:default => {
137+
:service => Chef::Provider::Service::Windows
138+
}
139+
},
140+
:windows => {
141+
:default => {
142+
:service => Chef::Provider::Service::Windows
143+
}
144+
},
131145
:solaris => {},
132146
:default => {
133147
:file => Chef::Provider::File,
@@ -284,8 +298,8 @@ def set(args)
284298

285299
def find_provider(platform, version, resource_type)
286300
pmap = Chef::Platform.find(platform, version)
287-
provider_klass = explicit_provider(platform, version, resource_type) ||
288-
platform_provider(platform, version, resource_type) ||
301+
provider_klass = explicit_provider(platform, version, resource_type) ||
302+
platform_provider(platform, version, resource_type) ||
289303
resource_matching_provider(platform, version, resource_type)
290304

291305
raise ArgumentError, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
@@ -308,8 +322,8 @@ def platform_provider(platform, version, resource_type)
308322
def resource_matching_provider(platform, version, resource_type)
309323
if resource_type.kind_of?(Chef::Resource)
310324
begin
311-
Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
312-
rescue NameError
325+
Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
326+
rescue NameError
313327
nil
314328
end
315329
else
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#
2+
# Author:: Nuo Yan <nuo@opscode.com>
3+
# Copyright:: Copyright (c) 2010 Opscode, Inc
4+
# License:: Apache License, Version 2.0
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
require 'chef/provider/service/init'
20+
21+
class Chef::Provider::Service::Windows < Chef::Provider::Service::Init
22+
23+
def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
24+
super(node, new_resource, collection, definitions, cookbook_loader)
25+
@init_command = "sc"
26+
end
27+
28+
def load_current_resource
29+
@current_resource = Chef::Resource::Service.new(@new_resource.name)
30+
@current_resource.service_name(@new_resource.service_name)
31+
status = IO.popen("#{@init_command} query #{@new_resource.service_name}").entries
32+
raise Chef::Exceptions::Exec, "Service #{@new_resource.service_name} does not exist.\n#{status.join}\n" if status[0].include?("FAILED 1060")
33+
34+
begin
35+
started = status[3].include?("4")
36+
@current_resource.running started
37+
38+
start_type = IO.popen("#{@init_command} qc #{@new_resource.service_name}").entries[4]
39+
@current_resource.enabled(start_type.include?('2') || start_type.include?('3') ? true : false)
40+
41+
Chef::Log.debug "#{@new_resource}: running: #{@current_resource.running}"
42+
rescue StandardError
43+
raise Chef::Exceptions::Exec
44+
rescue Chef::Exceptions::Exec
45+
Chef::Log.debug "Failed to determine the current status of the service, assuming it is not running"
46+
@current_resource.running false
47+
nil
48+
end
49+
@current_resource
50+
end
51+
52+
def start_service
53+
begin
54+
result = if @new_resource.start_command
55+
Chef::Log.debug "starting service using the given start_command"
56+
IO.popen(@new_resource.start_command).readlines
57+
else
58+
IO.popen("#{@init_command} start #{@new_resource.service_name}").readlines
59+
end
60+
Chef::Log.debug result.join
61+
result[3].include?('4') || result.include?('2') ? true : false
62+
rescue
63+
Chef::Log.debug "Failed to start service #{@new_resource.service_name}"
64+
false
65+
end
66+
end
67+
68+
def stop_service
69+
begin
70+
Chef::Log.debug "stopping service using the given stop_command"
71+
result = if @new_resource.stop_command
72+
IO.popen(@new_resource.stop_command).readlines
73+
else
74+
IO.popen("#{@init_command} stop #{@new_resource.service_name}").readlines
75+
end
76+
Chef::Log.debug result.join
77+
result[3].include?('1')
78+
rescue
79+
Chef::Log.debug "Failed to stop service #{@new_resource.service_name}"
80+
false
81+
end
82+
end
83+
84+
def restart_service
85+
begin
86+
if @new_resource.restart_command
87+
Chef::Log.debug "restarting service using the given restart_command"
88+
result = IO.popen(@new_resource.restart_command).readlines
89+
Chef::Log.debug result.join
90+
else
91+
Chef::Log.debug IO.popen("#{@init_command} stop #{@new_resource.service_name}").readlines.join
92+
sleep 1
93+
result = IO.popen("#{@init_command} start #{@new_resource.service_name}").readlines
94+
Chef::Log.debug result.join
95+
end
96+
result[3].include?('4') || result.include?('2')
97+
rescue
98+
Chef::Log.debug "Failed to restart service #{@new_resource.service_name}"
99+
false
100+
end
101+
end
102+
103+
def enable_service()
104+
begin
105+
Chef::Log.debug result = IO.popen("#{@init_command} config #{@new_resource.service_name} start= #{determine_startup_type}").readlines.join
106+
result.include?('SUCCESS')
107+
rescue
108+
Chef::Log.debug "Failed to enable service #{@new_resource.service_name}"
109+
false
110+
end
111+
end
112+
113+
def disable_service()
114+
begin
115+
Chef::Log.debug result = IO.popen("#{@init_command} config #{@new_resource.service_name} start= disabled").readlines.join
116+
result.include?('SUCCESS')
117+
rescue
118+
Chef::Log.debug "Failed to disable service #{@new_resource.service_name}"
119+
false
120+
end
121+
end
122+
123+
private
124+
125+
def determine_startup_type
126+
{:automatic => 'auto', :mannual => 'demand'}[@new_resource.startup_type]
127+
end
128+
129+
end

chef/lib/chef/providers.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
require 'chef/provider/service/redhat'
6363
require 'chef/provider/service/simple'
6464
require 'chef/provider/service/upstart'
65+
require 'chef/provider/service/windows'
6566

6667
require 'chef/provider/user/dscl'
6768
require 'chef/provider/user/pw'

chef/lib/chef/resource/service.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def initialize(name, collection=nil, node=nil)
3535
@restart_command = nil
3636
@reload_command = nil
3737
@action = "nothing"
38+
@startup_type = :automatic
3839
@supports = { :restart => false, :reload => false, :status => false }
3940
@allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload)
4041
end
@@ -127,7 +128,15 @@ def supports(args={})
127128
@supports
128129
end
129130
end
130-
131+
132+
# This attribute applies for Windows only.
133+
def startup_type(arg=nil)
134+
set_or_return(
135+
:startup_type,
136+
arg,
137+
:equal_to => [:automatic, :mannual]
138+
)
139+
end
131140

132141
end
133142
end

chef/spec/unit/platform_spec.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
:redhat,
3131
:gentoo,
3232
:arch,
33-
:solaris
33+
:solaris,
34+
:mswin,
35+
:mingw32,
36+
:windows
3437
].each do |platform|
3538
it "#{platform}" do
3639
Chef::Platform.platforms.should have_key(platform)
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#
2+
# Author:: Nuo Yan <nuo@opscode.com>
3+
# Copyright:: Copyright (c) 2010, Opscode, Inc
4+
# License:: Apache License, Version 2.0
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
20+
21+
describe Chef::Provider::Service::Windows, "load_current_resource" do
22+
before(:each) do
23+
@init_command = "sc"
24+
@node = mock("Chef::Node", :null_object => true)
25+
@new_resource = Chef::Resource::Service.new("chef")
26+
@new_resource.stub!(:pattern).and_return("chef")
27+
@new_resource.stub!(:status_command).and_return(false)
28+
29+
@current_resource = Chef::Resource::Service.new("chef")
30+
@provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
31+
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
32+
IO.stub!(:popen).with("#{@init_command} query #{@new_resource.service_name}").and_return(['','','','4'])
33+
IO.stub!(:popen).with("#{@init_command} qc #{@new_resource.service_name}").and_return(['','','','','2'])
34+
end
35+
36+
it "should create a current resource with the name of the new resource" do
37+
Chef::Resource::Service.should_receive(:new).and_return(@current_resource)
38+
@provider.load_current_resource
39+
end
40+
41+
it "should set the current resources service name to the new resources service name" do
42+
@current_resource.should_receive(:service_name).with(@new_resource.service_name)
43+
@provider.load_current_resource
44+
end
45+
46+
it "should return the current resource" do
47+
@provider.load_current_resource.should eql(@current_resource)
48+
end
49+
50+
end
51+
52+
describe Chef::Provider::Service::Windows, "start_service" do
53+
before(:each) do
54+
@new_resource = Chef::Resource::Service.new("chef")
55+
@new_resource.start_command "sc start chef"
56+
57+
@provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
58+
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
59+
end
60+
61+
it "should call the start command if one is specified" do
62+
@new_resource.stub!(:start_command).and_return("#{@new_resource.start_command}")
63+
IO.should_receive(:popen).with("#{@new_resource.start_command}").and_return(StringIO.new("foo\nbar\nbaz\n2 START_PENDING\n"))
64+
@provider.start_service()
65+
end
66+
end
67+
68+
describe Chef::Provider::Service::Windows, "stop_service" do
69+
before(:each) do
70+
@init_command = "sc"
71+
@new_resource = Chef::Resource::Service.new("chef")
72+
@new_resource.stop_command "sc stop chef"
73+
74+
@provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
75+
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
76+
end
77+
78+
it "should call the stop command if one is specified" do
79+
@new_resource.stub!(:stop_command).and_return("#{@new_resource.stop_command}")
80+
IO.should_receive(:popen).with("#{@new_resource.stop_command}").and_return(StringIO.new("foo\nbar\nbaz\n1 STOPPED\n"))
81+
@provider.stop_service().should be_true
82+
end
83+
84+
it "should use the built-in command if no stop command is specified" do
85+
@new_resource.stub!(:stop_command).and_return(nil)
86+
IO.stub!(:popen).with("#{@init_command} stop #{@new_resource.service_name}").and_return(IO.new(2,'w'))
87+
IO.popen("#{@init_command} stop #{@new_resource.service_name}").stub!(:readlines).and_return(["foo\n","bar\n","baz\n","1 STOPPED\n"])
88+
@provider.stop_service().should be_true
89+
end
90+
end
91+
92+
describe Chef::Provider::Service::Windows, "restart_service" do
93+
before(:each) do
94+
@init_command = "sc"
95+
@new_resource = Chef::Resource::Service.new("chef")
96+
97+
@provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
98+
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
99+
end
100+
101+
it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do
102+
IO.should_receive(:popen).with("#{@init_command} stop #{@new_resource.service_name}").and_return(StringIO.new("foo\nbar\nbaz\n1 STOPPED\n"))
103+
IO.should_receive(:popen).with("#{@init_command} start #{@new_resource.service_name}").and_return(StringIO.new("foo\nbar\nbaz\n2 START_PENDING\n"))
104+
@provider.restart_service()
105+
end
106+
end
107+
108+
describe Chef::Provider::Service::Windows, "enable_service" do
109+
before(:each) do
110+
@init_command = "sc"
111+
@node = Chef::Node.new
112+
@new_resource = Chef::Resource::Service.new("chef")
113+
@new_resource.running false
114+
@new_resource.enabled false
115+
116+
@provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
117+
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
118+
end
119+
120+
it "should enable service and set the startup type" do
121+
@new_resource.startup_type :automatic
122+
IO.should_receive(:popen).with("sc config chef start= auto").and_return(StringIO.new("SUCCESS"))
123+
@provider.enable_service().should be_true
124+
end
125+
end
126+
127+
describe Chef::Provider::Service::Windows, "disable_service" do
128+
before(:each) do
129+
@node = Chef::Node.new
130+
@new_resource = Chef::Resource::Service.new("chef")
131+
132+
@provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
133+
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
134+
end
135+
136+
it "should disable service" do
137+
IO.should_receive(:popen).with("sc config chef start= disabled").and_return(StringIO.new("SUCCESS"))
138+
@provider.disable_service().should be_true
139+
end
140+
end
141+

0 commit comments

Comments
 (0)