lib/omnibus/health_check.rb in omnibus-5.4.0 vs lib/omnibus/health_check.rb in omnibus-5.5.0

- old
+ new

@@ -12,19 +12,20 @@ # 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 'omnibus/sugarable' +require "omnibus/sugarable" begin - require 'pedump' + require "pedump" rescue LoadError STDERR.puts "pedump not found - windows health checks disabled" end module Omnibus class HealthCheck + include Instrumentation include Logging include Util include Sugarable WHITELIST_LIBS = [ @@ -155,11 +156,11 @@ /libkvm\.so/, /libprocstat\.so/, /libmd\.so/, ].freeze - IGNORED_ENDINGS = %w( + IGNORED_ENDINGS = %w{ .[ch] .e*rb .gemspec .gitignore .h*h @@ -189,18 +190,18 @@ Gemfile LICENSE README Rakefile VERSION - ).freeze + }.freeze - IGNORED_PATTERNS = %w( + IGNORED_PATTERNS = %w{ /share/doc/ /share/postgresql/ /share/terminfo/ /terminfo/ - ).freeze + }.freeze class << self # @see (HealthCheck#new) def run!(project) new(project).run! @@ -233,157 +234,159 @@ # # @return [true] # if the healthchecks pass # def run! - log.info(log_key) {"Running health on #{project.name}"} - bad_libs = case Ohai['platform'] - when 'mac_os_x' - health_check_otool - when 'aix' - health_check_aix - when 'windows' - # TODO: objdump -p will provided a very limited check of - # explicit dependencies on windows. Most dependencies are - # implicit and hence not detected. - log.warn(log_key) { 'Skipping dependency health checks on Windows.' } - {} - else - health_check_ldd - end + measure("Health check time") do + log.info(log_key) { "Running health on #{project.name}" } + bad_libs = case Ohai["platform"] + when "mac_os_x" + health_check_otool + when "aix" + health_check_aix + when "windows" + # TODO: objdump -p will provided a very limited check of + # explicit dependencies on windows. Most dependencies are + # implicit and hence not detected. + log.warn(log_key) { "Skipping dependency health checks on Windows." } + {} + else + health_check_ldd + end - unresolved = [] - unreliable = [] - detail = [] + unresolved = [] + unreliable = [] + detail = [] - if bad_libs.keys.length > 0 - bad_libs.each do |name, lib_hash| - lib_hash.each do |lib, linked_libs| - linked_libs.each do |linked, count| - if linked =~ /not found/ - unresolved << lib unless unresolved.include? lib - else - unreliable << linked unless unreliable.include? linked + if bad_libs.keys.length > 0 + bad_libs.each do |name, lib_hash| + lib_hash.each do |lib, linked_libs| + linked_libs.each do |linked, count| + if linked =~ /not found/ + unresolved << lib unless unresolved.include? lib + else + unreliable << linked unless unreliable.include? linked + end + detail << "#{name}|#{lib}|#{linked}|#{count}" end - detail << "#{name}|#{lib}|#{linked}|#{count}" end end - end - log.error(log_key) { 'Failed!' } - bad_omnibus_libs, bad_omnibus_bins = bad_libs.keys.partition { |k| k.include? 'embedded/lib' } + log.error(log_key) { "Failed!" } + bad_omnibus_libs, bad_omnibus_bins = bad_libs.keys.partition { |k| k.include? "embedded/lib" } - log.error(log_key) do - out = "The following libraries have unsafe or unmet dependencies:\n" + log.error(log_key) do + out = "The following libraries have unsafe or unmet dependencies:\n" - bad_omnibus_libs.each do |lib| - out << " --> #{lib}\n" + bad_omnibus_libs.each do |lib| + out << " --> #{lib}\n" + end + + out end - out - end + log.error(log_key) do + out = "The following binaries have unsafe or unmet dependencies:\n" - log.error(log_key) do - out = "The following binaries have unsafe or unmet dependencies:\n" + bad_omnibus_bins.each do |bin| + out << " --> #{bin}\n" + end - bad_omnibus_bins.each do |bin| - out << " --> #{bin}\n" + out end - out - end + if unresolved.length > 0 + log.error(log_key) do + out = "The following requirements could not be resolved:\n" - if unresolved.length > 0 - log.error(log_key) do - out = "The following requirements could not be resolved:\n" + unresolved.each do |lib| + out << " --> #{lib}\n" + end - unresolved.each do |lib| - out << " --> #{lib}\n" + out end + end - out + if unreliable.length > 0 + log.error(log_key) do + out = "The following libraries cannot be guaranteed to be on " + out << "target systems:\n" + + unreliable.each do |lib| + out << " --> #{lib}\n" + end + + out + end end - end - if unreliable.length > 0 log.error(log_key) do - out = "The following libraries cannot be guaranteed to be on " - out << "target systems:\n" + out = "The precise failures were:\n" - unreliable.each do |lib| - out << " --> #{lib}\n" + detail.each do |line| + item, dependency, location, count = line.split("|") + reason = location =~ /not found/ ? "Unresolved dependency" : "Unsafe dependency" + + out << " --> #{item}\n" + out << " DEPENDS ON: #{dependency}\n" + out << " COUNT: #{count}\n" + out << " PROVIDED BY: #{location}\n" + out << " FAILED BECAUSE: #{reason}\n" end out end - end - log.error(log_key) do - out = "The precise failures were:\n" - - detail.each do |line| - item, dependency, location, count = line.split('|') - reason = location =~ /not found/ ? 'Unresolved dependency' : 'Unsafe dependency' - - out << " --> #{item}\n" - out << " DEPENDS ON: #{dependency}\n" - out << " COUNT: #{count}\n" - out << " PROVIDED BY: #{location}\n" - out << " FAILED BECAUSE: #{reason}\n" - end - - out + raise HealthCheckFailed end - raise HealthCheckFailed - end + conflict_map = {} - conflict_map = {} + conflict_map = relocation_check if relocation_checkable? - conflict_map = relocation_check if relocation_checkable? + if conflict_map.keys.length > 0 + log.warn(log_key) { "Multiple dlls with overlapping images detected" } - if conflict_map.keys.length > 0 - log.warn(log_key) { 'Multiple dlls with overlapping images detected' } + conflict_map.each do |lib_name, data| + base = data[:base] + size = data[:size] + next_valid_base = data[:base] + data[:size] - conflict_map.each do |lib_name, data| - base = data[:base] - size = data[:size] - next_valid_base = data[:base] + data[:size] + log.warn(log_key) do + out = "Overlapping dll detected:\n" + out << " #{lib_name} :\n" + out << " IMAGE BASE: #{hex}\n" % base + out << " IMAGE SIZE: #{hex} (#{size} bytes)\n" % size + out << " NEXT VALID BASE: #{hex}\n" % next_valid_base + out << " CONFLICTS:\n" - log.warn(log_key) do - out = "Overlapping dll detected:\n" - out << " #{lib_name} :\n" - out << " IMAGE BASE: #{hex}\n" % base - out << " IMAGE SIZE: #{hex} (#{size} bytes)\n" % size - out << " NEXT VALID BASE: #{hex}\n" % next_valid_base - out << " CONFLICTS:\n" + data[:conflicts].each do |conflict_name| + cbase = conflict_map[conflict_name][:base] + csize = conflict_map[conflict_name][:size] + out << " - #{conflict_name} #{hex} + #{hex}\n" % [cbase, csize] + end - data[:conflicts].each do |conflict_name| - cbase = conflict_map[conflict_name][:base] - csize = conflict_map[conflict_name][:size] - out << " - #{conflict_name} #{hex} + #{hex}\n" % [cbase, csize] + out end - - out end + + # Don't raise an error yet. This is only bad for FIPS mode. end - # Don't raise an error yet. This is only bad for FIPS mode. + true end - - true end # Ensure the method relocation_check is able to run # # @return [Boolean] # def relocation_checkable? return false unless windows? begin - require 'pedump' + require "pedump" true rescue LoadError false end end @@ -401,11 +404,11 @@ embedded_bin = "#{project.install_dir}/embedded/bin" Dir.glob("#{embedded_bin}/*.dll") do |lib_path| log.debug(log_key) { "Analyzing dependencies for #{lib_path}" } - File.open(lib_path, 'rb') do |f| + File.open(lib_path, "rb") do |f| dump = PEdump.new(lib_path) pe = dump.pe f # Don't scan dlls for a different architecture. next if windows_arch_i386? == pe.x64? @@ -416,11 +419,11 @@ conflicts = [] # This can be done more smartly but O(n^2) is just fine for n = small conflict_map.each do |candidate_name, details| unless details[:base] >= base + size || - details[:base] + details[:size] <= base + details[:base] + details[:size] <= base details[:conflicts] << lib_name conflicts << candidate_name end end @@ -492,18 +495,18 @@ bad_libs end # # Run healthchecks against ldd. - # + # # @return [Hash<String, Hash<String, Hash<String, Int>>>] # the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count) # def health_check_ldd - regexp_ends = '.*(' + IGNORED_ENDINGS.map { |e| e.gsub(/\./, '\.') }.join('|') + ')$' - regexp_patterns = IGNORED_PATTERNS.map { |e| '.*' + e.gsub(/\//, '\/') + '.*' }.join('|') - regexp = regexp_ends + '|' + regexp_patterns + regexp_ends = ".*(" + IGNORED_ENDINGS.map { |e| e.gsub(/\./, '\.') }.join("|") + ")$" + regexp_patterns = IGNORED_PATTERNS.map { |e| ".*" + e.gsub(/\//, '\/') + ".*" }.join("|") + regexp = regexp_ends + "|" + regexp_patterns current_library = nil bad_libs = {} read_shared_libs("find #{project.install_dir}/ -type f -regextype posix-extended ! -regex '#{regexp}' | xargs ldd") do |line| @@ -590,21 +593,21 @@ # @return the modified bad_library hash # def check_for_bad_library(bad_libs, current_library, name, linked) safe = nil - whitelist_libs = case Ohai['platform'] - when 'arch' + whitelist_libs = case Ohai["platform"] + when "arch" ARCH_WHITELIST_LIBS - when 'mac_os_x' + when "mac_os_x" MAC_WHITELIST_LIBS - when 'solaris2' + when "solaris2" SOLARIS_WHITELIST_LIBS - when 'smartos' + when "smartos" SMARTOS_WHITELIST_LIBS - when 'freebsd' + when "freebsd" FREEBSD_WHITELIST_LIBS - when 'aix' + when "aix" AIX_WHITELIST_LIBS else WHITELIST_LIBS end