lib/omnibus/health_check.rb in omnibus-8.2.2 vs lib/omnibus/health_check.rb in omnibus-8.3.2
- old
+ new
@@ -1,7 +1,7 @@
-# Copyright 2012-2018 Chef Software, Inc.
+# Copyright:: Copyright (c) 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
#
@@ -64,24 +64,29 @@
# if the healthchecks pass
#
def run!
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
+ bad_libs, good_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." }
+ [{}, {}]
+ when "solaris2"
+ health_check_solaris
+ when "freebsd", "openbsd", "netbsd"
+ health_check_freebsd
+ else
+ health_check_linux
+ end
unresolved = []
unreliable = []
detail = []
@@ -165,10 +170,14 @@
end
raise HealthCheckFailed
end
+ if good_libs.keys.length == 0 && !windows?
+ raise "Internal error: no good libraries were found"
+ end
+
conflict_map = {}
conflict_map = relocation_check if relocation_checkable?
if conflict_map.keys.length > 0
@@ -278,23 +287,24 @@
# the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count)
#
def health_check_otool
current_library = nil
bad_libs = {}
+ good_libs = {}
- read_shared_libs("find #{project.install_dir}/ -type f | egrep '\.(dylib|bundle)$' | xargs otool -L") do |line|
+ read_shared_libs("find #{project.install_dir}/ -type f | egrep '\.(dylib|bundle)$'", "xargs otool -L") do |line|
case line
when /^(.+):$/
current_library = Regexp.last_match[1]
when /^\s+(.+) \(.+\)$/
linked = Regexp.last_match[1]
name = File.basename(linked)
- bad_libs = check_for_bad_library(bad_libs, current_library, name, linked)
+ bad_libs, good_libs = check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
end
end
- bad_libs
+ [bad_libs, good_libs]
end
#
# Run healthchecks against aix.
#
@@ -302,71 +312,139 @@
# the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count)
#
def health_check_aix
current_library = nil
bad_libs = {}
+ good_libs = {}
- read_shared_libs("find #{project.install_dir}/ -type f | xargs file | grep \"RISC System\" | awk -F: '{print $1}' | xargs -n 1 ldd") do |line|
+ read_shared_libs("find #{project.install_dir}/ -type f | xargs file | grep \"XCOFF\" | awk -F: '{print $1}'", "xargs -n 1 ldd") do |line|
case line
when /^(.+) needs:$/
current_library = Regexp.last_match[1]
log.debug(log_key) { "Analyzing dependencies for #{current_library}" }
when /^\s+(.+)$/
name = Regexp.last_match[1]
linked = Regexp.last_match[1]
- bad_libs = check_for_bad_library(bad_libs, current_library, name, linked)
+ ( bad_libs, good_libs ) = check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
when /File is not an executable XCOFF file/ # ignore non-executable files
else
log.warn(log_key) { "Line did not match for #{current_library}\n#{line}" }
end
end
- bad_libs
+ [bad_libs, good_libs]
end
#
- # Run healthchecks against ldd.
+ # Run healthchecks on Solaris.
#
# @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(%r{/}, '\/') + ".*" }.join("|")
- regexp = regexp_ends + "|" + regexp_patterns
+ def health_check_solaris
+ current_library = nil
+ bad_libs = {}
+ good_libs = {}
+ read_shared_libs("find #{project.install_dir}/ -type f | xargs file | grep \"ELF\" | awk -F: '{print $1}' | sed -e 's/:$//'", "xargs -n 1 ldd") do |line|
+ case line
+ when /^(.+):$/
+ current_library = Regexp.last_match[1]
+ log.debug(log_key) { "Analyzing dependencies for #{current_library}" }
+ when /^\s+(.+) \=\>\s+(.+)( \(.+\))?$/
+ name = Regexp.last_match[1]
+ linked = Regexp.last_match[2]
+ ( bad_libs, good_libs ) = check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
+ when /^\s+(.+) \(.+\)$/
+ next
+ when /^\s+statically linked$/
+ next
+ when /^\s+not a dynamic executable$/ # ignore non-executable files
+ else
+ log.warn(log_key) do
+ "Line did not match for #{current_library}\n#{line}"
+ end
+ end
+ end
+
+ [bad_libs, good_libs]
+ end
+
+ #
+ # Run healthchecks on FreeBSD
+ #
+ # @return [Hash<String, Hash<String, Hash<String, Int>>>]
+ # the bad libraries (library_name -> dependency_name -> satisfied_lib_path -> count)
+ #
+ def health_check_freebsd
current_library = nil
bad_libs = {}
+ good_libs = {}
- read_shared_libs("find #{project.install_dir}/ -type f -regextype posix-extended ! -regex '#{regexp}' | xargs ldd") do |line|
+ read_shared_libs("find #{project.install_dir}/ -type f | xargs file | grep \"ELF\" | awk -F: '{print $1}' | sed -e 's/:$//'", "xargs ldd") do |line|
case line
when /^(.+):$/
current_library = Regexp.last_match[1]
log.debug(log_key) { "Analyzing dependencies for #{current_library}" }
when /^\s+(.+) \=\>\s+(.+)( \(.+\))?$/
name = Regexp.last_match[1]
linked = Regexp.last_match[2]
- bad_libs = check_for_bad_library(bad_libs, current_library, name, linked)
+ ( bad_libs, good_libs ) = check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
when /^\s+(.+) \(.+\)$/
next
when /^\s+statically linked$/
next
- when /^\s+libjvm.so/
+ when /^\s+not a dynamic executable$/ # ignore non-executable files
+ else
+ log.warn(log_key) do
+ "Line did not match for #{current_library}\n#{line}"
+ end
+ end
+ end
+
+ [bad_libs, good_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_linux
+ current_library = nil
+ bad_libs = {}
+ good_libs = {}
+
+ read_shared_libs("find #{project.install_dir}/ -type f | xargs file | grep \"ELF\" | awk -F: '{print $1}' | sed -e 's/:$//'", "xargs ldd") do |line|
+ case line
+ when /^(.+):$/
+ current_library = Regexp.last_match[1]
+ log.debug(log_key) { "Analyzing dependencies for #{current_library}" }
+ when /^\s+(.+) \=\>\s+(.+)( \(.+\))?$/
+ name = Regexp.last_match[1]
+ linked = Regexp.last_match[2]
+ ( bad_libs, good_libs ) = check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
+ when /^\s+(.+) \(.+\)$/
next
- when /^\s+libjava.so/
+ when /^\s+statically linked$/
next
- when /^\s+libmawt.so/
+ when /^\s+libjvm.so/ # FIXME: should remove if it doesn't blow up server
next
+ when /^\s+libjava.so/ # FIXME: should remove if it doesn't blow up server
+ next
+ when /^\s+libmawt.so/ # FIXME: should remove if it doesn't blow up server
+ next
when /^\s+not a dynamic executable$/ # ignore non-executable files
else
log.warn(log_key) do
"Line did not match for #{current_library}\n#{line}"
end
end
end
- bad_libs
+ [bad_libs, good_libs]
end
private
#
@@ -397,15 +475,45 @@
# @param [String] command
# the command to execute
# @yield [String]
# each line
#
- def read_shared_libs(command)
- cmd = shellout(command)
- cmd.stdout.each_line do |line|
- yield line
+ def read_shared_libs(find_command, ldd_command, &output_proc)
+ #
+ # construct the list of files to check
+ #
+
+ find_output = shellout!(find_command).stdout.lines
+
+ find_output.reject! { |file| IGNORED_ENDINGS.any? { |ending| file.end_with?("#{ending}\n") } }
+
+ find_output.reject! { |file| IGNORED_SUBSTRINGS.any? { |substr| file.include?(substr) } }
+
+ if find_output.empty?
+ # probably the find_command is busted, it should never be empty or why are you using omnibus?
+ raise "Internal Error: Health Check found no lines"
end
+
+ if find_output.any? { |file| file !~ Regexp.new(project.install_dir) }
+ # every file in the find output should be within the install_dir
+ raise "Internal Error: Health Check lines not matching the install_dir"
+ end
+
+ #
+ # feed the list of files to the "ldd" command
+ #
+
+ # this command will typically fail if the last file isn't a valid lib/binary which happens often
+ ldd_output = shellout(ldd_command, input: find_output.join).stdout
+
+ #
+ # do the output process to determine if the files are good or bad
+ #
+
+ ldd_output.each_line do |line|
+ output_proc.call(line)
+ end
end
#
# Check the given path and library for "bad" libraries.
#
@@ -418,11 +526,11 @@
# @param [String]
# actual path of library satisfying the dependency
#
# @return the modified bad_library hash
#
- def check_for_bad_library(bad_libs, current_library, name, linked)
+ def check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
safe = nil
whitelist_libs = case Ohai["platform"]
when "arch"
ARCH_WHITELIST_LIBS
@@ -461,12 +569,13 @@
bad_libs[current_library][name][linked] += 1
else
bad_libs[current_library][name][linked] = 1
end
else
+ good_libs[current_library] = true
log.debug(log_key) { " -> PASSED: #{name} is either whitelisted or safely provided." }
end
- bad_libs
+ [bad_libs, good_libs]
end
end
end