Skip to content

Commit

Permalink
(FACT-2046) Integrate custom facts (puppetlabs#76)
Browse files Browse the repository at this point in the history
* (FACT-2046) Add custom fact manager and move logic for core facts in core fact manager.
* (FACT-2043) Add external fact.
* (FACT-2046) Add method for retriving custom facts.
* (FACT-2046) Add Symbol as accepted type for normalization.
* (FACT-2046) Add prototype integration for custom facts.
* (FACT-2046) Add fact loaders.
* (FACT-2046) Add custom fact loader.
* (FACT-2046) Remove core fact loader and legacy fact loader.
* (FACT-2046) Load core facts when no user query is provided.
* (FACT-2046) Add merging mechanism for core and custom facts.
* (FACT-2046) Fix rubocop.
* (FACT-2046) Add tests for fact loader.
* (FACT-2046) Add class discoverer tests.
* (FACT-2046) Add query parser tests.
* (FACT-2046) Add custom fact loader spec.
* (FACT-2046) Remove duplicated module.
* (FACT-2046) Fix rubocop.
* (FACT-2046) Add tests for facter.
* (FACT-2046) Allow symbols in normalization.
* (FACT-2046) Fix rubocop.
* (FACT-2046) Add tests for fact manager.
* (FACT-2046)  Add method for creation of searched fact for custom fact. Fix rubocop.
* (FACT-2046) Invalidate cache before running os release resolver test.
* (FACT-2046) Remove commented code.
* (FACT-2046) Add tests for core fact manager.
* (FACT-2046) Add tests for custom fact manager.
* (FACT-2046) Extract logic for calling fact classes from core fact manager.
* (FACT-2046) Load custom and external facts.
* (FACT-2046) Rename FactLoader to InternalFactLoader and CustomFactLoader to ExternalFactLoader.
* (FACT-2046) Implement the same interface in all loaders.
* (FACT-2046) Add method to extract external facts from fact collection. Load external facts independent of custom facts.
* (FACT-2046) Remove os detection from fact manager and place it inside internal fact loader.
* (FACT-2046) Load all internal facts at once.
  • Loading branch information
Bogdan Irimie authored and sebastian-miclea committed Oct 10, 2019
1 parent aee2851 commit baf5afc
Show file tree
Hide file tree
Showing 32 changed files with 741 additions and 315 deletions.
9 changes: 9 additions & 0 deletions custom_facts/my_custom_fact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

LegacyFacter.add(:my_custom_fact) do
has_weight(10_000)
setcode do
# 'my_custom_fact'
LegacyFacter.value('os')
end
end
8 changes: 8 additions & 0 deletions custom_facts/my_custom_os_fact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

LegacyFacter.add(:os) do
has_weight(10_000)
setcode do
'my_custom_os'
end
end
1 change: 1 addition & 0 deletions external_facts/my_external_fact.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
my_external_fact=123
22 changes: 22 additions & 0 deletions lib/custom_facts/util/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@ def list
@facts.keys
end

# Build a hash of external facts
def external_facts
return unless @external_facts.nil?

facts_before_load = @facts.clone
load_external_facts

@external_facts = @facts.dup.delete_if { |k, _| facts_before_load.key?(k) }
end

# Builds a hash of custom facts
def custom_facts
return unless @custom_facts.nil?

facts_before_load = @facts.clone
internal_loader.load_all

@custom_facts = @facts.dup.delete_if { |k, _| facts_before_load.key?(k) }
end

def load(name)
internal_loader.load(name)
load_external_facts
Expand Down Expand Up @@ -117,6 +137,8 @@ def to_hash

def value(name)
fact = fact(name)
return Facter.core_value(name) if fact.nil?

fact&.value
end

Expand Down
4 changes: 2 additions & 2 deletions lib/custom_facts/util/normalization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Util
module Normalization
class NormalizationError < StandardError; end

VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, String, Array, Hash].freeze
VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, Symbol, String, Array, Hash].freeze

module_function

Expand All @@ -16,7 +16,7 @@ class NormalizationError < StandardError; end
# @return [void]
def normalize(value)
case value
when Integer, Float, TrueClass, FalseClass, NilClass
when Integer, Float, TrueClass, FalseClass, NilClass, Symbol
value
when String
normalize_string(value)
Expand Down
17 changes: 12 additions & 5 deletions lib/facter-ng.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,30 @@

ROOT_DIR = Pathname.new(File.expand_path('..', __dir__)) unless defined?(ROOT_DIR)
require "#{ROOT_DIR}/lib/framework/core/file_loader"
require "#{ROOT_DIR}/lib/framework/core/facter"

module Facter
def self.to_hash
resolved_facts = Facter::Base.new.resolve_facts
resolved_facts = Facter::FactManager.instance.resolve_facts
ResolverManager.invalidate_all_caches
FactCollection.new.build_fact_collection!(resolved_facts)
end

def self.to_user_output(options, *args)
resolved_facts = Facter::Base.new.resolve_facts(options, args)
resolved_facts = Facter::FactManager.instance.resolve_facts(options, args)
ResolverManager.invalidate_all_caches
fact_formatter = Facter::FormatterFactory.build(options)
fact_formatter.format(resolved_facts)
end

def self.value(user_query)
user_query = user_query.to_s
resolved_facts = Facter::Base.new.resolve_facts({}, [user_query])
resolved_facts = Facter::FactManager.instance.resolve_facts({}, [user_query])
ResolverManager.invalidate_all_caches
fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)
fact_collection.dig(*user_query.split('.'))
end

def self.core_value(user_query)
resolved_facts = Facter::FactManager.instance.resolve_core({}, [user_query])
fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)
fact_collection.dig(*user_query.split('.'))
end
Expand Down
40 changes: 40 additions & 0 deletions lib/framework/core/core_fact_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

module Facter
class CoreFactManager
def resolve_facts(searched_facts)
threads = start_threads(searched_facts)
resolved_facts = join_threads(threads, searched_facts)

resolved_facts
end

private

def start_threads(searched_facts)
threads = []

searched_facts.reject { |elem| elem.fact_class.nil? }.each do |searched_fact|
threads << Thread.new do
fact = Facter::FactFactory.build(searched_fact)
fact.create
end
end

threads
end

def join_threads(threads, searched_facts)
resolved_facts = []

threads.each do |thread|
thread.join
resolved_facts << thread.value
end

resolved_facts.flatten!

FactAugmenter.augment_resolved_facts(searched_facts, resolved_facts)
end
end
end
29 changes: 29 additions & 0 deletions lib/framework/core/custom_fact_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module Facter
class CustomFactManager
def resolve_facts(searched_facts)
custom_facts(searched_facts)
end

private

def custom_facts(custom_facts)
LegacyFacter.search("#{ROOT_DIR}/custom_facts")
LegacyFacter.search_external(["#{ROOT_DIR}/external_facts"])

resolved_custom_facts = []

custom_facts.each do |custom_fact|
fact_value = LegacyFacter.value(custom_fact.name)
resolved_fact = ResolvedFact.new(custom_fact.name, fact_value)
resolved_fact.filter_tokens = []
resolved_fact.user_query = custom_fact.user_query

resolved_custom_facts << resolved_fact
end

resolved_custom_facts
end
end
end
15 changes: 15 additions & 0 deletions lib/framework/core/fact/core_fact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Facter
class CoreFact
def initialize(searched_fact)
@searched_fact = searched_fact
end

def create
fact_class = @searched_fact.fact_class

fact_class.new.call_the_resolver
end
end
end
13 changes: 13 additions & 0 deletions lib/framework/core/fact/fact_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Facter
class FactFactory
def self.build(searched_fact)
if searched_fact.name.include?('.*')
LegacyFact.new(searched_fact)
else
CoreFact.new(searched_fact)
end
end
end
end
24 changes: 24 additions & 0 deletions lib/framework/core/fact/legacy_fact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Facter
class LegacyFact
def initialize(searched_fact)
@searched_fact = searched_fact
end

def create
fact_class = @searched_fact.fact_class
filter_criteria = extract_filter_criteria(@searched_fact)

fact_class.new.call_the_resolver(filter_criteria)
end

Trimmer = Struct.new(:start, :end)
def extract_filter_criteria(searched_fact)
name_tokens = searched_fact.name.split('.*')
trimmer = Trimmer.new(name_tokens[0].length, -(name_tokens[1] || '').length - 1)

searched_fact.user_query[trimmer.start..trimmer.end]
end
end
end
63 changes: 0 additions & 63 deletions lib/framework/core/fact_loader.rb

This file was deleted.

14 changes: 14 additions & 0 deletions lib/framework/core/fact_loaders/class_discoverer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module Facter
class ClassDiscoverer
include Singleton

def discover_classes(operating_system)
os_module_name = Module.const_get("Facter::#{operating_system}")

# select only classes
os_module_name.constants.select { |c| os_module_name.const_get(c).is_a? Class }
end
end
end
24 changes: 24 additions & 0 deletions lib/framework/core/fact_loaders/external_fact_loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Facter
class ExternalFactLoader
attr_reader :custom_facts, :external_facts, :facts

def initialize
LegacyFacter.search("#{ROOT_DIR}/custom_facts")
LegacyFacter.search_external(["#{ROOT_DIR}/external_facts"])

custom_facts_to_load = LegacyFacter.collection.custom_facts
external_facts_to_load = LegacyFacter.collection.external_facts

@custom_facts = {}
@external_facts = {}
@facts = {}

custom_facts_to_load.each { |k, _v| @custom_facts.merge!(k.to_s => nil) }
external_facts_to_load.each { |k, _v| @external_facts.merge!(k.to_s => nil) }

@facts = @custom_facts.merge(@external_facts)
end
end
end
43 changes: 43 additions & 0 deletions lib/framework/core/fact_loaders/internal_fact_loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

module Facter
class InternalFactLoader
attr_reader :core_facts, :legacy_facts, :facts

def initialize
@core_facts = {}
@legacy_facts = {}
@facts = {}

load
end

private

def load
os = CurrentOs.instance.identifier.capitalize

# select only classes
classes = ClassDiscoverer.instance.discover_classes(os)

classes.each do |class_name|
klass = Class.const_get("Facter::#{os}::" + class_name.to_s)
fact_name = klass::FACT_NAME

if legacy_fact?(klass)
@legacy_facts.merge!(fact_name => klass)
else
@core_facts.merge!(fact_name => klass)
end
end

@facts = @legacy_facts.merge(@core_facts)

@facts
end

def legacy_fact?(klass)
klass.const_defined?('FACT_TYPE') && klass::FACT_TYPE.equal?(:legacy)
end
end
end
Loading

0 comments on commit baf5afc

Please sign in to comment.