Skip to content
Draft
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
20 changes: 19 additions & 1 deletion lib/facter/runlevel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@
Facter.add('runlevel') do
confine kernel: 'Linux'
setcode do
`"/sbin/runlevel"`.split.last
runlevel = nil

if (runlevel_path = Facter::Core::Execution.which('runlevel'))
result = Facter::Core::Execution.exec(runlevel_path)
runlevel = result.split.last if result
elsif Facter::Core::Execution.which('systemctl')
# Map systemd targets to traditional SysV runlevels
target_to_runlevel = {
'poweroff.target' => '0',
'rescue.target' => '1',
'multi-user.target' => '3',
'graphical.target' => '5',
'reboot.target' => '6',
}
result = Facter::Core::Execution.exec('systemctl get-default')
runlevel = target_to_runlevel[result&.strip]
end

runlevel
end
end
65 changes: 65 additions & 0 deletions spec/unit/facter/runlevel_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require 'spec_helper'

describe 'runlevel' do
before :each do
Facter.clear

# mock out Facter method called when evaluating confine for :kernel
allow(Facter::Core::Execution).to receive(:exec).with('uname -s').and_return('Linux')
end

context 'when runlevel binary is available' do
it 'returns the current runlevel' do
expect(Facter::Core::Execution).to receive(:which).with('runlevel').and_return('/sbin/runlevel')
expect(Facter::Core::Execution).to receive(:exec).with('/sbin/runlevel').and_return('N 3')

expect(Facter.fact('runlevel').value).to eq('3')
end
end

context 'when runlevel binary is not available' do
before :each do
expect(Facter::Core::Execution).to receive(:which).with('runlevel').and_return(nil)
end

context 'when systemctl is available' do
before :each do
expect(Facter::Core::Execution).to receive(:which).with('systemctl').and_return('/usr/bin/systemctl')
end

{
'poweroff.target' => '0',
'rescue.target' => '1',
'multi-user.target' => '3',
'graphical.target' => '5',
'reboot.target' => '6',
}.each do |target, expected_runlevel|
context "when the default target is #{target}" do
it "returns runlevel #{expected_runlevel}" do
expect(Facter::Core::Execution).to receive(:exec).with('systemctl get-default').and_return("#{target}\n")

expect(Facter.fact('runlevel').value).to eq(expected_runlevel)
end
end
end

context 'when the default target is an unmapped target' do
it 'returns nil' do
expect(Facter::Core::Execution).to receive(:exec).with('systemctl get-default').and_return("emergency.target\n")

expect(Facter.fact('runlevel').value).to be nil
end
end
end

context 'when systemctl is not available' do
it 'returns nil' do
expect(Facter::Core::Execution).to receive(:which).with('systemctl').and_return(nil)

expect(Facter.fact('runlevel').value).to be nil
end
end
end
end