Jira (FACT-2934) Facter::Core::Execution does not set status variables in Facter 4

109 views
Skip to first unread message

Charlie Sharpsteen (Jira)

unread,
Feb 2, 2021, 4:37:03 PM2/2/21
to puppe...@googlegroups.com
Charlie Sharpsteen created an issue
 
Facter / Bug FACT-2934
Facter::Core::Execution does not set status variables in Facter 4
Issue Type: Bug Bug
Affects Versions: FACT 4.0.49
Assignee: Unassigned
Created: 2021/02/02 1:36 PM
Priority: Critical Critical
Reporter: Charlie Sharpsteen

The exec and execute methods of the Facter::Core::Execution module are commonly used to execute external processes in custom facts. In Facter 4, these methods no longer set the global Ruby variables $CHILD_STATUS or $?.

This is a regression of FACT-1284 which was its self a regression of behavior present in Facter 2.x and 1.x.

Reproduction Case

  • Install puppet-agent 7 on CentOS 7:

yum install -y http://yum.puppetlabs.com/puppet7-release-el-7.noarch.rpm
yum install -y puppet-agent
 
source /etc/profile.d/puppet-agent.sh

  • Create a directory containing a custom fact that uses the $? variable:

mkdir exit_code_test
 
cat <<'EOF' > exit_code_test/exit_code_test.rb
Facter.add("exit_code_test") do
  setcode do
    Facter::Util::Resolution.exec('/usr/bin/false')
    "/usr/bin/false exited with code: %

" %

  end
end
EOF

  • Run Facter:

env FACTERLIB=$PWD/exit_code_test facter exit_code_test

Outcome

The fact fails to resolve, logging an error from a `nil` reference:

# facter --version
4.0.49
 
# env FACTERLIB=$PWD/exit_code_test facter exit_code_test
[2021-02-02 21:33:19.961928 ] ERROR Facter - Error while resolving custom fact fact='exit_code_test', resolution='<anonymous>': undefined method `exitstatus' for nil:NilClass

Expected Outcome

The exit status resolves to 1 as it does in Facter 3:

# facter --version
3.14.14 (commit e36657bea27254f003c8fc71d8ef57454db643e2)
 
# env FACTERLIB=$PWD/exit_code_test facter exit_code_test
/usr/bin/false exited with code: 1

And in Facter 2:

# facter --version
2.5.7
 
# env FACTERLIB=$PWD/exit_code_test facter exit_code_test
/usr/bin/false exited with code: 1

Add Comment Add Comment
 
This message was sent by Atlassian Jira (v8.5.2#805002-sha1:a66f935)
Atlassian logo

Charlie Sharpsteen (Jira)

unread,
Feb 2, 2021, 4:41:03 PM2/2/21
to puppe...@googlegroups.com
Charlie Sharpsteen commented on Bug FACT-2934
 
Re: Facter::Core::Execution does not set status variables in Facter 4

I think this is occurring because Facter 4 implements Facter::Core::Execution using Open3.popen3:

https://github.com/puppetlabs/facter/blob/4.0.49/lib/facter/custom_facts/core/execution/base.rb#L73

However, Open3 calls Process.detach which spawns a thread to reap the child process. This consumes the exit status of the child without setting the global status variables:

https://github.com/ruby/ruby/blob/v2_7_2/lib/open3.rb#L214

Charlie Sharpsteen (Jira)

unread,
Feb 2, 2021, 4:44:03 PM2/2/21
to puppe...@googlegroups.com
Charlie Sharpsteen updated an issue
 
Change By: Charlie Sharpsteen
The {{exec}} and {{execute}} methods of the {{Facter::Core::Execution}} module are commonly used to execute external processes in custom facts. In Facter 4, these methods no longer set the global Ruby variables {{$CHILD_STATUS}} or {{$?}}.


This is a regression of FACT-1284 which was its self a regression of behavior present in Facter 2.x and 1.x.

h2. Reproduction Case

  - Install {{puppet-agent}} 7 on CentOS 7:

{code:bash}

yum install -y http://yum.puppetlabs.com/puppet7-release-el-7.noarch.rpm
yum install -y puppet-agent

source /etc/profile.d/puppet-agent.sh
{code}

  - Create a directory containing a custom fact that uses the {{$?}} variable:

{code:bash}

mkdir exit_code_test

cat <<'EOF' > exit_code_test/exit_code_test.rb
Facter.add("exit_code_test") do
  setcode do
    Facter::Util::Resolution.exec('/usr/bin/false')
    "/usr/bin/false exited with code: %{ code status }" % { code status : $?.exitstatus}
  end
end
EOF
{code}

  - Run Facter:

{code:bash}
env FACTERLIB=$PWD/exit_code_test facter exit_code_test
{code}

h3. Outcome


The fact fails to resolve, logging an error from a `nil` reference:

{noformat}

# facter --version
4.0.49

# env FACTERLIB=$PWD/exit_code_test facter exit_code_test
[2021-02-02 21:33:19.961928 ] ERROR Facter - Error while resolving custom fact fact='exit_code_test', resolution='<anonymous>': undefined method `exitstatus' for nil:NilClass
{noformat}

h3. Expected Outcome


The exit status resolves to 1 as it does in Facter 3:

{noformat}

# facter --version
3.14.14 (commit e36657bea27254f003c8fc71d8ef57454db643e2)

# env FACTERLIB=$PWD/exit_code_test facter exit_code_test
/usr/bin/false exited with code: 1
{noformat}

And in Facter 2:

{noformat}

# facter --version
2.5.7

# env FACTERLIB=$PWD/exit_code_test facter exit_code_test
/usr/bin/false exited with code: 1
{noformat}

Nick Walker (Jira)

unread,
Feb 2, 2021, 4:53:03 PM2/2/21
to puppe...@googlegroups.com

Ciprian Badescu (Jira)

unread,
Feb 4, 2021, 5:40:03 AM2/4/21
to puppe...@googlegroups.com

Oana Tanasoiu (Jira)

unread,
Feb 4, 2021, 5:47:03 AM2/4/21
to puppe...@googlegroups.com
Oana Tanasoiu updated an issue
Change By: Oana Tanasoiu
Release Notes: Bug Fix
Release Notes Summary: Description of the problem: The exec and execute methods of the Facter::Core::Execution module are commonly used to execute external processes in custom facts. In Facter 4, these methods no longer set the global Ruby variables $CHILD_STATUS or $? because Open3 is used and it consumes the exit status of the child process.

Description of the fix: Reimplement Open3.popen3 to not use Process.detach but Process.wait instead.

Claire Cadman (Jira)

unread,
Feb 5, 2021, 4:19:02 AM2/5/21
to puppe...@googlegroups.com

Charlie Sharpsteen (Jira)

unread,
Feb 5, 2021, 6:03:02 PM2/5/21
to puppe...@googlegroups.com
Charlie Sharpsteen commented on Bug FACT-2934
 
Re: Facter::Core::Execution does not set status variables in Facter 4

I tried out PE 2021.0.0-rc5-67-gbfc68f0 and the new behavior looks good

# /opt/puppetlabs/puppet/bin/ruby -rfacter -e 'Facter::Core::Execution.execute("false") and puts($?.exitstatus)'
1

Reply all
Reply to author
Forward
0 new messages