Skip to content

Commit

Permalink
Merge pull request #62 from nwops/external_facts
Browse files Browse the repository at this point in the history
Fixes #27 - allow loading of external fact hashes
  • Loading branch information
DavidS committed Oct 18, 2017
2 parents 369c1ec + 695e103 commit 6dbac7e
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ if facterversion = ENV['FACTER_GEM_VERSION']
gem 'facter', facterversion, :require => false
else
gem 'facter', :require => false
end
end
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ require 'facterdb'
FacterDB::get_facts('osfamily=Debian')
```

# Facter versions supported
## Facter versions supported

* 1.6
* 1.7
Expand All @@ -72,7 +72,7 @@ FacterDB::get_facts('osfamily=Debian')
* 3.7
* 3.8

# Operating Systems supported
## Operating Systems supported

* AIX
* ArchLinux
Expand Down Expand Up @@ -118,7 +118,7 @@ FacterDB::get_facts('osfamily=Debian')
* Windows 2012 r2
* Windows 7

# Add new Operating System support
## Add new Operating System support

There is `Vagrantfile` to automagically populate `facts` directory by spawning a new VM and launches a provisioning scripts.

Expand All @@ -139,3 +139,55 @@ for file in facts/*/centos-*.facts; do cat $file | sed -e 's/CentOS/RedHat/' > $
for file in facts/*/centos-*.facts; do cat $file | sed -e 's/CentOS/Scientific/' > $(echo $file | sed 's/centos/scientific/'); done
for file in facts/*/centos-*.facts; do cat $file | sed -e 's/CentOS/OracleLinux/' > $(echo $file | sed 's/centos/oraclelinux/'); done
```

## Supplying custom external facts
The default facts are great for many things but there will be times when you need to have facterdb search custom
fact sets that only make sense in your environment or might contain sensitive information.

This can be useful when combined with [rspec_puppet_facts](https://github.com/mcanevet/rspec-puppet-facts) or the [puppet-debugger](https://github.com/nwops/puppet-debugger) which both use this gem.

To supply external facts to facterdb just set the `FACTERDB_SEARCH_PATHS` environment variable with one or more
paths to your facts. Do this any time facterdb is used directly or indirectly.

When separating paths please use the default path separator character supported by your OS.
* Unix/Linux/OSX = `:`
* Windows = `;`

Each fact set you create must meet the following requirements:
1. A JSON serialized file containing a single Hash of all the facts.
2. The facts file must end in `.facts`
3. Must be placed inside some directory. You can organize this directory however you like.

Facterdb is smart enough the search your supplied directories for files ending with '.facts'. You can even supply
multiple directories.

Example:

`FACTERDB_SEARCH_PATHS="/var/opt/lib/custom_facts"`

or

`FACTERDB_SEARCH_PATHS="/var/opt/lib/custom_facts:/tmp/custom_facts:/home/user1/custom_facts"`

We still highly encourage you to create pull requests with new fact sets over of using external facts.

You can create these files via many methods.

* `puppet facts | jq '.values' > /tmp/custom_facts/datacenter_a/2.4/os_x.facts` # must have jq command
* Via puppetdb queries
* hand crafted


Additionally you can skip the default FacterDB facts completely by setting the environment variable `FACTERDB_SKIP_DEFAULTDB`.
This will instruct facterdb to not look at its built-in facts which can be useful should you need to completely replace which facts are used.


Setting the variable `FACTERDB_SKIP_DEFAULTDB` to anything will disable the internal facts used by facterdb. You would most likely use this in combination
with the `FACTERDB_SEARCH_PATHS` environment variable.

Example:

```
FACTERDB_SEARCH_PATHS="/var/opt/lib/custom_facts:/tmp/custom_facts:/home/user1/custom_facts"
FACTERDB_SKIP_DEFAULTDB='yes'
```
1 change: 1 addition & 0 deletions facterdb.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Gem::Specification.new do |s|

s.add_development_dependency 'coveralls'
s.add_development_dependency 'rake'
s.add_development_dependency 'pry'
s.add_development_dependency 'rspec'
s.add_development_dependency 'github_changelog_generator', '~> 1.10', '< 1.10.4'
s.add_runtime_dependency 'json' if RUBY_VERSION =~ /^1\.8/
Expand Down
40 changes: 39 additions & 1 deletion lib/facterdb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,46 @@

module FacterDB

# @return [String] - returns a giant incomprehensible string of concatenated json data
def self.database
@database ||= "[#{Dir.glob("#{File.expand_path(File.join(File.dirname(__FILE__), '../facts'))}/*/*.facts").map { |f| File.read(f) }.join(',')}]\n"
@database ||= "[#{facterdb_fact_files.map { |f| File.read(f) }.join(',')}]\n"
end

# @return [Boolean] - returns true if we should use the default facterdb database, false otherwise
# @note If the user passes anything to the FACTERDB_SKIP_DEFAULTDB environment variable we assume
# they want to skip the default db
def self.use_defaultdb?
ENV['FACTERDB_SKIP_DEFAULTDB'].nil?
end

# @return [Array[String]] - list of all files found in the default facterdb facts path
def self.default_fact_files
return [] unless use_defaultdb?
proj_root = File.join(File.dirname(File.dirname(__FILE__)))
facts_dir = File.expand_path(File.join(proj_root, 'facts'))
Dir.glob(File.join(facts_dir, "**", '*.facts'))
end

# @return [Array[String]] - list of all files found in the user supplied facterdb facts path
# @param fact_paths [String] - a comma separated list of paths to search for fact files
def self.external_fact_files(fact_paths = ENV['FACTERDB_SEARCH_PATHS'])
fact_paths ||= ''
return [] if fact_paths.empty?
paths = fact_paths.split(File::PATH_SEPARATOR).map do |fact_path|
unless File.directory?(fact_path)
warn("[FACTERDB] Ignoring external facts path #{fact_path} as it is not a directory")
next nil
end
fact_path = fact_path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
File.join(fact_path.strip, '**', '*.facts')
end.compact
Dir.glob(paths)
end

# @return [Array[String]] - list of all files found in the default facterdb facts path and user supplied path
# @note external fact files supplied by the user will take precedence over default fact files found in this gem
def self.facterdb_fact_files
(external_fact_files + default_fact_files).uniq
end

def self.get_os_facts(facter_version='*', filter=[])
Expand Down
94 changes: 94 additions & 0 deletions spec/facterdb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,100 @@

describe 'FacterDB' do

describe 'database' do

before(:each) do
stub_const("File::PATH_SEPARATOR", ":")
ENV['FACTERDB_SKIP_DEFAULTDB'] = nil
FacterDB.instance_variable_set(:@database, nil)
end

it 'skip default facts' do
ENV['FACTERDB_SKIP_DEFAULTDB'] = 'true'
expect(FacterDB.use_defaultdb?).to eq(false)
end

it 'use default facts' do
expect(FacterDB.use_defaultdb?).to eq(true)
end

it '#external_fact_files with env variable' do
ENV['FACTERDB_SEARCH_PATHS'] = File.join(project_dir, 'facts', '2.4')
expect(FacterDB.external_fact_files.count).to be >= 50
end

it '#external_fact_files with env variable and multiple paths' do
ENV['FACTERDB_SEARCH_PATHS'] = [File.join(project_dir, 'facts', '2.4'), File.join(project_dir, 'facts', '2.3')].join(File::PATH_SEPARATOR)
expect(FacterDB.external_fact_files.count).to be >= 150
end

it '#external_fact_files with argument' do
ENV['FACTERDB_SEARCH_PATHS'] = nil
path = File.join(project_dir, 'facts', '2.4')
expect(FacterDB.external_fact_files(path).count).to be >= 102
end

it '#external_fact_files without argument or env variable' do
ENV['FACTERDB_SEARCH_PATHS'] = nil
expect(FacterDB.external_fact_files.count).to be 0
end

it '#default_fact_files' do
expect(FacterDB.default_fact_files.count).to be >= 1000
end

it '#facterdb_fact_files' do
ENV['FACTERDB_SEARCH_PATHS'] = File.join(project_dir, 'facts', '2.4')
external_count = FacterDB.external_fact_files.count
internal_count = FacterDB.default_fact_files.count
# because we call unique on the array we remove the duplicates which skews this test
expect(FacterDB.facterdb_fact_files.count).to eq(internal_count)
end

it '#database' do
ENV['FACTERDB_SEARCH_PATHS'] = nil
# to be an incomprehensible string
expect(FacterDB.database).to be_a String
end

it '#database with external paths' do
ENV['FACTERDB_SEARCH_PATHS'] = File.join(project_dir, 'facts', '2.4')
# to be an incomprehensible string
expect(FacterDB.database).to be_a String
end

it 'without internal paths' do
ENV['FACTERDB_SKIP_DEFAULTDB'] = 'true'
ENV['FACTERDB_SEARCH_PATHS'] = nil
expect(FacterDB.facterdb_fact_files).to eq([])
end

describe :windows do
before(:each) do
stub_const("File::PATH_SEPARATOR", ";")
stub_const("File::ALT_SEPARATOR", '\\')
stub_const("File::SEPARATOR", '/')
allow(File).to receive(:directory?).and_return(true)
end

it '#external_fact_files with env variable and multiple paths' do
ENV['FACTERDB_SEARCH_PATHS'] = [File.join(project_dir, 'facts', '2.4').gsub('/', '\\'), File.join(project_dir, 'facts', '2.3').gsub('/', '\\')].join(File::PATH_SEPARATOR)
expect(FacterDB.external_fact_files.count).to be >= 150
end

it '#external_fact_files with env variable' do
ENV['FACTERDB_SEARCH_PATHS'] = File.join(project_dir, 'facts', '2.4').gsub('/', '\\')
expect(FacterDB.external_fact_files.count).to be >= 50
end

it '#external_fact_files with argument' do
ENV['FACTERDB_SEARCH_PATHS'] = nil
path = File.join(project_dir, 'facts', '2.4').gsub('/', '\\')
expect(FacterDB.external_fact_files(path).count).to be >= 102
end
end
end

describe '#get_os_facts' do
context 'without parameters' do
subject { FacterDB.get_os_facts() }
Expand Down
4 changes: 4 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
require 'rspec'
require 'facterdb'
include FacterDB

def project_dir
File.dirname File.dirname(File.expand_path(__FILE__))
end

0 comments on commit 6dbac7e

Please sign in to comment.