Skip to content

Commit 3e47ee6

Browse files
authored
Merge pull request #2574 from lollipopman/crio-container-support
(FACT-3202) Add is_virtual and virtual support for crio
2 parents 27f870b + 7bc38cc commit 3e47ee6

File tree

9 files changed

+125
-29
lines changed

9 files changed

+125
-29
lines changed

lib/facter/framework/core/file_loader.rb

+1
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@
372372
when 'linux'
373373
require_relative '../../util/linux/dhcp'
374374
require_relative '../../util/linux/if_inet6'
375+
require_relative '../../util/linux/proc'
375376
require_relative '../../util/linux/routing_table'
376377
require_relative '../../util/linux/socket_parser'
377378

lib/facter/resolvers/containers.rb

+38-18
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,65 @@ class << self
1414
private
1515

1616
def post_resolve(fact_name, _options)
17-
@fact_list.fetch(fact_name) { read_cgroup(fact_name) }
17+
@fact_list.fetch(fact_name) do
18+
read_environ(fact_name) || read_cgroup(fact_name)
19+
end
1820
end
1921

2022
def read_cgroup(fact_name)
2123
output_cgroup = Facter::Util::FileHelper.safe_read('/proc/1/cgroup', nil)
22-
output_environ = Facter::Util::FileHelper.safe_read('/proc/1/environ', nil)
23-
return unless output_cgroup && output_environ
24+
return unless output_cgroup
2425

2526
output_docker = %r{docker/(.+)}.match(output_cgroup)
2627
output_lxc = %r{^/lxc/([^/]+)}.match(output_cgroup)
27-
lxc_from_environ = /container=lxc/ =~ output_environ
2828

29-
info, vm = extract_vm_and_info(output_docker, output_lxc, lxc_from_environ)
30-
info, vm = extract_for_nspawn(output_environ) unless vm
29+
info, vm = extract_vm_and_info(output_docker, output_lxc)
30+
@fact_list[:vm] = vm
31+
@fact_list[:hypervisor] = { vm.to_sym => info } if vm
32+
@fact_list[fact_name]
33+
end
34+
35+
def read_environ(fact_name)
36+
begin
37+
container = Facter::Util::Linux::Proc.getenv_for_pid(1, 'container')
38+
rescue StandardError => e
39+
log.warn("Unable to getenv for pid 1, '#{e}'")
40+
return nil
41+
end
42+
return if container.nil? || container.empty?
43+
44+
info = {}
45+
case container
46+
when 'lxcroot'
47+
vm = 'lxc'
48+
when 'podman'
49+
vm = 'podman'
50+
when 'crio'
51+
vm = 'crio'
52+
when 'systemd-nspawn'
53+
vm = 'systemd_nspawn'
54+
info = { 'id' => Facter::Util::FileHelper.safe_read('/etc/machine-id', nil).strip }
55+
else
56+
vm = 'container_other'
57+
log.warn("Container runtime, '#{container}', is unsupported, setting to, '#{vm}'")
58+
end
3159
@fact_list[:vm] = vm
3260
@fact_list[:hypervisor] = { vm.to_sym => info } if vm
3361
@fact_list[fact_name]
3462
end
3563

36-
def extract_vm_and_info(output_docker, output_lxc, lxc_from_environ)
64+
def extract_vm_and_info(output_docker, output_lxc)
3765
vm = nil
3866
if output_docker
3967
vm = 'docker'
4068
info = output_docker[1]
69+
elsif output_lxc
70+
vm = 'lxc'
71+
info = output_lxc[1]
4172
end
42-
vm = 'lxc' if output_lxc || lxc_from_environ
43-
info = output_lxc[1] if output_lxc
4473

4574
[info ? { INFO[vm] => info } : {}, vm]
4675
end
47-
48-
def extract_for_nspawn(output_environ)
49-
nspawn = /container=systemd-nspawn/ =~ output_environ
50-
if nspawn
51-
vm = 'systemd_nspawn'
52-
info = Facter::Util::FileHelper.safe_read('/etc/machine-id', nil)
53-
end
54-
[info ? { 'id' => info.strip } : {}, vm]
55-
end
5676
end
5777
end
5878
end

lib/facter/util/file_helper.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ def safe_read(path, default_return = '')
1515
default_return
1616
end
1717

18-
def safe_readlines(path, default_return = [])
19-
return File.readlines(path, encoding: Encoding::UTF_8) if File.readable?(path)
18+
# rubocop:disable Style/SpecialGlobalVars
19+
def safe_readlines(path, default_return = [], sep = $/, chomp: false)
20+
return File.readlines(path, sep, chomp: chomp, encoding: Encoding::UTF_8) if File.readable?(path)
2021

2122
log_failed_to_read(path)
2223
default_return
2324
end
25+
# rubocop:enable Style/SpecialGlobalVars
2426

2527
# This previously acted as a helper method for versions of Ruby older
2628
# than 2.5, before Dir.children was added. As it isn't a private

lib/facter/util/linux/proc.rb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
module Facter
4+
module Util
5+
module Linux
6+
class Proc
7+
class << self
8+
def getenv_for_pid(pid, field)
9+
path = "/proc/#{pid}/environ"
10+
lines = Facter::Util::FileHelper.safe_readlines(path, [], "\0", chomp: true)
11+
lines.each do |line|
12+
key, value = line.split('=', 2)
13+
return value if key == field
14+
end
15+
nil
16+
end
17+
end
18+
end
19+
end
20+
end
21+
end

spec/facter/resolvers/containers_spec.rb

+18-7
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
allow(Facter::Util::FileHelper).to receive(:safe_read)
88
.with('/proc/1/cgroup', nil)
99
.and_return(cgroup_output)
10-
allow(Facter::Util::FileHelper).to receive(:safe_read)
11-
.with('/proc/1/environ', nil)
10+
allow(Facter::Util::FileHelper).to receive(:safe_readlines)
11+
.with('/proc/1/environ', [], "\0", chomp: true)
1212
.and_return(environ_output)
1313
end
1414

@@ -18,7 +18,7 @@
1818

1919
context 'when hypervisor is docker' do
2020
let(:cgroup_output) { load_fixture('docker_cgroup').read }
21-
let(:environ_output) { 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
21+
let(:environ_output) { ['PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'] }
2222
let(:result) { { docker: { 'id' => 'ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90' } } }
2323

2424
it 'return docker for vm' do
@@ -32,7 +32,7 @@
3232

3333
context 'when hypervisor is nspawn' do
3434
let(:cgroup_output) { load_fixture('cgroup_file').read }
35-
let(:environ_output) { 'PATH=/usr/local/sbin:/bincontainer=systemd-nspawnTERM=xterm-256color' }
35+
let(:environ_output) { ['PATH=/usr/local/sbin:/bin', 'container=systemd-nspawn', 'TERM=xterm-256color'] }
3636
let(:result) { { systemd_nspawn: { 'id' => 'ee6e3c05422f1273c9b41a26f2b4ec64bdb4480d63a1ad9741e05cafc1651b90' } } }
3737

3838
before do
@@ -52,7 +52,7 @@
5252

5353
context 'when hypervisor is lxc and it is discovered by cgroup' do
5454
let(:cgroup_output) { load_fixture('lxc_cgroup').read }
55-
let(:environ_output) { 'PATH=/usr/local/sbin:/sbin:/bin' }
55+
let(:environ_output) { ['PATH=/usr/local/sbin:/sbin:/bin'] }
5656
let(:result) { { lxc: { 'name' => 'lxc_container' } } }
5757

5858
it 'return lxc for vm' do
@@ -66,7 +66,7 @@
6666

6767
context 'when hypervisor is lxc and it is discovered by environ' do
6868
let(:cgroup_output) { load_fixture('cgroup_file').read }
69-
let(:environ_output) { 'container=lxcroot' }
69+
let(:environ_output) { ['container=lxcroot'] }
7070
let(:result) { { lxc: {} } }
7171

7272
it 'return lxc for vm' do
@@ -80,7 +80,7 @@
8080

8181
context 'when hypervisor is neighter lxc nor docker' do
8282
let(:cgroup_output) { load_fixture('cgroup_file').read }
83-
let(:environ_output) { 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin' }
83+
let(:environ_output) { ['PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin'] }
8484
let(:result) { nil }
8585

8686
it 'return lxc for vm' do
@@ -91,4 +91,15 @@
9191
expect(containers_resolver.resolve(:hypervisor)).to eq(result)
9292
end
9393
end
94+
95+
context 'when hypervisor is an unknown container runtime discovered by environ' do
96+
let(:cgroup_output) { load_fixture('cgroup_file').read }
97+
let(:environ_output) { ['container=UNKNOWN'] }
98+
let(:logger) { Facter::Log.class_variable_get(:@@logger) }
99+
100+
it 'return container_other for vm' do
101+
expect(logger).to receive(:warn).with(/Container runtime, 'UNKNOWN', is unsupported, setting to, 'container_other'/)
102+
expect(containers_resolver.resolve(:vm)).to eq('container_other')
103+
end
104+
end
94105
end

spec/facter/util/file_helper_spec.rb

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
let(:path) { '/Users/admin/file.txt' }
77
let(:entries) { ['file.txt', 'a'] }
88
let(:content) { 'file content' }
9+
# rubocop:disable Style/SpecialGlobalVars
10+
let(:sep) { $/ }
11+
# rubocop:enable Style/SpecialGlobalVars
912
let(:error_message) do
1013
"Facter::Util::FileHelper - #{Facter::CYAN}File at: /Users/admin/file.txt is not accessible.#{Facter::RESET}"
1114
end
@@ -114,7 +117,7 @@
114117

115118
describe '#safe_read_lines' do
116119
before do
117-
allow(File).to receive(:readlines).with(path, anything).and_return(array_content)
120+
allow(File).to receive(:readlines).with(path, sep, anything).and_return(array_content)
118121
end
119122

120123
context 'when successfully read the file lines' do
@@ -133,7 +136,7 @@
133136
it 'File.readlines is called with the correct path' do
134137
file_helper.safe_readlines(path)
135138

136-
expect(File).to have_received(:readlines).with(path, anything)
139+
expect(File).to have_received(:readlines).with(path, sep, anything)
137140
end
138141

139142
it "doesn't log anything" do

spec/facter/util/linux/proc_spec.rb

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# frozen_string_literal: true
2+
3+
describe Facter::Util::Linux::Proc do
4+
describe '#getenv_for_pid' do
5+
before do
6+
allow(Facter::Util::FileHelper).to receive(:safe_readlines)
7+
.with('/proc/1/environ', [], "\0", { chomp: true })
8+
.and_return(proc_environ.readlines("\0", chomp: true))
9+
end
10+
11+
context 'when field exists' do
12+
let(:proc_environ) { load_fixture('proc_environ_podman') }
13+
14+
it 'returns the field' do
15+
result = Facter::Util::Linux::Proc.getenv_for_pid(1, 'container')
16+
expect(result).to eq('podman')
17+
end
18+
end
19+
20+
context 'when field does not exist' do
21+
let(:proc_environ) { load_fixture('proc_environ_podman') }
22+
23+
it 'returns nil' do
24+
result = Facter::Util::Linux::Proc.getenv_for_pid(1, 'butter')
25+
expect(result).to eq(nil)
26+
end
27+
end
28+
29+
context 'when field is empty' do
30+
let(:proc_environ) { load_fixture('proc_environ_no_value') }
31+
32+
it 'returns an empty string' do
33+
result = Facter::Util::Linux::Proc.getenv_for_pid(1, 'bubbles')
34+
expect(result).to eq('')
35+
end
36+
end
37+
end
38+
end

spec/fixtures/proc_environ_no_value

28 Bytes
Binary file not shown.

spec/fixtures/proc_environ_podman

127 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)