Module: Hiera::Backend

Defined in:
lib/hiera/backend.rb,
lib/hiera/backend/yaml_backend.rb

Defined Under Namespace

Classes: Yaml_backend

Class Method Summary collapse

Class Method Details

.datadir(backend, scope) ⇒ Object

Data lives in /var/lib/hiera by default. If a backend supplies a datadir in the config it will be used and subject to variable expansion based on scope



7
8
9
10
11
12
13
14
15
16
# File 'lib/hiera/backend.rb', line 7

def datadir(backend, scope)
    backend = backend.to_sym
    default = "/var/lib/hiera"

    if Config.include?(backend)
        parse_string(Config[backend][:datadir] || default, scope)
    else
        parse_string(default, scope)
    end
end

.datafile(backend, scope, source, extension) ⇒ Object

Finds the path to a datafile based on the Backend#datadir and extension

If the file is not found nil is returned



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/hiera/backend.rb', line 22

def datafile(backend, scope, source, extension)
    file = File.join([datadir(backend, scope), "#{source}.#{extension}"])

    unless File.exist?(file)
        Hiera.debug("Cannot find datafile #{file}, skipping")

        return nil
    end

    return file
end

.datasources(scope, override = nil, hierarchy = nil) ⇒ Object

Constructs a list of data sources to search

If you give it a specific hierarchy it will just use that else it will use the global configured one, failing that it will just look in the ‘common’ data source.

An override can be supplied that will be pre-pended to the hierarchy.

The source names will be subject to variable expansion based on scope



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/hiera/backend.rb', line 57

def datasources(scope, override=nil, hierarchy=nil)
    if hierarchy
        hierarchy = [hierarchy]
    elsif Config.include?(:hierarchy)
        hierarchy = [Config[:hierarchy]].flatten
    else
        hierarchy = ["common"]
    end

    hierarchy.insert(0, override) if override

    hierarchy.flatten.map do |source|
        source = parse_string(source, scope)
        yield(source) unless source == "" or source =~ /(^\/|\/\/|\/$)/
    end
end

.empty_answer(resolution_type) ⇒ Object

Returns an appropriate empty answer dependant on resolution type



35
36
37
38
39
40
41
42
43
44
# File 'lib/hiera/backend.rb', line 35

def empty_answer(resolution_type)
    case resolution_type
    when :array
        return []
    when :hash
        return {}
    else
        return nil
    end
end

.lookup(key, default, scope, order_override, resolution_type) ⇒ Object

Calls out to all configured backends in the order they were specified. The first one to answer will win.

This lets you declare multiple backends, a possible use case might be in Puppet where a Puppet module declares default data using in-module data while users can override using JSON/YAML etc. By layering the backends and putting the Puppet one last you can override module author data easily.

Backend instances are cached so if you need to connect to any databases then do so in your constructor, future calls to your backend will not create new instances



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/hiera/backend.rb', line 153

def lookup(key, default, scope, order_override, resolution_type)
    @backends ||= {}
    answer = nil

    Config[:backends].each do |backend|
        if constants.include?("#{backend.capitalize}_backend")
            @backends[backend] ||= Backend.const_get("#{backend.capitalize}_backend").new
            answer = @backends[backend].lookup(key, scope, order_override, resolution_type)

            break if answer
        end
    end

    answer = resolve_answer(answer, resolution_type)
    answer = parse_string(default, scope) if answer.nil?

    return default if answer == empty_answer(resolution_type)
    return answer
end

.parse_answer(data, scope, extra_data = {}) ⇒ Object

Parses a answer received from data files

Ultimately it just pass the data through parse_string but it makes some effort to handle arrays of strings as well



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/hiera/backend.rb', line 107

def parse_answer(data, scope, extra_data={})
    if data.is_a?(Numeric) or data.is_a?(TrueClass) or data.is_a?(FalseClass)
        return data
    elsif data.is_a?(String)
        return parse_string(data, scope, extra_data)
    elsif data.is_a?(Hash)
        answer = {}
        data.each_pair do |key, val|
            answer[key] = parse_answer(val, scope, extra_data)
        end

        return answer
    elsif data.is_a?(Array)
        answer = []
        data.each do |item|
            answer << parse_answer(item, scope, extra_data)
        end

        return answer
    end
end

.parse_string(data, scope, extra_data = {}) ⇒ Object

Parse a string like ‘%foo’ against a supplied scope and additional scope. If either scope or extra_scope includes the varaible ‘foo’ it will be replaced else an empty string will be placed.

If both scope and extra_data has “foo” scope will win. See hiera-puppet for an example of this to make hiera aware of additional non scope variables



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/hiera/backend.rb', line 83

def parse_string(data, scope, extra_data={})
    return nil unless data

    tdata = data.clone

    if tdata.is_a?(String)
        while tdata =~ /%\{(.+?)\}/
            var = $1
            val = scope[var] || extra_data[var] || ""

            # Puppet can return this for unknown scope vars
            val = "" if val == :undefined

            tdata.gsub!(/%\{#{var}\}/, val)
        end
    end

    return tdata
end

.resolve_answer(answer, resolution_type) ⇒ Object



129
130
131
132
133
134
135
136
137
138
# File 'lib/hiera/backend.rb', line 129

def resolve_answer(answer, resolution_type)
    case resolution_type
    when :array
        [answer].flatten.uniq.compact
    when :hash
        answer # Hash structure should be preserved
    else
        answer
    end
end