forked from chef/chef
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tests, documentation and better error handling for windows service ma…
…nager.
- Loading branch information
sersut
committed
Mar 1, 2013
1 parent
21882ab
commit 4b880bf
Showing
3 changed files
with
344 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
# | ||
# Author:: Serdar Sutay (<serdar@opscode.com>) | ||
# Copyright:: Copyright (c) 2013 Opscode, Inc. | ||
# 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' | ||
require 'chef/application/windows_service_manager' | ||
|
||
# | ||
# ATTENTION: | ||
# This test creates a windows service for testing purposes and runs it | ||
# as Local System on windows boxes. | ||
# This test will fail if you run the tests inside a Windows VM by | ||
# sharing the code from your host since Local System account by | ||
# default can't see the mounted partitions. | ||
# Run this test by copying the code to a local VM directory or setup | ||
# Local System account to see the maunted partitions for the shared | ||
# directories. | ||
# | ||
|
||
describe "Chef::Application::WindowsServiceManager", :windows_only do | ||
|
||
# Definition for the test-service | ||
|
||
let(:test_service) { | ||
{ | ||
:service_name => "spec-service", | ||
:service_display_name => "Spec Test Service", | ||
:service_description => "Service for testing Chef::Application::WindowsServiceManager.", | ||
:service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../support/lib/spec_service.rb')) | ||
} | ||
} | ||
|
||
# Test service creates a file for us to verify that it is running. | ||
# Since our test service is running as Local System we should look | ||
# for the file it creates under SYSTEM temp directory | ||
|
||
let(:test_service_file) { | ||
"#{ENV['SystemDrive']}\\windows\\temp\\spec_service_file" | ||
} | ||
|
||
context "with invalid service definition" do | ||
it "throws an error when initialized with no service definition" do | ||
lambda { Chef::Application::WindowsServiceManager.new(nil) }.should raise_error(ArgumentError) | ||
end | ||
|
||
it "throws an error with required missing options" do | ||
test_service.each do |key,value| | ||
service_def = test_service.dup | ||
service_def.delete(key) | ||
|
||
lambda { Chef::Application::WindowsServiceManager.new(service_def) }.should raise_error(ArgumentError) | ||
end | ||
end | ||
end | ||
|
||
context "with valid definition" do | ||
before(:each) do | ||
@service_manager_output = [ ] | ||
# Uncomment below lines to debug this test | ||
# original_puts = $stdout.method(:puts) | ||
$stdout.stub(:puts) do |message| | ||
@service_manager_output << message | ||
# original_puts.call(message) | ||
end | ||
end | ||
|
||
after(:each) do | ||
cleanup | ||
end | ||
|
||
context "when service doesn't exist" do | ||
it "default => should say service don't exist" do | ||
service_manager.run | ||
|
||
@service_manager_output.grep(/doesn't exist on the system/).length.should > 0 | ||
end | ||
|
||
it "install => should install the service" do | ||
service_manager.run(["-a", "install"]) | ||
|
||
test_service_exists?.should be_true | ||
end | ||
|
||
it "other actions => should say service doesn't exist" do | ||
["delete", "start", "stop", "pause", "resume", "uninstall"].each do |action| | ||
service_manager.run(["-a", action]) | ||
@service_manager_output.grep(/doesn't exist on the system/).length.should > 0 | ||
@service_manager_output = [ ] | ||
end | ||
end | ||
end | ||
|
||
context "when service exists" do | ||
before(:each) do | ||
service_manager.run(["-a", "install"]) | ||
end | ||
|
||
it "install => should say service already exists" do | ||
service_manager.run(["-a", "install"]) | ||
@service_manager_output.grep(/already exists/).length.should > 0 | ||
end | ||
|
||
context "and service is stopped" do | ||
["delete", "uninstall"].each do |action| | ||
it "#{action} => should remove the service" do | ||
service_manager.run(["-a", action]) | ||
test_service_exists?.should be_false | ||
end | ||
end | ||
|
||
it "default, status => should say service is stopped" do | ||
service_manager.run([ ]) | ||
@service_manager_output.grep(/stopped/).length.should > 0 | ||
@service_manager_output = [ ] | ||
|
||
service_manager.run(["-a", "status"]) | ||
@service_manager_output.grep(/stopped/).length.should > 0 | ||
end | ||
|
||
it "start should start the service" do | ||
service_manager.run(["-a", "start"]) | ||
test_service_state.should == "running" | ||
File.exists?(test_service_file).should be_true | ||
end | ||
|
||
it "stop should not affect the service" do | ||
service_manager.run(["-a", "stop"]) | ||
test_service_state.should == "stopped" | ||
end | ||
|
||
|
||
["pause", "resume"].each do |action| | ||
it "#{action} => should raise error" do | ||
lambda {service_manager.run(["-a", action])}.should raise_error(::Win32::Service::Error) | ||
end | ||
end | ||
|
||
context "and service is started" do | ||
before(:each) do | ||
service_manager.run(["-a", "start"]) | ||
end | ||
|
||
["delete", "uninstall"].each do |action| | ||
it "#{action} => should remove the service" do | ||
service_manager.run(["-a", action]) | ||
test_service_exists?.should be_false | ||
end | ||
end | ||
|
||
it "default, status => should say service is running" do | ||
service_manager.run([ ]) | ||
@service_manager_output.grep(/running/).length.should > 0 | ||
@service_manager_output = [ ] | ||
|
||
service_manager.run(["-a", "status"]) | ||
@service_manager_output.grep(/running/).length.should > 0 | ||
end | ||
|
||
it "stop should stop the service" do | ||
service_manager.run(["-a", "stop"]) | ||
test_service_state.should == "stopped" | ||
end | ||
|
||
it "pause should pause the service" do | ||
service_manager.run(["-a", "pause"]) | ||
test_service_state.should == "paused" | ||
end | ||
|
||
it "resume should have no affect" do | ||
service_manager.run(["-a", "resume"]) | ||
test_service_state.should == "running" | ||
end | ||
end | ||
|
||
context "and service is paused" do | ||
before(:each) do | ||
service_manager.run(["-a", "start"]) | ||
service_manager.run(["-a", "pause"]) | ||
end | ||
|
||
actions = ["delete", "uninstall"] | ||
actions.each do |action| | ||
it "#{action} => should remove the service" do | ||
service_manager.run(["-a", action]) | ||
test_service_exists?.should be_false | ||
end | ||
end | ||
|
||
it "default, status => should say service is paused" do | ||
service_manager.run([ ]) | ||
@service_manager_output.grep(/paused/).length.should > 0 | ||
@service_manager_output = [ ] | ||
|
||
service_manager.run(["-a", "status"]) | ||
@service_manager_output.grep(/paused/).length.should > 0 | ||
end | ||
|
||
it "stop should stop the service" do | ||
service_manager.run(["-a", "stop"]) | ||
test_service_state.should == "stopped" | ||
end | ||
|
||
it "pause should not affect the service" do | ||
service_manager.run(["-a", "pause"]) | ||
test_service_state.should == "paused" | ||
end | ||
|
||
it "start should raise an error" do | ||
lambda {service_manager.run(["-a", "start"])}.should raise_error(::Win32::Service::Error) | ||
end | ||
|
||
end | ||
end | ||
end | ||
end | ||
|
||
def test_service_exists? | ||
::Win32::Service.exists?("spec-service") | ||
end | ||
|
||
def test_service_state | ||
::Win32::Service.status("spec-service").current_state | ||
end | ||
|
||
def service_manager | ||
Chef::Application::WindowsServiceManager.new(test_service) | ||
end | ||
|
||
def cleanup | ||
# Uninstall if the test service is installed. | ||
if test_service_exists? | ||
|
||
# We can only uninstall when the service is stopped. | ||
if test_service_state != "stopped" | ||
::Win32::Service.send("stop", "spec-service") | ||
while test_service_state != "stopped" | ||
sleep 1 | ||
end | ||
end | ||
|
||
::Win32::Service.delete("spec-service") | ||
end | ||
|
||
# Delete the test_service_file if it exists | ||
if File.exists?(test_service_file) | ||
File.delete(test_service_file) | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# | ||
# Author:: Serdar Sutay (<serdar@lambda.local>) | ||
# Copyright:: Copyright (c) 2013 Opscode, Inc. | ||
# 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 'win32/daemon' | ||
|
||
class SpecService < ::Win32::Daemon | ||
def service_init | ||
@test_service_file = "#{ENV['TMP']}/spec_service_file" | ||
end | ||
|
||
def service_main(*startup_parameters) | ||
while running? do | ||
if !File.exists?(@test_service_file) | ||
File.open(@test_service_file, 'wb') do |f| | ||
f.write("This file is created by SpecService") | ||
end | ||
end | ||
|
||
sleep 1 | ||
end | ||
end | ||
|
||
################################################################################ | ||
# Control Signal Callback Methods | ||
################################################################################ | ||
|
||
def service_stop | ||
end | ||
|
||
def service_pause | ||
end | ||
|
||
def service_resume | ||
end | ||
|
||
def service_shutdown | ||
end | ||
end | ||
|
||
# To run this file as a service, it must be called as a script from within | ||
# the Windows Service framework. In that case, kick off the main loop! | ||
if __FILE__ == $0 | ||
SpecService.mainloop | ||
end |