Skip to content

Commit 15e7f17

Browse files
committed
(FACT-3202) Add is_virtual and virtual support for crio
Prior to this change facter returned: $ facter is_virtual false $ facter virtual physical After this change facter returns: $ facter is_virtual true $ facter virtual crio This change separates out reading pid 1's environment from proc and reading the cgroup information. It also adds explicit support for podman and returns container_other when the container runtime is not explicitly supported.
1 parent 05e4387 commit 15e7f17

File tree

7 files changed

+117
-25
lines changed

7 files changed

+117
-25
lines changed

Diff for: 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

Diff for: 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

Diff for: 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

Diff for: spec/facter/resolvers/containers_spec.rb

+19-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,16 @@
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+
105+
end
94106
end

Diff for: 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

Diff for: spec/fixtures/proc_environ_no_value

28 Bytes
Binary file not shown.

Diff for: spec/fixtures/proc_environ_podman

127 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)