1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
|
require 'spec_helper'
require 'puppet_spec/compiler'
describe "when using a hiera data provider" do
include PuppetSpec::Compiler
# There is a fully configured 'sample' environment in fixtures at this location
let(:environmentpath) { parent_fixture('environments') }
let(:facts) { Puppet::Node::Facts.new("facts", {}) }
around(:each) do |example|
# Initialize settings to get a full compile as close as possible to a real
# environment load
Puppet.settings.initialize_global_settings
# Initialize loaders based on the environmentpath. It does not work to
# just set the setting environmentpath for some reason - this achieves the same:
# - first a loader is created, loading directory environments from the fixture (there is
# one environment, 'sample', which will be loaded since the node references this
# environment by name).
# - secondly, the created env loader is set as 'environments' in the puppet context.
#
loader = Puppet::Environments::Directories.new(environmentpath, [])
Puppet.override(:environments => loader) do
example.run
end
end
def compile_and_get_notifications(environment, code = nil)
extract_notifications(compile(environment, code))
end
def compile(environment, code = nil)
Puppet[:code] = code if code
node = Puppet::Node.new("testnode", :facts => facts, :environment => environment)
compiler = Puppet::Parser::Compiler.new(node)
compiler.topscope['domain'] = 'example.com'
block_given? ? compiler.compile { |catalog| yield(compiler); catalog } : compiler.compile
end
def extract_notifications(catalog)
catalog.resources.map(&:ref).select { |r| r.start_with?('Notify[') }.map { |r| r[7..-2] }
end
it 'uses default configuration for environment and module data' do
resources = compile_and_get_notifications('hiera_defaults')
expect(resources).to include('module data param_a is 100, param default is 200, env data param_c is 300')
end
it 'reads hiera.yaml in environment root and configures multiple json and yaml providers' do
resources = compile_and_get_notifications('hiera_env_config')
expect(resources).to include("env data param_a is 10, env data param_b is 20, env data param_c is 30, env data param_d is 40, env data param_e is 50, env data param_yaml_utf8 is \u16EB\u16D2\u16E6, env data param_json_utf8 is \u16A0\u16C7\u16BB")
end
it 'reads hiera.yaml in module root and configures multiple json and yaml providers' do
resources = compile_and_get_notifications('hiera_module_config')
expect(resources).to include('module data param_a is 100, module data param_b is 200, module data param_c is 300, module data param_d is 400, module data param_e is 500')
end
it 'keeps lookup_options in one module separate from lookup_options in another' do
resources1 = compile('hiera_modules', 'include one').resources.select {|r| r.ref.start_with?('Class[One]')}
resources2 = compile('hiera_modules', 'include two').resources.select {|r| r.ref.start_with?('Class[One]')}
expect(resources1).to eq(resources2)
end
it 'does not perform merge of values declared in environment and module when resolving parameters' do
resources = compile_and_get_notifications('hiera_misc')
expect(resources).to include('env 1, ')
end
it 'performs hash merge of values declared in environment and module' do
resources = compile_and_get_notifications('hiera_misc', '$r = lookup(one::test::param, Hash[String,String], hash) notify{"${r[key1]}, ${r[key2]}":}')
expect(resources).to include('env 1, module 2')
end
it 'performs unique merge of values declared in environment and module' do
resources = compile_and_get_notifications('hiera_misc', '$r = lookup(one::array, Array[String], unique) notify{"${r}":}')
expect(resources.size).to eq(1)
expect(resources[0][1..-2].split(', ')).to contain_exactly('first', 'second', 'third', 'fourth')
end
it 'performs merge found in lookup_options in environment of values declared in environment and module' do
resources = compile_and_get_notifications('hiera_misc', 'include one::lopts_test')
expect(resources.size).to eq(1)
expect(resources[0]).to eq('A, B, C, MA, MB, MC')
end
it 'performs merge found in lookup_options in module of values declared in environment and module' do
resources = compile_and_get_notifications('hiera_misc', 'include one::loptsm_test')
expect(resources.size).to eq(1)
expect(resources[0]).to eq('A, B, C, MA, MB, MC')
end
it "will not find 'lookup_options' as a regular value" do
expect { compile_and_get_notifications('hiera_misc', '$r = lookup("lookup_options")') }.to raise_error(Puppet::DataBinding::LookupError, /did not find a value/)
end
it 'does find unqualified keys in the environment' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(ukey1):}')
expect(resources).to include('Some value')
end
it 'does not find unqualified keys in the module' do
expect do
compile_and_get_notifications('hiera_misc', 'notify{lookup(ukey2):}')
end.to raise_error(Puppet::ParseError, /did not find a value for the name 'ukey2'/)
end
it 'can use interpolation lookup method "alias"' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_alias):}')
expect(resources).to include('Value from interpolation with alias')
end
it 'can use interpolation lookup method "lookup"' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_lookup):}')
expect(resources).to include('Value from interpolation with lookup')
end
it 'can use interpolation lookup method "hiera"' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_hiera):}')
expect(resources).to include('Value from interpolation with hiera')
end
it 'can use interpolation lookup method "literal"' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_literal):}')
expect(resources).to include('Value from interpolation with literal')
end
it 'can use interpolation lookup method "scope"' do
resources = compile_and_get_notifications('hiera_misc', '$target_scope = "with scope" notify{lookup(km_scope):}')
expect(resources).to include('Value from interpolation with scope')
end
it 'can use interpolation using default lookup method (scope)' do
resources = compile_and_get_notifications('hiera_misc', '$target_default = "with default" notify{lookup(km_default):}')
expect(resources).to include('Value from interpolation with default')
end
it 'performs lookup using qualified expressions in interpolation' do
resources = compile_and_get_notifications('hiera_misc', "$os = { name => 'Fedora' } notify{lookup(km_qualified):}")
expect(resources).to include('Value from qualified interpolation OS = Fedora')
end
it 'can have multiple interpolate expressions in one value' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_multi):}')
expect(resources).to include('cluster/%{::cluster}/%{role}')
end
it 'performs single quoted interpolation' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(km_sqalias):}')
expect(resources).to include('Value from interpolation with alias')
end
it 'uses compiler lifecycle for caching' do
Puppet[:code] = 'notify{lookup(one::my_var):}'
node = Puppet::Node.new('testnode', :facts => facts, :environment => 'hiera_module_config')
compiler = Puppet::Parser::Compiler.new(node)
compiler.topscope['my_fact'] = 'server1'
expect(extract_notifications(compiler.compile)).to include('server1')
compiler = Puppet::Parser::Compiler.new(node)
compiler.topscope['my_fact'] = 'server2'
expect(extract_notifications(compiler.compile)).to include('server2')
compiler = Puppet::Parser::Compiler.new(node)
expect(extract_notifications(compiler.compile)).to include('In name.yaml')
end
it 'traps endless interpolate recursion' do
expect do
compile_and_get_notifications('hiera_misc', '$r1 = "%{r2}" $r2 = "%{r1}" notify{lookup(recursive):}')
end.to raise_error(Puppet::DataBinding::RecursiveLookupError, /detected in \[recursive, scope:r1, scope:r2\]/)
end
it 'does not consider use of same key in the lookup and scope namespaces as recursion' do
resources = compile_and_get_notifications('hiera_misc', 'notify{lookup(domain):}')
expect(resources).to include('-- example.com --')
end
it 'traps bad alias declarations' do
expect do
compile_and_get_notifications('hiera_misc', "$r1 = 'Alias within string %{alias(\"r2\")}' $r2 = '%{r1}' notify{lookup(recursive):}")
end.to raise_error(Puppet::DataBinding::LookupError, /'alias' interpolation is only permitted if the expression is equal to the entire string/)
end
it 'reports syntax errors for JSON files' do
expect do
compile_and_get_notifications('hiera_bad_syntax_json')
end.to raise_error(Puppet::DataBinding::LookupError, /Unable to parse \(#{environmentpath}[^)]+\):/)
end
it 'reports syntax errors for YAML files' do
expect do
compile_and_get_notifications('hiera_bad_syntax_yaml')
end.to raise_error(Puppet::DataBinding::LookupError, /Unable to parse \(#{environmentpath}[^)]+\):/)
end
describe 'when using explain' do
it 'will report config path (original and resolved), data path (original and resolved), and interpolation (before and after)' do
compile('hiera_misc', '$target_scope = "with scope"') do |compiler|
lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)
Puppet::Pops::Lookup.lookup('km_scope', nil, nil, nil, nil, lookup_invocation)
expect(lookup_invocation.explainer.explain).to include(<<-EOS)
Path "#{environmentpath}/hiera_misc/data/common.yaml"
Original path: "common.yaml"
Interpolation on "Value from interpolation %{scope("target_scope")}"
Global Scope
Found key: "target_scope" value: "with scope"
Found key: "km_scope" value: "Value from interpolation with scope"
EOS
end
end
it 'will report that merge options was found in the lookup_options hash' do
compile('hiera_misc', '$target_scope = "with scope"') do |compiler|
lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, true)
Puppet::Pops::Lookup.lookup('one::loptsm_test::hash', nil, nil, nil, nil, lookup_invocation)
expect(lookup_invocation.explainer.explain).to include("Using merge options from \"lookup_options\" hash")
end
end
it 'will report lookup_options details in combination with details of found value' do
compile('hiera_misc', '$target_scope = "with scope"') do |compiler|
lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, Puppet::Pops::Lookup::Explainer.new(true))
Puppet::Pops::Lookup.lookup('one::loptsm_test::hash', nil, nil, nil, nil, lookup_invocation)
expect(lookup_invocation.explainer.explain).to eq(<<EOS)
Searching for "lookup_options"
Global Data Provider (hiera configuration version 5)
No such key: "lookup_options"
Environment Data Provider (hiera configuration version 5)
Hierarchy entry "Common"
Path "#{environmentpath}/hiera_misc/data/common.yaml"
Original path: "common.yaml"
Found key: "lookup_options" value: {
"one::lopts_test::hash" => {
"merge" => "deep"
}
}
Module "one" Data Provider (hiera configuration version 5)
Hierarchy entry "Common"
Path "#{environmentpath}/hiera_misc/modules/one/data/common.yaml"
Original path: "common.yaml"
Found key: "lookup_options" value: {
"one::loptsm_test::hash" => {
"merge" => "deep"
}
}
Merge strategy hash
Global and Environment
Found key: "lookup_options" value: {
"one::lopts_test::hash" => {
"merge" => "deep"
}
}
Module one
Found key: "lookup_options" value: {
"one::loptsm_test::hash" => {
"merge" => "deep"
}
}
Merged result: {
"one::loptsm_test::hash" => {
"merge" => "deep"
},
"one::lopts_test::hash" => {
"merge" => "deep"
}
}
Using merge options from "lookup_options" hash
Searching for "one::loptsm_test::hash"
Merge strategy deep
Global Data Provider (hiera configuration version 5)
No such key: "one::loptsm_test::hash"
Environment Data Provider (hiera configuration version 5)
Hierarchy entry "Common"
Path "#{environmentpath}/hiera_misc/data/common.yaml"
Original path: "common.yaml"
Found key: "one::loptsm_test::hash" value: {
"a" => "A",
"b" => "B",
"m" => {
"ma" => "MA",
"mb" => "MB"
}
}
Module "one" Data Provider (hiera configuration version 5)
Hierarchy entry "Common"
Path "#{environmentpath}/hiera_misc/modules/one/data/common.yaml"
Original path: "common.yaml"
Found key: "one::loptsm_test::hash" value: {
"a" => "A",
"c" => "C",
"m" => {
"ma" => "MA",
"mc" => "MC"
}
}
Merged result: {
"a" => "A",
"c" => "C",
"m" => {
"ma" => "MA",
"mc" => "MC",
"mb" => "MB"
},
"b" => "B"
}
EOS
end
end
it 'will report config path (original and resolved), data path (original and resolved), and interpolation (before and after)' do
compile('hiera_misc', '$target_scope = "with scope"') do |compiler|
lookup_invocation = Puppet::Pops::Lookup::Invocation.new(compiler.topscope, {}, {}, Puppet::Pops::Lookup::Explainer.new(true, true))
Puppet::Pops::Lookup.lookup('one::loptsm_test::hash', nil, nil, nil, nil, lookup_invocation)
expect(lookup_invocation.explainer.explain).to eq(<<EOS)
Merge strategy hash
Global Data Provider (hiera configuration version 5)
No such key: "lookup_options"
Environment Data Provider (hiera configuration version 5)
Hierarchy entry "Common"
Path "#{environmentpath}/hiera_misc/data/common.yaml"
Original path: "common.yaml"
Found key: "lookup_options" value: {
"one::lopts_test::hash" => {
"merge" => "deep"
}
}
Module "one" Data Provider (hiera configuration version 5)
Hierarchy entry "Common"
Path "#{environmentpath}/hiera_misc/modules/one/data/common.yaml"
Original path: "common.yaml"
Found key: "lookup_options" value: {
"one::loptsm_test::hash" => {
"merge" => "deep"
}
}
Merged result: {
"one::loptsm_test::hash" => {
"merge" => "deep"
},
"one::lopts_test::hash" => {
"merge" => "deep"
}
}
EOS
end
end
end
def parent_fixture(dir_name)
File.absolute_path(File.join(my_fixture_dir(), "../#{dir_name}"))
end
def resources_in(catalog)
catalog.resources.map(&:ref)
end
end
|