Class: Facter::Util::DotD

Inherits:
Object
  • Object
show all
Defined in:
lib/facter/facter_dot_d.rb

Overview

A Facter plugin that loads facts from /etc/facter/facts.d and /etc/puppetlabs/facter/facts.d.

Facts can be in the form of JSON, YAML or Text files and any executable that returns key=value pairs.

In the case of scripts you can also create a file that contains a cache TTL. For foo.sh store the ttl as just a number in foo.sh.ttl

The cache is stored in $libdir/facts_dot_d.cache as a mode 600 file and will have the end result of not calling your fact scripts more often than is needed

Instance Method Summary collapse

Constructor Details

#initialize(dir = '/etc/facts.d', cache_file = File.join(Puppet[:libdir], 'facts_dot_d.cache')) ⇒ DotD

Returns a new instance of DotD.



17
18
19
20
21
22
# File 'lib/facter/facter_dot_d.rb', line 17

def initialize(dir = '/etc/facts.d', cache_file = File.join(Puppet[:libdir], 'facts_dot_d.cache'))
  @dir = dir
  @cache_file = cache_file
  @cache = nil
  @types = { '.txt' => :txt, '.json' => :json, '.yaml' => :yaml }
end

Instance Method Details

#cache_lookup(file) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/facter/facter_dot_d.rb', line 126

def cache_lookup(file)
  cache = load_cache

  return nil if cache.empty?

  ttl = cache_time(file)

  return nil unless cache[file]
  now = Time.now.to_i

  return cache[file][:data] if ttl == -1
  return cache[file][:data] if (now - cache[file][:stored]) <= ttl
  return nil
rescue
  return nil
end

#cache_save!Object



113
114
115
116
117
# File 'lib/facter/facter_dot_d.rb', line 113

def cache_save!
  cache = load_cache
  File.open(@cache_file, 'w', 0o600) { |f| f.write(YAML.dump(cache)) }
rescue # rubocop:disable Lint/HandleExceptions
end

#cache_store(file, data) ⇒ Object



119
120
121
122
123
124
# File 'lib/facter/facter_dot_d.rb', line 119

def cache_store(file, data)
  load_cache

  @cache[file] = { :data => data, :stored => Time.now.to_i }
rescue # rubocop:disable Lint/HandleExceptions
end

#cache_time(file) ⇒ Object



143
144
145
146
147
148
149
# File 'lib/facter/facter_dot_d.rb', line 143

def cache_time(file)
  meta = file + '.ttl'

  return File.read(meta).chomp.to_i
rescue
  return 0
end

#createObject



164
165
166
167
168
169
170
171
172
173
174
# File 'lib/facter/facter_dot_d.rb', line 164

def create
  entries.each do |fact|
    type = fact_type(fact)
    parser = "#{type}_parser"

    next unless respond_to?("#{type}_parser")
    Facter.debug("Parsing #{fact} using #{parser}")

    send(parser, fact)
  end
end

#entriesObject



24
25
26
27
28
# File 'lib/facter/facter_dot_d.rb', line 24

def entries
  Dir.entries(@dir).reject { |f| f =~ %r{^\.|\.ttl$} }.sort.map { |f| File.join(@dir, f) }
rescue
  []
end

#fact_type(file) ⇒ Object



30
31
32
33
34
35
36
37
38
# File 'lib/facter/facter_dot_d.rb', line 30

def fact_type(file)
  extension = File.extname(file)

  type = @types[extension] || :unknown

  type = :script if type == :unknown && File.executable?(file)

  type
end

#json_parser(file) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/facter/facter_dot_d.rb', line 54

def json_parser(file)
  begin
    require 'json'
  rescue LoadError
    retry if require 'rubygems'
    raise
  end

  JSON.parse(File.read(file)).each_pair do |f, v|
    Facter.add(f) do
      setcode { v }
    end
  end
rescue StandardError => e
  Facter.warn("Failed to handle #{file} as json facts: #{e.class}: #{e}")
end

#load_cacheObject



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/facter/facter_dot_d.rb', line 151

def load_cache
  @cache ||= if File.exist?(@cache_file)
               YAML.load_file(@cache_file)
             else
               {}
             end

  return @cache
rescue
  @cache = {}
  return @cache
end

#script_parser(file) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/facter/facter_dot_d.rb', line 83

def script_parser(file)
  result = cache_lookup(file)
  ttl = cache_time(file)

  if result
    Facter.debug("Using cached data for #{file}")
  else
    result = Facter::Util::Resolution.exec(file)

    if ttl > 0
      Facter.debug("Updating cache for #{file}")
      cache_store(file, result)
      cache_save!
    end
  end

  result.split("\n").each do |line|
    next unless line =~ %r{^(.+)=(.+)$}
    var = Regexp.last_match(1)
    val = Regexp.last_match(2)

    Facter.add(var) do
      setcode { val }
    end
  end
rescue StandardError => e
  Facter.warn("Failed to handle #{file} as script facts: #{e.class}: #{e}")
  Facter.debug(e.backtrace.join("\n\t"))
end

#txt_parser(file) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/facter/facter_dot_d.rb', line 40

def txt_parser(file)
  File.readlines(file).each do |line|
    next unless line =~ %r{^([^=]+)=(.+)$}
    var = Regexp.last_match(1)
    val = Regexp.last_match(2)

    Facter.add(var) do
      setcode { val }
    end
  end
rescue StandardError => e
  Facter.warn("Failed to handle #{file} as text facts: #{e.class}: #{e}")
end

#yaml_parser(file) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
# File 'lib/facter/facter_dot_d.rb', line 71

def yaml_parser(file)
  require 'yaml'

  YAML.load_file(file).each_pair do |f, v|
    Facter.add(f) do
      setcode { v }
    end
  end
rescue StandardError => e
  Facter.warn("Failed to handle #{file} as yaml facts: #{e.class}: #{e}")
end