Skip to content

Commit b1c1c4d

Browse files
(FACT-2872_2) Reimplement linux networking resolver
2 parents 7635fb1 + 4e38114 commit b1c1c4d

File tree

18 files changed

+135
-24
lines changed

18 files changed

+135
-24
lines changed

.rubocop.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ AllCops:
66
Exclude:
77
- acceptance/**/*
88
- vendor/**/*
9+
- lib/facter/resolvers/networking_linux_resolver.rb
910

1011
require:
1112
- rubocop-performance
@@ -93,6 +94,8 @@ Metrics/ClassLength:
9394
- 'lib/facter/framework/core/cache_manager.rb'
9495
- 'install.rb'
9596
- 'scripts/generate_changelog.rb'
97+
- 'lib/facter/resolvers/solaris/networking.rb'
98+
9699

97100
Naming/AccessorMethodName:
98101
Exclude:
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
test_name "FACT-2874: file containing external facts are loaded in lexicographical order" do
2+
tag 'risk:high'
3+
4+
require 'facter/acceptance/user_fact_utils'
5+
extend Facter::Acceptance::UserFactUtils
6+
7+
fact_name = 'test'
8+
# Use a static external fact
9+
ext_fact1 = "#{fact_name}: 'EXTERNAL1'"
10+
ext_fact2 = "#{fact_name}: 'EXTERNAL2'"
11+
12+
agents.each do |agent|
13+
facts_dir = agent.tmpdir('facts.d')
14+
ext_fact_path1 = "#{facts_dir}/a_test.yaml"
15+
ext_fact_path2 = "#{facts_dir}/b_test.yaml"
16+
create_remote_file(agent, ext_fact_path1, ext_fact1)
17+
create_remote_file(agent, ext_fact_path2, ext_fact2)
18+
19+
teardown do
20+
agent.rm_rf(facts_dir)
21+
end
22+
23+
step "Agent #{agent}: resolve external fact with the last value it resolves to" do
24+
on(agent, facter("--external-dir \"#{facts_dir}\" #{fact_name}")) do |facter_output|
25+
assert_equal("EXTERNAL2", facter_output.stdout.chomp)
26+
end
27+
end
28+
end
29+
end
30+
31+

acceptance/tests/facts/networking_facts_with_secondary_ips.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def ip_name(interface, index)
6767
end
6868

6969
step "Check secondary interfaces are inside the bindings of the primary interface" do
70-
expected_bindings(interface, 4).each do |fact_tokens, regex|
70+
expected_bindings(interface, 2).each do |fact_tokens, regex|
7171
assert_match(regex, networking_facts.dig(*fact_tokens).to_s)
7272
end
7373
end

lib/facter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ def to_hash
334334

335335
resolved_facts = Facter::FactManager.instance.resolve_facts
336336
resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? }
337-
Facter::FactCollection.new.build_fact_collection!(resolved_facts)
337+
collection = Facter::FactCollection.new.build_fact_collection!(resolved_facts)
338+
Hash[collection]
338339
end
339340

340341
# Check whether printing stack trace is enabled

lib/facter/custom_facts/util/directory_loader.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def resolve_fact(fact, parser)
109109

110110
def entries
111111
dirs = @directories.select { |directory| File.directory?(directory) }.map do |directory|
112-
Dir.entries(directory).map { |directory_entry| File.join(directory, directory_entry) }
112+
Dir.entries(directory).map { |directory_entry| File.join(directory, directory_entry) }.sort.reverse!
113113
end
114114
dirs.flatten.select { |f| should_parse?(f) }
115115
rescue Errno::ENOENT

lib/facter/custom_facts/util/fact.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,16 @@ def find_suitable_resolutions(resolutions)
173173
end
174174

175175
def sort_by_weight(resolutions)
176-
resolutions.sort { |a, b| b <=> a }
176+
# sort resolutions:
177+
# - descending by weight
178+
# - multiple facts have the same weight but different types, the :external fact take precedence
179+
# - multiple facts with the same weight and type, the order is preserved.
180+
# note: sort_by is slower than .sort
181+
# we cannot use .sort because it is not stable: https://bugs.ruby-lang.org/issues/1089
182+
183+
# solution from: https://bugs.ruby-lang.org/issues/1089#note-10
184+
idx = 0
185+
resolutions.sort_by { |x| [-x.weight, (x.fact_type == :external ? 0 : 1), idx += 1] }
177186
end
178187

179188
def find_first_real_value(resolutions)

lib/facter/resolvers/networking_resolver.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@ def post_resolve(fact_name)
1313
end
1414

1515
def read_facts(fact_name)
16-
primary_interface
1716
interfaces_data
17+
primary_interface
1818
Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
1919
@fact_list[fact_name]
2020
end
2121

2222
def primary_interface
23-
result = Facter::Core::Execution.execute('route -n get default', logger: log)
23+
primary_helper = Facter::Util::Resolvers::Networking::PrimaryInterface
24+
primary_interface ||= primary_helper.read_from_route
25+
26+
primary_interface ||= primary_helper.find_in_interfaces(@fact_list[:interfaces])
2427

25-
@fact_list[:primary_interface] = result.match(/interface: (.+)/)&.captures&.first
28+
@fact_list[:primary_interface] = primary_interface
2629
end
2730

2831
def interfaces_data

lib/facter/resolvers/solaris/networking.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# frozen_string_literal: true
22

33
require_relative 'ffi/ffi.rb'
4-
54
module Facter
65
module Resolvers
76
module Solaris
@@ -25,9 +24,14 @@ def read_facts(fact_name)
2524
obtain_info_for_interface(lifreq)
2625
end
2726

28-
@fact_list = { interfaces: @interfaces } unless @interfaces.empty?
2927
@fact_list[:primary_interface] = Facter::Util::Resolvers::Networking::PrimaryInterface.read_from_route
3028

29+
unless @interfaces.empty?
30+
@fact_list = { interfaces: @interfaces }
31+
@fact_list[:primary_interface] ||=
32+
Facter::Util::Resolvers::Networking::PrimaryInterface.find_in_interfaces(@interfaces)
33+
end
34+
3135
Facter::Util::Resolvers::Networking.expand_main_bindings(@fact_list)
3236
rescue StandardError => e
3337
@log.log_exception(e)

lib/facter/resolvers/ssh_resolver.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def retrieve_info(fact_name)
2727
next unless file_content
2828

2929
key_type, key = file_content.split(' ')
30-
ssh_list << Facter::Util::Resolvers::SshHelper.create_ssh(key_type, key)
30+
ssh = Facter::Util::Resolvers::SshHelper.create_ssh(key_type, key)
31+
ssh_list << ssh if ssh
3132
end
3233
end
3334
@fact_list[:ssh] = ssh_list

lib/facter/util/resolvers/networking/networking.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def calculate_mask_length(netmask)
7474
ipaddr.to_i.to_s(2).count('1')
7575
end
7676

77+
def format_mac_address(address)
78+
address.split('.').map { |e| format('%<mac_address>02s', mac_address: e) }.join(':').tr(' ', '0')
79+
end
80+
7781
private
7882

7983
def expand_interfaces(interfaces)

0 commit comments

Comments
 (0)