summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith84@gmail.com>2021-01-22 19:54:12 -0800
committerTim Smith <tsmith84@gmail.com>2021-02-03 16:09:37 -0800
commit7677a88c72f9097f8cc5455e2a16e19bf2bf81a7 (patch)
tree5fc6825df88fd17416825158a3e77610dfa33b7b
parentb14a3e76165e243fcabe61db60be80f8c13c36a1 (diff)
downloadchef-7677a88c72f9097f8cc5455e2a16e19bf2bf81a7.tar.gz
Chef 17: Remove windows service manager capabilities
This is something we've recommended against using for many years now. Windows users should instead run Chef Infra Client using a Scheduled Task which is far more reliable and has the added benefit of requiring zero CPU or memory while not running. Signed-off-by: Tim Smith <tsmith@chef.io>
-rwxr-xr-xchef-bin/bin/chef-service-manager37
-rwxr-xr-xchef-bin/bin/chef-windows-service33
-rw-r--r--lib/chef/application/windows_service.rb338
-rw-r--r--lib/chef/application/windows_service_manager.rb205
-rw-r--r--spec/functional/resource/windows_service_spec.rb105
-rw-r--r--spec/functional/win32/service_manager_spec.rb220
-rw-r--r--spec/spec_helper.rb5
-rw-r--r--spec/support/chef_helpers.rb16
-rw-r--r--spec/support/shared/functional/execute_resource.rb4
-rw-r--r--spec/support/shared/functional/win32_service.rb57
-rw-r--r--spec/support/shared/functional/windows_script.rb4
-rw-r--r--spec/unit/windows_service_spec.rb118
12 files changed, 6 insertions, 1136 deletions
diff --git a/chef-bin/bin/chef-service-manager b/chef-bin/bin/chef-service-manager
index dcaae80141..336b1c4715 100755
--- a/chef-bin/bin/chef-service-manager
+++ b/chef-bin/bin/chef-service-manager
@@ -1,38 +1,3 @@
#!/usr/bin/env ruby
#
-# ./chef-service-manager - Control chef-service on Windows platforms.
-#
-# Author:: Serdar Sutay (serdar@chef.io)
-# Copyright:: Copyright 2013-2018, Chef Software 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.
-
-$:.unshift(File.join(__dir__, "..", "lib"))
-require "chef"
-require "chef/application/windows_service_manager"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-if Chef::Platform.windows?
- chef_client_service = {
- service_name: ChefUtils::Dist::Infra::CLIENT,
- service_display_name: "#{ChefUtils::Dist::Infra::PRODUCT} Service",
- service_description: "Runs #{ChefUtils::Dist::Infra::PRODUCT} on regular, configurable intervals.",
- service_file_path: File.expand_path("../chef-windows-service", $PROGRAM_NAME),
- delayed_start: true,
- dependencies: ["Winmgmt"],
- }
- Chef::Application::WindowsServiceManager.new(chef_client_service).run
-else
- puts "chef-service-manager is only available on Windows platforms."
-end
+puts "As of Chef Infra Client 17 the chef-service-manager is no longer available. Please run Chef Infra Client as a scheduled task instead for more reliable operation with lower memory and CPU overhead."
diff --git a/chef-bin/bin/chef-windows-service b/chef-bin/bin/chef-windows-service
index ce1a30baae..e84cbe2e7a 100755
--- a/chef-bin/bin/chef-windows-service
+++ b/chef-bin/bin/chef-windows-service
@@ -1,34 +1,3 @@
#!/usr/bin/env ruby
#
-# Author:: Jay Mundrawala (<jdm@chef.io>)
-#
-# Copyright:: 2014-2018, Chef Software, Inc.
-#
-# 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.
-#
-
-# Note:
-# The file is used by appbundler to generate a ruby file that
-# we can execute using the correct gems. The batch file we
-# generate will call that file, and will be registered as
-# a windows service.
-
-$:.unshift(File.join(__dir__, "..", "lib"))
-require "chef"
-require "chef/application/windows_service"
-
-if Chef::Platform.windows?
- Chef::Application::WindowsService.mainloop
-else
- puts "chef-windows-service is only available on Windows platforms."
-end
+puts "As of Chef Infra Client 17 the chef-windows-service is no longer available. Please run Chef Infra Client as a scheduled task instead for more reliable operation with lower memory and CPU overhead."
diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb
deleted file mode 100644
index 8975556f75..0000000000
--- a/lib/chef/application/windows_service.rb
+++ /dev/null
@@ -1,338 +0,0 @@
-#
-# Author:: Christopher Maier (<maier@lambda.local>)
-# Copyright:: Copyright (c) Chef Software 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_relative "../../chef"
-require_relative "../monologger"
-require_relative "../application"
-require_relative "../client"
-require_relative "../config"
-require_relative "../handler/error_report"
-require_relative "../log"
-require_relative "../http"
-require "mixlib/cli" unless defined?(Mixlib::CLI)
-require "socket" unless defined?(Socket)
-require "uri" unless defined?(URI)
-require "win32/daemon"
-require_relative "../mixin/shell_out"
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Application
- class WindowsService < ::Win32::Daemon
- include Mixlib::CLI
- include Chef::Mixin::ShellOut
-
- option :config_file,
- short: "-c CONFIG",
- long: "--config CONFIG",
- default: "#{Chef::Config.etc_chef_dir}/client.rb",
- description: "The configuration file to use for #{ChefUtils::Dist::Infra::PRODUCT} runs."
-
- option :log_location,
- short: "-L LOGLOCATION",
- long: "--logfile LOGLOCATION",
- description: "Set the log file location."
-
- 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 #{ChefUtils::Dist::Infra::PRODUCT} runs.",
- proc: lambda { |s| s.to_i }
-
- DEFAULT_LOG_LOCATION ||= "#{Chef::Config.c_chef_dir}/client.log".freeze
-
- def service_init
- @service_action_mutex = Mutex.new
- @service_signal = ConditionVariable.new
-
- reconfigure
- Chef::Log.info("#{ChefUtils::Dist::Infra::CLIENT} Service initialized")
- end
-
- def service_main(*startup_parameters)
- # Chef::Config is initialized during service_init
- # Set the initial timeout to splay sleep time
- timeout = rand Chef::Config[:splay]
-
- while running?
- # Grab the service_action_mutex to make a chef-client run
- @service_action_mutex.synchronize do
-
- Chef::Log.info("Next #{ChefUtils::Dist::Infra::CLIENT} run will happen in #{timeout} seconds")
- @service_signal.wait(@service_action_mutex, timeout)
-
- # Continue only if service is RUNNING
- next if state != RUNNING
-
- # Reconfigure each time through to pick up any changes in the client file
- Chef::Log.info("Reconfiguring with startup parameters")
- reconfigure(startup_parameters)
- timeout = Chef::Config[:interval]
-
- # Honor splay sleep config
- timeout += rand Chef::Config[:splay]
-
- # run chef-client only if service is in RUNNING state
- next if state != RUNNING
-
- Chef::Log.info("#{ChefUtils::Dist::Infra::CLIENT} service is starting a #{ChefUtils::Dist::Infra::CLIENT} run...")
- run_chef_client
- rescue SystemExit => e
- # Do not raise any of the errors here in order to
- # prevent service crash
- Chef::Log.error("#{e.class}: #{e}")
- rescue Exception => e
- Chef::Log.error("#{e.class}: #{e}")
-
- end
- end
-
- # Daemon class needs to have all the signal callbacks return
- # before service_main returns.
- Chef::Log.trace("Giving signal callbacks some time to exit...")
- sleep 1
- Chef::Log.trace("Exiting service...")
- end
-
- ################################################################################
- # Control Signal Callback Methods
- ################################################################################
-
- def service_stop
- run_warning_displayed = false
- Chef::Log.info("STOP request from operating system.")
- loop do
- # See if a run is in flight
- if @service_action_mutex.try_lock
- # Run is not in flight. Wake up service_main to exit.
- @service_signal.signal
- @service_action_mutex.unlock
- break
- else
- unless run_warning_displayed
- Chef::Log.info("Currently a #{ChefUtils::Dist::Infra::PRODUCT} run is happening on this system.")
- Chef::Log.info("Service will stop when run is completed.")
- run_warning_displayed = true
- end
-
- Chef::Log.trace("Waiting for #{ChefUtils::Dist::Infra::PRODUCT} run...")
- sleep 1
- end
- end
- Chef::Log.info("Service is stopping....")
- end
-
- def service_pause
- Chef::Log.info("PAUSE request from operating system.")
-
- # We don't need to wake up the service_main if it's waiting
- # since this is a PAUSE signal.
-
- if @service_action_mutex.locked?
- Chef::Log.info("Currently a #{ChefUtils::Dist::Infra::PRODUCT} run is happening.")
- Chef::Log.info("Service will pause once it's completed.")
- else
- Chef::Log.info("Service is pausing....")
- end
- end
-
- def service_resume
- # We don't need to wake up the service_main if it's waiting
- # since this is a RESUME signal.
-
- Chef::Log.info("RESUME signal received from the OS.")
- Chef::Log.info("Service is resuming....")
- end
-
- def service_shutdown
- Chef::Log.info("SHUTDOWN signal received from the OS.")
-
- # Treat shutdown similar to stop.
-
- service_stop
- end
-
- ################################################################################
- # Internal Methods
- ################################################################################
-
- private
-
- # Initializes Chef::Client instance and runs it
- def run_chef_client
- # The chef client will be started in a new process. We have used shell_out to start the chef-client.
- # The log_location and config_file of the parent process is passed to the new chef-client process.
- # We need to add the --no-fork, as by default it is set to fork=true.
-
- Chef::Log.info "Starting #{ChefUtils::Dist::Infra::CLIENT} in a new process"
- # Pass config params to the new process
- config_params = " --no-fork"
- config_params += " -c #{Chef::Config[:config_file]}" unless Chef::Config[:config_file].nil?
- # log_location might be an event logger and if so we cannot pass as a command argument
- # but shed no tears! If the logger is an event logger, it must have been configured
- # as such in the config file and chef-client will use that when no arg is passed here
- config_params += " -L #{resolve_log_location}" if resolve_log_location.is_a?(String)
-
- # Starts a new process and waits till the process exits
-
- result = shell_out(
- "#{ChefUtils::Dist::Infra::CLIENT}.bat #{config_params}",
- timeout: Chef::Config[:windows_service][:watchdog_timeout],
- logger: Chef::Log
- )
- Chef::Log.trace (result.stdout).to_s
- Chef::Log.trace (result.stderr).to_s
- rescue Mixlib::ShellOut::CommandTimeout => e
- Chef::Log.error "#{ChefUtils::Dist::Infra::CLIENT} timed out\n(#{e})"
- Chef::Log.error(<<-EOF)
- Your #{ChefUtils::Dist::Infra::CLIENT} run timed out. You can increase the time #{ChefUtils::Dist::Infra::CLIENT} is given
- to complete by configuring windows_service.watchdog_timeout in your client.rb.
- EOF
- rescue Mixlib::ShellOut::ShellCommandFailed => e
- Chef::Log.warn "Not able to start #{ChefUtils::Dist::Infra::CLIENT} in new process (#{e})"
- rescue => e
- Chef::Log.error e
- ensure
- # Once process exits, we log the current process' pid
- Chef::Log.info "Child process exited (pid: #{Process.pid})"
- end
-
- def apply_config(config_file_path)
- Chef::Config.from_file(config_file_path)
- Chef::Config.merge!(config)
- end
-
- # Lifted from Chef::Application, with addition of optional startup parameters
- # for playing nicely with Windows Services
- def reconfigure(startup_parameters = [])
- configure_chef startup_parameters
- configure_logging
-
- Chef::Config[:chef_server_url] = config[:chef_server_url] if config.key? :chef_server_url
- unless Chef::Config[:exception_handlers].any? { |h| Chef::Handler::ErrorReport === h }
- Chef::Config[:exception_handlers] << Chef::Handler::ErrorReport.new
- end
-
- Chef::Config[:interval] ||= 1800
- end
-
- # Lifted from application.rb
- # See application.rb for related comments.
-
- def configure_logging
- Chef::Log.init(MonoLogger.new(resolve_log_location))
- if want_additional_logger?
- configure_stdout_logger
- end
- Chef::Log.level = resolve_log_level
- end
-
- def configure_stdout_logger
- stdout_logger = MonoLogger.new(STDOUT)
- stdout_logger.formatter = Chef::Log.logger.formatter
- Chef::Log.loggers << stdout_logger
- end
-
- # Based on config and whether or not STDOUT is a tty, should we setup a
- # secondary logger for stdout?
- def want_additional_logger?
- ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && !Chef::Config[:daemonize]
- end
-
- # Use of output formatters is assumed if `force_formatter` is set or if
- # `force_logger` is not set
- def using_output_formatter?
- Chef::Config[:force_formatter] || !Chef::Config[:force_logger]
- end
-
- def auto_log_level?
- Chef::Config[:log_level] == :auto
- end
-
- def resolve_log_location
- # STDOUT is the default log location, but makes no sense for a windows service
- Chef::Config[:log_location] == STDOUT ? DEFAULT_LOG_LOCATION : Chef::Config[:log_location]
- end
-
- # if log_level is `:auto`, convert it to :warn (when using output formatter)
- # or :info (no output formatter). See also +using_output_formatter?+
- def resolve_log_level
- if auto_log_level?
- if using_output_formatter?
- :warn
- else
- :info
- end
- else
- Chef::Config[:log_level]
- end
- end
-
- def configure_chef(startup_parameters)
- # Bit of a hack ahead:
- # It is possible to specify a service's binary_path_name with arguments, like "foo.exe -x argX".
- # It is also possible to specify startup parameters separately, either via the Services manager
- # or by using the registry (I think).
-
- # In order to accommodate all possible sources of parameterization, we first parse any command line
- # arguments. We then parse any startup parameters. This works, because Mixlib::CLI reuses its internal
- # 'config' hash; thus, anything in startup parameters will override any command line parameters that
- # might be set via the service's binary_path_name
- #
- # All these parameters then get layered on top of those from Chef::Config
-
- parse_options # Operates on ARGV by default
- parse_options startup_parameters
-
- begin
- case config[:config_file]
- when %r{^(http|https)://}
- Chef::HTTP.new("").streaming_request(config[:config_file]) { |f| apply_config(f.path) }
- else
- ::File.open(config[:config_file]) { |f| apply_config(f.path) }
- end
- rescue Errno::ENOENT
- Chef::Log.warn("*****************************************")
- Chef::Log.warn("Did not find config file: #{config[:config_file]}. Using command line options instead.")
- Chef::Log.warn("*****************************************")
-
- Chef::Config.merge!(config)
- rescue SocketError
- Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}")
- rescue Chef::Exceptions::ConfigurationError => error
- Chef::Application.fatal!("Error processing config file #{Chef::Config[:config_file]} with error #{error.message}")
- rescue Exception => error
- Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}")
- end
- end
-
- end
- 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
- Chef::Application::WindowsService.mainloop
-end
diff --git a/lib/chef/application/windows_service_manager.rb b/lib/chef/application/windows_service_manager.rb
deleted file mode 100644
index 4f0de26411..0000000000
--- a/lib/chef/application/windows_service_manager.rb
+++ /dev/null
@@ -1,205 +0,0 @@
-#
-# Author:: Seth Chisamore (<schisamo@chef.io>)
-# Copyright:: Copyright (c) Chef Software 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.
-#
-
-if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
- require "win32/service"
-end
-require_relative "../config"
-require "mixlib/cli" unless defined?(Mixlib::CLI)
-require "chef-utils/dist" unless defined?(ChefUtils::Dist)
-
-class Chef
- class Application
- #
- # This class is used to create and manage a windows service.
- # Service should be created using Daemon class from
- # win32/service gem.
- # For an example see: Chef::Application::WindowsService
- #
- # Outside programs are expected to use this class to manage
- # windows services.
- #
- class WindowsServiceManager
- include Mixlib::CLI
-
- option :action,
- short: "-a ACTION",
- long: "--action ACTION",
- default: "status",
- description: "Action to carry out on #{ChefUtils::Dist::Infra::SHORT}-service (install, uninstall, status, start, stop, pause, or resume)."
-
- option :config_file,
- short: "-c CONFIG",
- long: "--config CONFIG",
- default: "#{ChefConfig::Config.c_chef_dir}/client.rb",
- description: "The configuration file to use for #{ChefUtils::Dist::Infra::PRODUCT} runs."
-
- option :log_location,
- short: "-L LOGLOCATION",
- long: "--logfile LOGLOCATION",
- description: "Set the log file location for #{ChefUtils::Dist::Infra::SHORT}-service."
-
- option :help,
- short: "-h",
- long: "--help",
- description: "Show this help message.",
- on: :tail,
- boolean: true,
- show_options: true,
- exit: 0
-
- option :version,
- short: "-v",
- long: "--version",
- description: "Show #{ChefUtils::Dist::Infra::PRODUCT} version.",
- boolean: true,
- proc: lambda { |v| puts "#{ChefUtils::Dist::Infra::PRODUCT}: #{::Chef::VERSION}" },
- exit: 0
-
- def initialize(service_options)
- # having to call super in initialize is the most annoying
- # anti-pattern :(
- super()
-
- raise ArgumentError, "Service definition is not provided" if service_options.nil?
-
- required_options = %i{service_name service_display_name service_description service_file_path}
-
- required_options.each do |req_option|
- unless service_options.key?(req_option)
- raise ArgumentError, "Service definition doesn't contain required option #{req_option}"
- end
- end
-
- @service_name = service_options[:service_name]
- @service_display_name = service_options[:service_display_name]
- @service_description = service_options[:service_description]
- @service_file_path = service_options[:service_file_path]
- @service_start_name = service_options[:run_as_user]
- @password = service_options[:run_as_password]
- @delayed_start = service_options[:delayed_start]
- @dependencies = service_options[:dependencies]
- end
-
- def run(params = ARGV)
- parse_options(params)
-
- case config[:action]
- when "install"
- if service_exists?
- puts "Service #{@service_name} already exists on the system."
- else
- ruby = File.join(RbConfig::CONFIG["bindir"], "ruby")
-
- opts = ""
- opts << " -c #{config[:config_file]}" if config[:config_file]
- opts << " -L #{config[:log_location]}" if config[:log_location]
-
- # Quote the full paths to deal with possible spaces in the path name.
- # Also ensure all forward slashes are backslashes
- cmd = "\"#{ruby}\" \"#{@service_file_path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR)
-
- ::Win32::Service.new(
- service_name: @service_name,
- display_name: @service_display_name,
- description: @service_description,
- # Prior to 0.8.5, win32-service creates interactive services by default,
- # and we don't want that, so we need to override the service type.
- service_type: ::Win32::Service::SERVICE_WIN32_OWN_PROCESS,
- start_type: ::Win32::Service::SERVICE_AUTO_START,
- binary_path_name: cmd,
- service_start_name: @service_start_name,
- password: @password,
- dependencies: @dependencies
- )
- unless @delayed_start.nil?
- ::Win32::Service.configure(
- service_name: @service_name,
- delayed_start: @delayed_start
- )
- end
- puts "Service '#{@service_name}' has successfully been installed."
- end
- when "status"
- if !service_exists?
- puts "Service #{@service_name} doesn't exist on the system."
- else
- puts "State of #{@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 #{@service_name} doesn't exist on the system."
- else
- ::Win32::Service.delete(@service_name)
- puts "Service #{@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".freeze
- RUNNING = "running".freeze
- PAUSED = "paused".freeze
-
- def service_exists?
- ::Win32::Service.exists?(@service_name)
- end
-
- def take_action(action = nil, desired_state = nil)
- if service_exists?
- if current_state != desired_state
- ::Win32::Service.send(action, @service_name)
- wait_for_state(desired_state)
- puts "Service '#{@service_name}' is now '#{current_state}'."
- else
- puts "Service '#{@service_name}' is already '#{desired_state}'."
- end
- else
- puts "Cannot '#{action}' service '#{@service_name}'"
- puts "Service #{@service_name} doesn't exist on the system."
- end
- end
-
- def current_state
- ::Win32::Service.status(@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
diff --git a/spec/functional/resource/windows_service_spec.rb b/spec/functional/resource/windows_service_spec.rb
deleted file mode 100644
index 4c0c3acb58..0000000000
--- a/spec/functional/resource/windows_service_spec.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# Author:: Chris Doherty (<cdoherty@chef.io>)
-# Copyright:: Copyright (c) Chef Software 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"
-
-describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only do
-
- include_context "using Win32::Service"
-
- let(:username) { "service_spec_user" }
- let(:qualified_username) { "#{ENV["COMPUTERNAME"]}\\#{username}" }
- let(:password) { "1a2b3c4X!&narf" }
-
- let(:user_resource) do
- r = Chef::Resource::User::WindowsUser.new(username, run_context)
- r.username(username)
- r.password(password)
- r.comment("temp spec user")
- r
- end
-
- let(:global_service_file_path) do
- "#{ENV["WINDIR"]}\\temp\\#{File.basename(test_service[:service_file_path])}"
- end
-
- let(:service_params) do
-
- id = "#{$$}_#{rand(1000)}"
-
- test_service.merge( {
- run_as_user: qualified_username,
- run_as_password: password,
- service_name: "spec_service_#{id}",
- service_display_name: "windows_service spec #{id}}",
- service_description: "Test service for running the windows_service functional spec.",
- service_file_path: global_service_file_path,
- } )
- end
-
- let(:manager) do
- Chef::Application::WindowsServiceManager.new(service_params)
- end
-
- let(:service_resource) do
- r = Chef::Resource::WindowsService.new(service_params[:service_name], run_context)
- %i{run_as_user run_as_password}.each { |prop| r.send(prop, service_params[prop]) }
- r
- end
-
- let(:run_context) do
- Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new)
- end
-
- before do
- user_resource.run_action(:create)
-
- # the service executable has to be outside the current user's home
- # directory in order for the logon user to execute it.
- FileUtils.copy_file(test_service[:service_file_path], global_service_file_path)
-
- # if you don't make the file executable by the service user, you'll get
- # the not-very-helpful "service did not respond fast enough" error.
-
- # #mode may break in a post-Windows 8.1 release, and have to be replaced
- # with the rights stuff in the file resource.
- file = Chef::Resource::File.new(global_service_file_path, run_context)
- file.mode("0777")
-
- file.run_action(:create)
-
- manager.run(%w{--action install})
- end
-
- after do
- user_resource.run_action(:remove)
- manager.run(%w{--action uninstall})
- File.delete(global_service_file_path)
- end
-
- describe "logon as a service" do
- it "successfully runs a service as another user" do
- service_resource.run_action(:start)
- end
-
- it "grants the user the log on as service right" do
- service_resource.run_action(:start)
- expect(Chef::ReservedNames::Win32::Security.get_account_right(qualified_username)).to include("SeServiceLogonRight")
- end
- end
-end
diff --git a/spec/functional/win32/service_manager_spec.rb b/spec/functional/win32/service_manager_spec.rb
deleted file mode 100644
index 5746283b78..0000000000
--- a/spec/functional/win32/service_manager_spec.rb
+++ /dev/null
@@ -1,220 +0,0 @@
-#
-# Author:: Serdar Sutay (<serdar@chef.io>)
-# Copyright:: Copyright (c) Chef Software 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"
-if ChefUtils.windows?
- require "chef/application/windows_service_manager"
-end
-
-#
-# ATTENTION:
-# This test creates a windows service for testing purposes and runs it
-# as Local System (or an otherwise specified user) 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, :system_windows_service_gem_only do
-
- include_context "using Win32::Service"
-
- context "with invalid service definition" do
- it "throws an error when initialized with no service definition" do
- expect { Chef::Application::WindowsServiceManager.new(nil) }.to raise_error(ArgumentError)
- end
-
- it "throws an error with required missing options" do
- %i{service_name service_display_name service_description service_file_path}.each do |key|
- service_def = test_service.dup
- service_def.delete(key)
-
- expect { Chef::Application::WindowsServiceManager.new(service_def) }.to 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)
- allow($stdout).to receive(: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
-
- expect(@service_manager_output.grep(/doesn't exist on the system/).length).to be > 0
- end
-
- it "install => should install the service" do
- service_manager.run(["-a", "install"])
-
- expect(test_service_exists?).to be_truthy
- end
-
- it "other actions => should say service doesn't exist" do
- %w{delete start stop pause resume uninstall}.each do |action|
- service_manager.run(["-a", action])
- expect(@service_manager_output.grep(/doesn't exist on the system/).length).to be > 0
- @service_manager_output = [ ]
- end
- end
- end
-
- context "when service exists" do
- before(:each) do
- service_manager.run(["-a", "install"])
- end
-
- it "should have an own-process, non-interactive type" do
- status = ::Win32::Service.status("spec-service")
- expect(status[:service_type]).to eq("own process")
- expect(status[:interactive]).to be_falsey
- end
-
- it "install => should say service already exists" do
- service_manager.run(["-a", "install"])
- expect(@service_manager_output.grep(/already exists/).length).to be > 0
- end
-
- context "and service is stopped" do
- %w{delete uninstall}.each do |action|
- it "#{action} => should remove the service", :volatile do
- service_manager.run(["-a", action])
- expect(test_service_exists?).to be_falsey
- end
- end
-
- it "default, status => should say service is stopped" do
- service_manager.run([ ])
- expect(@service_manager_output.grep(/stopped/).length).to be > 0
- @service_manager_output = [ ]
-
- service_manager.run(["-a", "status"])
- expect(@service_manager_output.grep(/stopped/).length).to be > 0
- end
-
- it "start should start the service", :volatile do
- service_manager.run(["-a", "start"])
- expect(test_service_state).to eq("running")
- expect(File.exist?(test_service_file)).to be_truthy
- end
-
- it "stop should not affect the service" do
- service_manager.run(["-a", "stop"])
- expect(test_service_state).to eq("stopped")
- end
-
- %w{pause resume}.each do |action|
- it "#{action} => should raise error" do
- expect { service_manager.run(["-a", action]) }.to raise_error(SystemCallError)
- end
- end
-
- context "and service is started", :volatile do
- before(:each) do
- service_manager.run(["-a", "start"])
- end
-
- %w{delete uninstall}.each do |action|
- it "#{action} => should remove the service", :volatile do
- service_manager.run(["-a", action])
- expect(test_service_exists?).to be_falsey
- end
- end
-
- it "default, status => should say service is running" do
- service_manager.run([ ])
- expect(@service_manager_output.grep(/running/).length).to be > 0
- @service_manager_output = [ ]
-
- service_manager.run(["-a", "status"])
- expect(@service_manager_output.grep(/running/).length).to be > 0
- end
-
- it "stop should stop the service" do
- service_manager.run(["-a", "stop"])
- expect(test_service_state).to eq("stopped")
- end
-
- it "pause should pause the service" do
- service_manager.run(["-a", "pause"])
- expect(test_service_state).to eq("paused")
- end
-
- it "resume should have no affect" do
- service_manager.run(["-a", "resume"])
- expect(test_service_state).to eq("running")
- end
- end
-
- context "and service is paused", :volatile do
- before(:each) do
- service_manager.run(["-a", "start"])
- service_manager.run(["-a", "pause"])
- end
-
- actions = %w{delete uninstall}
- actions.each do |action|
- it "#{action} => should remove the service" do
- service_manager.run(["-a", action])
- expect(test_service_exists?).to be_falsey
- end
- end
-
- it "default, status => should say service is paused" do
- service_manager.run([ ])
- expect(@service_manager_output.grep(/paused/).length).to be > 0
- @service_manager_output = [ ]
-
- service_manager.run(["-a", "status"])
- expect(@service_manager_output.grep(/paused/).length).to be > 0
- end
-
- it "stop should stop the service" do
- service_manager.run(["-a", "stop"])
- expect(test_service_state).to eq("stopped")
- end
-
- it "pause should not affect the service" do
- service_manager.run(["-a", "pause"])
- expect(test_service_state).to eq("paused")
- end
-
- it "start should raise an error" do
- expect { service_manager.run(["-a", "start"]) }.to raise_error(::Win32::Service::Error)
- end
-
- end
- end
- end
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 52f17788bb..95a4c62a77 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -159,12 +159,7 @@ RSpec.configure do |config|
config.filter_run_excluding windows_powershell_no_dsc_only: true if windows_powershell_dsc?
config.filter_run_excluding windows_domain_joined_only: true unless windows_domain_joined?
config.filter_run_excluding windows_not_domain_joined_only: true if windows_domain_joined?
- # We think this line was causing rspec tests to not run on the Jenkins windows
- # testers. If we ever fix it we should restore it.
- # config.filter_run_excluding :windows_service_requires_assign_token => true if !STDOUT.isatty && !windows_user_right?("SeAssignPrimaryTokenPrivilege")
- config.filter_run_excluding windows_service_requires_assign_token: true
config.filter_run_excluding solaris_only: true unless solaris?
- config.filter_run_excluding system_windows_service_gem_only: true unless system_windows_service_gem?
config.filter_run_excluding unix_only: true unless unix?
config.filter_run_excluding linux_only: true unless linux?
config.filter_run_excluding aix_only: true unless aix?
diff --git a/spec/support/chef_helpers.rb b/spec/support/chef_helpers.rb
index 6a0ca878cd..3738c4a41e 100644
--- a/spec/support/chef_helpers.rb
+++ b/spec/support/chef_helpers.rb
@@ -40,22 +40,6 @@ def make_tmpname(prefix_suffix, n = nil)
path << suffix
end
-# This is a helper to determine if the ruby in the PATH contains
-# win32/service gem. windows_service_manager tests create a windows
-# service that starts with the system ruby and requires this gem.
-def system_windows_service_gem?
- windows_service_gem_check_command = %{ruby -r "win32/daemon" -e ":noop" > #{File::NULL} 2>&1}
- if defined?(Bundler)
- Bundler.with_unbundled_env do
- # This returns true if the gem can be loaded
- system(windows_service_gem_check_command)
- end
- else
- # This returns true if the gem can be loaded
- system(windows_service_gem_check_command)
- end
-end
-
# This is a helper to canonicalize paths that we're using in the file
# tests.
def canonicalize_path(path)
diff --git a/spec/support/shared/functional/execute_resource.rb b/spec/support/shared/functional/execute_resource.rb
index 9d1c29dfac..3f870d0fb6 100644
--- a/spec/support/shared/functional/execute_resource.rb
+++ b/spec/support/shared/functional/execute_resource.rb
@@ -68,7 +68,7 @@ shared_context "a command that can be executed as an alternate user" do
end
shared_examples_for "an execute resource that supports alternate user identity" do
- context "when running on Windows", :windows_only, :windows_service_requires_assign_token do
+ context "when running on Windows", :windows_only do
include_context "a command that can be executed as an alternate user"
@@ -102,7 +102,7 @@ shared_examples_for "an execute resource that supports alternate user identity"
end
shared_examples_for "a resource with a guard specifying an alternate user identity" do
- context "when running on Windows", :windows_only, :windows_service_requires_assign_token do
+ context "when running on Windows", :windows_only do
include_context "alternate user identity"
let(:resource_command_property) { :command }
diff --git a/spec/support/shared/functional/win32_service.rb b/spec/support/shared/functional/win32_service.rb
deleted file mode 100644
index 890c28de2c..0000000000
--- a/spec/support/shared/functional/win32_service.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-
-require "chef/application/windows_service_manager"
-
-shared_context "using Win32::Service" do
- # Some helper methods.
-
- 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")
- sleep 1 while test_service_state != "stopped"
- end
-
- ::Win32::Service.delete("spec-service")
- end
-
- # Delete the test_service_file if it exists
- if File.exist?(test_service_file)
- File.delete(test_service_file)
- end
- end
-
- # Definition for the test-service
-
- let(:test_service) do
- {
- 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(__dir__, "../../platforms/win32/spec_service.rb")),
- delayed_start: true,
- }
- end
-
- # 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) do
- "#{ENV["SystemDrive"]}\\windows\\temp\\spec_service_file"
- end
-end
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index 151ad2387c..9f328f0b92 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -168,11 +168,11 @@ shared_context Chef::Resource::WindowsScript do
resource.run_action(:run)
end
- context "the script is executed with the identity of the current user", :windows_service_requires_assign_token do
+ context "the script is executed with the identity of the current user" do
it_behaves_like "a script that cannot be accessed by other users if they are not administrators"
end
- context "the script is executed with an alternate non-admin identity", :windows_service_requires_assign_token do
+ context "the script is executed with an alternate non-admin identity" do
include_context "alternate user identity"
before do
diff --git a/spec/unit/windows_service_spec.rb b/spec/unit/windows_service_spec.rb
deleted file mode 100644
index 02a795426d..0000000000
--- a/spec/unit/windows_service_spec.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
-# Copyright:: Copyright (c) Chef Software 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"
-if ChefUtils.windows?
- require "chef/application/windows_service"
-end
-
-describe "Chef::Application::WindowsService", :windows_only do
- let(:shell_out_result) { double("shellout", stdout: nil, stderr: nil) }
- let(:config_options) do
- {
- log_location: STDOUT,
- config_file: "test_config_file",
- log_level: :info,
- }
- end
- let(:timeout) { 7200 }
- let(:shellout_options) do
- {
- timeout: timeout,
- logger: Chef::Log,
- }
- end
-
- before do
- monologger = instance_double("MonoLogger", :level= => nil, :add => nil, :formatter= => nil, :formatter => nil)
- allow(MonoLogger).to receive(:new).and_return(monologger)
-
- Chef::Config.merge!(config_options)
- allow(subject).to receive(:configure_chef)
- allow(subject).to receive(:parse_options)
- allow(subject).to receive(:running?).and_return(true, false)
- allow(subject).to receive(:state).and_return(4)
- subject.service_init
- end
-
- subject { Chef::Application::WindowsService.new }
-
- it "passes DEFAULT_LOG_LOCATION to chef-client instead of STDOUT" do
- expect(subject).to receive(:shell_out).with(
- "chef-client.bat --no-fork -c test_config_file -L #{Chef::Application::WindowsService::DEFAULT_LOG_LOCATION}",
- shellout_options
- ).and_return(shell_out_result)
- subject.service_main
- end
-
- context "has a log location configured" do
- let(:tempfile) { Tempfile.new "log_file" }
- let(:config_options) do
- {
- log_location: tempfile.path,
- config_file: "test_config_file",
- log_level: :info,
- }
- end
-
- after do
- tempfile.unlink
- end
-
- it "uses the configured log location" do
- expect(subject).to receive(:shell_out).with(
- "chef-client.bat --no-fork -c test_config_file -L #{tempfile.path}",
- shellout_options
- ).and_return(shell_out_result)
- subject.service_main
- end
-
- context "configured to Event Logger" do
- let(:config_options) do
- {
- log_location: Chef::Log::WinEvt.new,
- config_file: "test_config_file",
- log_level: :info,
- }
- end
-
- it "does not pass log location to new process" do
- expect(subject).to receive(:shell_out).with(
- "chef-client.bat --no-fork -c test_config_file",
- shellout_options
- ).and_return(shell_out_result)
- subject.service_main
- end
- end
- end
-
- context "configures a watchdog timeout" do
- let(:timeout) { 10 }
-
- before do
- Chef::Config[:windows_service][:watchdog_timeout] = 10
- end
-
- it "passes watchdog timeout to new process" do
- expect(subject).to receive(:shell_out).with(
- "chef-client.bat --no-fork -c test_config_file -L #{Chef::Application::WindowsService::DEFAULT_LOG_LOCATION}",
- shellout_options
- ).and_return(shell_out_result)
- subject.service_main
- end
- end
-end