Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update healthchecks to handle ldd failures #1137

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions lib/omnibus/health_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -511,16 +511,40 @@ def read_shared_libs(find_command, ldd_command, &output_proc)
# 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
# instance Mixlib::ShellOut
ldd_cmd = shellout(ldd_command, input: find_output.join)

# Optimized path: Attempt to run the `ldd` command on all file paths. If it succeeds, then process
# the stdout result in bulk. If the command returned a non-zero exit status code, then something went wrong.
# Each path will have to be manually resolved
unless ldd_cmd.error?
# do the output process to determine if the files are good or bad
ldd_cmd.stdout.each_line do |line|
output_proc.call(line)
end
else
log.debug(log_key) { "Failed running #{ldd_command} with exit status #{ldd_cmd.exitstatus} when resolving individually" }

#
# do the output process to determine if the files are good or bad
#
# Verify each path separately
find_output.each do |path|
ldd_cmd = shellout(ldd_command, input: path)
if ldd_cmd.error?
log.debug(log_key) { "Failed running #{ldd_command} with exit status #{ldd_cmd.exitstatus} against: #{path}" }
end

ldd_output.each_line do |line|
output_proc.call(line)
ldd_output = ldd_cmd.stdout

# Yield the path first
output_proc.call("#{path.rstrip}:")

# 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
end

nil
end

#
Expand Down
54 changes: 54 additions & 0 deletions spec/unit/health_check_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,28 @@ def mkdump(base, size, x64 = false)
let(:file_list) do
double("Mixlib::Shellout",
error!: false,
error?: false,
stdout: <<~EOH
/opt/chefdk/shouldnt/matter
EOH
)
end

let(:file_list_multiple) do
double("Mixlib::Shellout",
error!: false,
error?: false,
stdout: <<~EOH
/opt/chefdk/first
/opt/chefdk/second
EOH
)
end

let(:empty_list) do
double("Mixlib::Shellout",
error!: false,
error?: false,
stdout: <<~EOH
EOH
)
Expand All @@ -130,6 +143,7 @@ def mkdump(base, size, x64 = false)
let(:bad_list) do
double("Mixlib::Shellout",
error!: false,
error?: false,
stdout: <<~EOH
/somewhere/other/than/install/dir
EOH
Expand All @@ -139,6 +153,7 @@ def mkdump(base, size, x64 = false)
let(:bad_healthcheck) do
double("Mixlib::Shellout",
error!: false,
error?: false,
stdout: <<~EOH
/bin/ls:
linux-vdso.so.1 => (0x00007fff583ff000)
Expand All @@ -161,6 +176,7 @@ def mkdump(base, size, x64 = false)
let(:good_healthcheck) do
double("Mixlib::Shellout",
error!: false,
error?: false,
stdout: <<~EOH
/bin/echo:
linux-vdso.so.1 => (0x00007fff8a6ee000)
Expand All @@ -174,6 +190,17 @@ def mkdump(base, size, x64 = false)
)
end

let(:bad_exitstatus_healthcheck) do
double("Mixlib::Shellout",
error!: -> { raise Mixlib::ShellOut::ShellCommandFailed },
error?: true,
exitstatus: 135,
stdout: <<~EOH
/bin/echo:
EOH
)
end

it "raises an exception when there are external dependencies" do
allow(subject).to receive(:shellout)
.with("find /opt/chefdk/ -type f | xargs file | grep \"ELF\" | awk -F: '{print $1}' | sed -e 's/:$//'")
Expand All @@ -198,6 +225,33 @@ def mkdump(base, size, x64 = false)
expect { subject.run! }.to_not raise_error
end

it "does checks lld for each file if the initial bulk ldd command fails" do
allow(subject).to receive(:shellout)
.with("find /opt/chefdk/ -type f | xargs file | grep \"ELF\" | awk -F: '{print $1}' | sed -e 's/:$//'")
.and_return(file_list_multiple)

# Bulk ldd command fails
allow(subject).to receive(:shellout)
.with("xargs ldd", { input: "/opt/chefdk/first\n/opt/chefdk/second\n" })
.and_return(bad_exitstatus_healthcheck)

# First file ldd fails
allow(subject).to receive(:shellout)
.with("xargs ldd", { input: "/opt/chefdk/first\n" })
.and_return(bad_exitstatus_healthcheck)

# Second file lld succeeds
allow(subject).to receive(:shellout)
.with("xargs ldd", { input: "/opt/chefdk/second\n" })
.and_return(good_healthcheck)

output = capture_logging do
expect { subject.run! }.to_not raise_error
end
expect(output).to match(/Failed running xargs ldd with exit status 135 when resolving individually/)
expect(output).to match(%r{Failed running xargs ldd with exit status 135 against: /opt/chefdk/first})
end

it "will not perform dll base relocation checks" do
expect(subject.relocation_checkable?).to be false
end
Expand Down