-
Notifications
You must be signed in to change notification settings - Fork 496
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(FACT-3474) Update facter schema and add test to ensure facts conform…
… to schema This commit: - Updates facter.yaml to include any facts added since the schema was last updated - Updates CONTRIBUTING.md so contributors know to add new facts to facter.yaml schema - Adds an acceptance test that tests the facter output conforms to the schema on each platform
- Loading branch information
Showing
3 changed files
with
139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# How to contribute # | ||
|
||
Third-party patches are essential for keeping Puppet great. We simply can't access the huge number of platforms and myriad configurations for running Puppet. We want to keep it as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. | ||
|
||
## Adding New Facts ## | ||
|
||
When adding new facts, they need to be added to the schema[facter.yaml]. The fact name, description, and type must be specified in the schema[facter.yaml]. | ||
|
||
Learn more about how to contribute in our Contribution Guidelines[https://github.com/puppetlabs/.github/blob/main/CONTRIBUTING.md]. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
test_name "Validate facter output conforms to schema" do | ||
tag 'risk:high' | ||
|
||
require 'yaml' | ||
require 'ipaddr' | ||
|
||
# Validates passed in output facts correctly conform to the facter schema, facter.yaml. | ||
# @param schema_fact The schema fact that matches/corresponds with output_fact | ||
# @param schema_fact_value The fact value for the schema fact | ||
# @param output_fact The fact that is being validated | ||
# @param output_fact The fact value of the output_fact | ||
def validate_fact(schema_fact, schema_fact_value, output_fact, output_fact_value) | ||
fact_type = output_fact_value.class.to_s.downcase | ||
schema_fact_type = schema_fact ? schema_fact_value["type"] : nil | ||
|
||
# For each fact, it is validated by verifying that output_fact_value can | ||
# successfully parse to fact_type and the output fact has a matching schema | ||
# fact where both their types and name or regex match. | ||
if fact_type == "hash" | ||
Hash(output_fact_value) | ||
fact_type = "map" | ||
elsif fact_type == "array" | ||
Array(output_fact_value) | ||
elsif fact_type == "trueclass" || fact_type == "falseclass" | ||
fact_type = "boolean" | ||
elsif fact_type == "float" | ||
Float(output_fact_value) | ||
fact_type = "double" | ||
elsif fact_type == "integer" | ||
Integer(output_fact_value) | ||
elsif fact_type == "string" | ||
if schema_fact_type == "ip" | ||
fail_test("Invalid ipv4 value given for #{output_fact} with value #{output_fact_value}") unless IPAddr.new(output_fact_value).ipv4? | ||
fact_type = "ip" | ||
elsif schema_fact_type == "ip6" | ||
fail_test("Invalid ipv6 value given for #{output_fact} with value #{output_fact_value}") unless IPAddr.new(output_fact_value).ipv6? | ||
fact_type = "ip6" | ||
elsif schema_fact_type == "mac" | ||
mac_regex = Regexp.new('^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$') | ||
fail_test("Invalid mac value given for #{output_fact} with value #{output_fact_value}") unless mac_regex.match?(output_fact_value) | ||
fact_type = "mac" | ||
else | ||
String(output_fact_value) | ||
end | ||
else | ||
fail_test("Invalid fact type given: #{fact_type}") | ||
end | ||
|
||
# Recurse over facts that have more nested facts within it | ||
if fact_type == "map" | ||
binding.irb | ||
if output_fact_value.is_a?(Hash) | ||
schema_elements = schema_fact_value["elements"] | ||
output_fact_value.each do |fact, value| | ||
if value.nil? || !schema_elements | ||
next | ||
# Sometimes facts with map as their type aren't nested facts, like | ||
# hypervisors and simply just a fact with a hash value. For these | ||
# cases, they don't need to be iterated over. | ||
end | ||
schema_fact, schema_fact_value = get_fact(schema_elements, fact) | ||
validate_fact(schema_fact, schema_fact_value, fact, value) | ||
end | ||
end | ||
end | ||
assert_match(fact_type, schema_fact_type, "#{output_fact} has value: #{output_fact_value} and type: #{fact_type} does not conform to schema fact value type: #{schema_fact_type}") | ||
end | ||
|
||
# @param fact_hash The hash being searched for the passed in fact_name | ||
# @param fact_name The fact that is being searched for | ||
# @return The fact that has the same name as fact_name, if found. If not found, nil is returned. | ||
def get_fact(fact_hash, fact_name) | ||
fact_hash.each_key do |fact| | ||
|
||
# Some facts, like disks.<devicename>, will have different names depending | ||
# on the machine its running on. For these facts, a pattern AKA a regex is | ||
# provided in the facter schema. | ||
fact_pattern = fact_hash[fact]["pattern"] | ||
fact_regex = fact_pattern ? Regexp.new(fact_pattern) : nil | ||
if (fact_pattern && fact_regex.match?(fact_name)) || fact_name == fact | ||
return fact, fact_hash[fact] | ||
end | ||
end | ||
return nil | ||
end | ||
|
||
step 'Validate fact collection conforms to schema' do | ||
agents.each do |agent| | ||
|
||
# Load schema to compare to output_facts | ||
schema_file = File.join(File.dirname(__FILE__), '../../../lib/schema/facter.yaml') | ||
schema = YAML.load_file(schema_file) | ||
on(agent, facter('--yaml')) do |facter_output| | ||
|
||
#get facter output for each platform | ||
output_facts = YAML.load(facter_output.stdout) | ||
|
||
# validate facter output facts match facter schema | ||
output_facts.each do |fact, value| | ||
schema_fact, schema_fact_value = get_fact(schema, fact) | ||
validate_fact(schema_fact, schema_fact_value, fact, value) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters