summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/chef-service-manager31
-rw-r--r--chef.gemspec6
-rw-r--r--lib/chef/application/windows_service_manager.rb165
3 files changed, 201 insertions, 1 deletions
diff --git a/bin/chef-service-manager b/bin/chef-service-manager
new file mode 100755
index 0000000000..c7f2fa43a9
--- /dev/null
+++ b/bin/chef-service-manager
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# ./chef-service-manager - Control chef-service on Windows platforms.
+#
+# 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 'rubygems'
+$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
+require 'chef'
+require 'chef/application/windows_service_manager'
+
+if Chef::Platform.windows?
+ Chef::Application::WindowsServiceManager.new.run
+else
+ puts "chef-service-manager is only available on Windows platforms."
+end
+
diff --git a/chef.gemspec b/chef.gemspec
index 70b6141812..4e2f57af44 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -34,7 +34,11 @@ Gem::Specification.new do |s|
%w(rspec-core rspec-expectations rspec-mocks).each { |gem| s.add_development_dependency gem, "~> 2.12.0" }
s.bindir = "bin"
- s.executables = %w( chef-client chef-solo knife chef-shell shef chef-apply )
+ # chef-service-manager is a windows only executable.
+ # However gemspec doesn't give us a way to have this executable only
+ # on windows. So we're including this in all platforms.
+ s.executables = %w( chef-client chef-solo knife chef-shell shef chef-apply chef-service-manager )
+
s.require_path = 'lib'
s.files = %w(Rakefile LICENSE README.md CONTRIBUTING.md) + Dir.glob("{distro,lib,tasks,spec}/**/*")
end
diff --git a/lib/chef/application/windows_service_manager.rb b/lib/chef/application/windows_service_manager.rb
new file mode 100644
index 0000000000..ef93e88517
--- /dev/null
+++ b/lib/chef/application/windows_service_manager.rb
@@ -0,0 +1,165 @@
+#
+# Author:: Seth Chisamore (<schisamo@opscode.com>)
+# Copyright:: Copyright (c) 2011 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/service'
+require 'chef/config'
+require 'mixlib/cli'
+
+class Chef
+ class Application
+ class WindowsServiceManager
+ include Mixlib::CLI
+
+ option :action,
+ :short => "-a ACTION",
+ :long => "--action ACTION",
+ :default => "start",
+ :description => "Action to carry out on chef-service (install, uninstall, status, start, stop, pause, or resume)"
+
+ option :config_file,
+ :short => "-c CONFIG",
+ :long => "--config CONFIG",
+ :default => "#{ENV['SYSTEMDRIVE']}/chef/client.rb",
+ :description => "The configuration file to use for chef runs"
+
+ option :log_location,
+ :short => "-L LOGLOCATION",
+ :long => "--logfile LOGLOCATION",
+ :description => "Set the log file location for chef-service",
+ :default => "#{ENV['SYSTEMDRIVE']}/chef/client.log"
+
+ option :splay,
+ :short => "-s SECONDS",
+ :long => "--splay SECONDS",
+ :description => "The splay time for running at intervals, in seconds",
+ :proc => lambda { |s| s.to_i }
+
+ option :interval,
+ :short => "-i SECONDS",
+ :long => "--interval SECONDS",
+ :description => "Set the number of seconds to wait between chef-client runs",
+ :proc => lambda { |s| s.to_i }
+
+ option :help,
+ :short => "-h",
+ :long => "--help",
+ :description => "Show this message",
+ :on => :tail,
+ :boolean => true,
+ :show_options => true,
+ :exit => 0
+
+ CHEF_SERVICE_NAME = "chef-client"
+ CHEF_SERVICE_DISPLAY_NAME = "Chef-Client Service"
+ CHEF_SERVICE_DESCRIPTION = "Runs chef-client periodically"
+
+ def run
+ parse_options
+
+ case config[:action]
+ when 'install'
+ if service_exists?
+ puts "Service #{CHEF_SERVICE_NAME} already exists on the system."
+ else
+ ruby = File.join(RbConfig::CONFIG['bindir'], 'ruby')
+ path = File.expand_path(File.join(File.dirname(__FILE__), 'windows_service.rb'))
+
+ opts = ""
+ opts << " -c #{config[:config_file]}" if config[:config_file]
+ opts << " -L #{config[:log_location]}" if config[:log_location]
+ opts << " -i #{config[:interval]}" if config[:interval]
+ opts << " -s #{config[:splay]}" if config[:splay]
+
+ # Quote the full paths to deal with possible spaces in the path name.
+ # Also ensure all forward slashes are backslashes
+ cmd = "\"#{ruby}\" \"#{path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR)
+
+ ::Win32::Service.new(
+ :service_name => CHEF_SERVICE_NAME,
+ :display_name => CHEF_SERVICE_DISPLAY_NAME,
+ :description => CHEF_SERVICE_DESCRIPTION,
+ :start_type => ::Win32::Service::SERVICE_AUTO_START,
+ :binary_path_name => cmd)
+ puts "Service '#{CHEF_SERVICE_NAME}' has successfully been installed."
+ end
+ when 'status'
+ if !service_exists?
+ puts "Service #{CHEF_SERVICE_NAME} doesn't exist on the system."
+ else
+ puts "State of #{CHEF_SERVICE_NAME} service is: #{current_state}"
+ end
+ when 'start'
+ # TODO: allow override of startup parameters here?
+ take_action('start', RUNNING)
+ when 'stop'
+ take_action('stop', STOPPED)
+ when 'uninstall', 'delete'
+ take_action('stop', STOPPED)
+ unless service_exists?
+ puts "Service #{CHEF_SERVICE_NAME} doesn't exist on the system."
+ else
+ ::Win32::Service.delete(CHEF_SERVICE_NAME)
+ puts "Service #{CHEF_SERVICE_NAME} deleted"
+ end
+ when 'pause'
+ take_action('pause', PAUSED)
+ when 'resume'
+ take_action('resume', RUNNING)
+ end
+ end
+
+ private
+
+ # Just some state constants
+ STOPPED = "stopped"
+ RUNNING = "running"
+ PAUSED = "paused"
+
+ def service_exists?
+ return ::Win32::Service.exists?(CHEF_SERVICE_NAME)
+ end
+
+ def take_action(action=nil, desired_state=nil)
+ if service_exists?
+ if current_state != desired_state
+ ::Win32::Service.send(action, CHEF_SERVICE_NAME)
+ wait_for_state(desired_state)
+ puts "Service '#{CHEF_SERVICE_NAME}' is now '#{current_state}'."
+ else
+ puts "Service '#{CHEF_SERVICE_NAME}' is already '#{desired_state}'."
+ end
+ else
+ puts "Cannot '#{action}' service '#{CHEF_SERVICE_NAME}', service does not exist."
+ end
+ end
+
+ def current_state
+ ::Win32::Service.status(CHEF_SERVICE_NAME).current_state
+ end
+
+ # Helper method that waits for a status to change its state since state
+ # changes aren't usually instantaneous.
+ def wait_for_state(desired_state)
+ while current_state != desired_state
+ puts "One moment... #{current_state}"
+ sleep 1
+ end
+ end
+ end
+ end
+end