Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow loading of external fact hashes #62

Merged
merged 1 commit into from
Oct 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to get rubocop involved as it would have changed a whole lot of code. But now that this is merged, let the games begin. Also we need to bump the version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rubocop++, and I would suggest the PDK template, obviously. But final say is with @mcanevet and @raphink .

the version gets bumped during release-prep in accordance with the changes in the release.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DavidS I didn't have time to check PDK yet but feel free to add this in this module. Ping me onde it's OK for release.

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