diff --git a/lib/jetpants.rb b/lib/jetpants.rb index 95d70f3..5a76fb1 100644 --- a/lib/jetpants.rb +++ b/lib/jetpants.rb @@ -9,7 +9,7 @@ module Jetpants; end $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'jetpants'), File.join(File.dirname(__FILE__), '..', 'plugins') -%w(output callback table host db pool topology shard shardpool monkeypatch commandsuite).each {|g| require g} +%w(output callback table host hostservice db pool topology shard shardpool monkeypatch commandsuite).each {|g| require g} # Since Jetpants is extremely multi-threaded, we need to force uncaught exceptions to # kill all threads in order to have any kind of sane error handling. diff --git a/lib/jetpants/host.rb b/lib/jetpants/host.rb index d559a30..723b2c0 100644 --- a/lib/jetpants/host.rb +++ b/lib/jetpants/host.rb @@ -37,6 +37,7 @@ def initialize(ip) @lock = Mutex.new @available = nil @clone_multi_threaded = false # default use fast_copy_chain + @service_manager = nil end # Returns a Host object for the machine Jetpants is running on. @@ -770,40 +771,37 @@ def mount_stats(mount) end end - ###### Misc methods ######################################################## + ###### Service management methods ########################################## + def service_api + if @service_manager.nil? + @service_manager = Jetpants::HostService.pick_by_preflight(self) + end + + @service_manager + end def service_start(name, options=[]) - output service(:start, name, options.join(' ')) + output service_api.start(name, options) end def service_restart(name, options=[]) - output service(:restart, name, options.join(' ')) + output service_api.restart(name, options) end def service_stop(name) - output service(:stop, name) + output service_api.stop(name) end def service_running?(name) - status = service(:status, name).downcase - # mysql is running if the output of "service mysql status" doesn't include any of these strings - not_running_strings = ['not running', 'stop/waiting'] - - not_running_strings.none? {|str| status.include? str} + service_api.running?(name) end - # Performs the given operation (:start, :stop, :restart, :status) for the - # specified service (ie "mysql"). Requires that the "service" bin is in - # root's PATH. - # Please be aware that the output format and exit codes for the service - # binary vary between Linux distros! You may find that you need to override - # methods that call Host#service with :status operation (such as - # DB#probe_running) in a custom plugin, to parse the output properly on - # your chosen Linux distro. def service(operation, name, options='') - ssh_cmd "service #{name} #{operation.to_s} #{options}".rstrip + output "Warning: Calling Host.service directly is deprecated!".red + service_manager.service_direct(operation, name, options).rstrip end + ###### Misc methods ######################################################## # `stat` call to get all the information about the given file def get_file_stats(filename) mode_re = /^Access:\s+\((?\d+)\/(?[drwx-]+)\)\s+Uid:\s+\(\s+\d+\/\s+(?\w+)\)\s+Gid:\s+\(\s+\d+\/\s+(?\w+)\)$/x diff --git a/lib/jetpants/hostservice.rb b/lib/jetpants/hostservice.rb new file mode 100644 index 0000000..4c971e4 --- /dev/null +++ b/lib/jetpants/hostservice.rb @@ -0,0 +1,26 @@ +require 'hostservice/upstart' + +module Jetpants + module HostService + def self.pick_by_preflight(host) + # We want to pick the first provider which the machine supports. Ruby 1.9.2 has no + # `first_where` method like + # [1, 2, 3].first { |i| i > 1 } == 2 + # but the next line fakes it, by deleting items until we find one which does match, then + # taking the first item in the array. + # This could be a `.map` or a `.select` but we really don't want to try any more than we + # have to. + provider = all_providers.drop_while { |candidate| ! candidate.preflight(host) }.first + raise "Cannot detect a valid service provider for #{host}" if provider.nil? + + return provider.new(host) + end + + def self.all_providers + # Service managers that we can support, in order of most to least likely + [ + Jetpants::HostService::Upstart, + ] + end + end +end diff --git a/lib/jetpants/hostservice/upstart.rb b/lib/jetpants/hostservice/upstart.rb new file mode 100644 index 0000000..d4e6b76 --- /dev/null +++ b/lib/jetpants/hostservice/upstart.rb @@ -0,0 +1,47 @@ +module Jetpants + module HostService + class Upstart + def self.preflight(host) + host.has_installed('service') + end + + def initialize(host) + @host = host + end + + def start(name, options=[]) + service(:start, name, options.join(' ')) + end + + def restart(name, options=[]) + service(:restart, name, options.join(' ')) + end + + def stop(name) + service(:stop, name) + end + + def running?(name) + status = service(:status, name).downcase + # the service is running if the output of "service #{name} status" doesn't include any of + # these strings + not_running_strings = ['not running', 'stop/waiting'] + + not_running_strings.none? {|str| status.include? str} + end + + def service_direct(operation, name, options='') + service(operation, name, options) + end + + # Performs the given operation (:start, :stop, :restart, :status) for the + # specified service (ie "mysql"). Requires that the "service" bin is in + # root's PATH. + # Please be aware that the output format and exit codes for the service + # binary vary between Linux distros! + def service(operation, name, options='') + @host.ssh_cmd "service #{name} #{operation.to_s} #{options}".rstrip + end + end + end +end