1""" 2 :codeauthor: Pedro Algarvio (pedro@algarvio.me) 3 :codeauthor: Alexandru Bleotu (alexandru.bleotu@morganstanley.com) 4 5 tests.unit.pillar_test 6 ~~~~~~~~~~~~~~~~~~~~~~ 7""" 8 9 10import logging 11import os 12import shutil 13import tempfile 14import textwrap 15 16import salt.config 17import salt.exceptions 18import salt.fileclient 19import salt.utils.stringutils 20from salt.utils.files import fopen 21from tests.support.helpers import with_tempdir 22from tests.support.mock import MagicMock, patch 23from tests.support.runtests import RUNTIME_VARS 24from tests.support.unit import TestCase 25 26log = logging.getLogger(__name__) 27 28 29class MockFileclient: 30 def __init__(self, cache_file=None, get_state=None, list_states=None): 31 if cache_file is not None: 32 self.cache_file = lambda *x, **y: cache_file 33 if get_state is not None: 34 self.get_state = lambda sls, env: get_state[sls] 35 if list_states is not None: 36 self.list_states = lambda *x, **y: list_states 37 38 # pylint: disable=unused-argument,no-method-argument,method-hidden 39 def cache_file(*args, **kwargs): 40 raise NotImplementedError() 41 42 def get_state(*args, **kwargs): 43 raise NotImplementedError() 44 45 def list_states(*args, **kwargs): 46 raise NotImplementedError() 47 48 # pylint: enable=unused-argument,no-method-argument,method-hidden 49 50 51class PillarTestCase(TestCase): 52 def tearDown(self): 53 for attrname in ( 54 "generic_file", 55 "generic_minion_file", 56 "ssh_file", 57 "ssh_minion_file", 58 "top_file", 59 ): 60 try: 61 delattr(self, attrname) 62 except AttributeError: 63 continue 64 65 def test_pillarenv_from_saltenv(self): 66 with patch("salt.pillar.compile_template") as compile_template: 67 opts = { 68 "optimization_order": [0, 1, 2], 69 "renderer": "json", 70 "renderer_blacklist": [], 71 "renderer_whitelist": [], 72 "state_top": "", 73 "pillar_roots": {"dev": [], "base": []}, 74 "file_roots": {"dev": [], "base": []}, 75 "extension_modules": "", 76 "pillarenv_from_saltenv": True, 77 } 78 grains = { 79 "os": "Ubuntu", 80 } 81 pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "dev") 82 self.assertEqual(pillar.opts["saltenv"], "dev") 83 self.assertEqual(pillar.opts["pillarenv"], "dev") 84 85 def test_ext_pillar_no_extra_minion_data_val_dict(self): 86 opts = { 87 "optimization_order": [0, 1, 2], 88 "renderer": "json", 89 "renderer_blacklist": [], 90 "renderer_whitelist": [], 91 "state_top": "", 92 "pillar_roots": {"dev": [], "base": []}, 93 "file_roots": {"dev": [], "base": []}, 94 "extension_modules": "", 95 "pillarenv_from_saltenv": True, 96 } 97 mock_ext_pillar_func = MagicMock() 98 with patch( 99 "salt.loader.pillars", 100 MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), 101 ): 102 pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev") 103 # ext pillar function doesn't have the extra_minion_data arg 104 with patch( 105 "salt.utils.args.get_function_argspec", 106 MagicMock(return_value=MagicMock(args=[])), 107 ): 108 pillar._external_pillar_data( 109 "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" 110 ) 111 mock_ext_pillar_func.assert_called_once_with( 112 "mocked-minion", "fake_pillar", arg="foo" 113 ) 114 # ext pillar function has the extra_minion_data arg 115 mock_ext_pillar_func.reset_mock() 116 with patch( 117 "salt.utils.args.get_function_argspec", 118 MagicMock(return_value=MagicMock(args=["extra_minion_data"])), 119 ): 120 pillar._external_pillar_data( 121 "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" 122 ) 123 mock_ext_pillar_func.assert_called_once_with( 124 "mocked-minion", "fake_pillar", arg="foo" 125 ) 126 127 def test_ext_pillar_no_extra_minion_data_val_list(self): 128 opts = { 129 "optimization_order": [0, 1, 2], 130 "renderer": "json", 131 "renderer_blacklist": [], 132 "renderer_whitelist": [], 133 "state_top": "", 134 "pillar_roots": {"dev": [], "base": []}, 135 "file_roots": {"dev": [], "base": []}, 136 "extension_modules": "", 137 "pillarenv_from_saltenv": True, 138 } 139 mock_ext_pillar_func = MagicMock() 140 with patch( 141 "salt.loader.pillars", 142 MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), 143 ): 144 pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev") 145 # ext pillar function doesn't have the extra_minion_data arg 146 with patch( 147 "salt.utils.args.get_function_argspec", 148 MagicMock(return_value=MagicMock(args=[])), 149 ): 150 pillar._external_pillar_data("fake_pillar", ["foo"], "fake_ext_pillar") 151 mock_ext_pillar_func.assert_called_once_with( 152 "mocked-minion", "fake_pillar", "foo" 153 ) 154 # ext pillar function has the extra_minion_data arg 155 mock_ext_pillar_func.reset_mock() 156 with patch( 157 "salt.utils.args.get_function_argspec", 158 MagicMock(return_value=MagicMock(args=["extra_minion_data"])), 159 ): 160 pillar._external_pillar_data("fake_pillar", ["foo"], "fake_ext_pillar") 161 mock_ext_pillar_func.assert_called_once_with( 162 "mocked-minion", "fake_pillar", "foo" 163 ) 164 165 def test_ext_pillar_no_extra_minion_data_val_elem(self): 166 opts = { 167 "optimization_order": [0, 1, 2], 168 "renderer": "json", 169 "renderer_blacklist": [], 170 "renderer_whitelist": [], 171 "state_top": "", 172 "pillar_roots": {"dev": [], "base": []}, 173 "file_roots": {"dev": [], "base": []}, 174 "extension_modules": "", 175 "pillarenv_from_saltenv": True, 176 } 177 mock_ext_pillar_func = MagicMock() 178 with patch( 179 "salt.loader.pillars", 180 MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), 181 ): 182 pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev") 183 # ext pillar function doesn't have the extra_minion_data arg 184 with patch( 185 "salt.utils.args.get_function_argspec", 186 MagicMock(return_value=MagicMock(args=[])), 187 ): 188 pillar._external_pillar_data("fake_pillar", "fake_val", "fake_ext_pillar") 189 mock_ext_pillar_func.assert_called_once_with( 190 "mocked-minion", "fake_pillar", "fake_val" 191 ) 192 # ext pillar function has the extra_minion_data arg 193 mock_ext_pillar_func.reset_mock() 194 with patch( 195 "salt.utils.args.get_function_argspec", 196 MagicMock(return_value=MagicMock(args=["extra_minion_data"])), 197 ): 198 pillar._external_pillar_data("fake_pillar", "fake_val", "fake_ext_pillar") 199 mock_ext_pillar_func.assert_called_once_with( 200 "mocked-minion", "fake_pillar", "fake_val" 201 ) 202 203 def test_ext_pillar_with_extra_minion_data_val_dict(self): 204 opts = { 205 "optimization_order": [0, 1, 2], 206 "renderer": "json", 207 "renderer_blacklist": [], 208 "renderer_whitelist": [], 209 "state_top": "", 210 "pillar_roots": {"dev": [], "base": []}, 211 "file_roots": {"dev": [], "base": []}, 212 "extension_modules": "", 213 "pillarenv_from_saltenv": True, 214 } 215 mock_ext_pillar_func = MagicMock() 216 with patch( 217 "salt.loader.pillars", 218 MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), 219 ): 220 pillar = salt.pillar.Pillar( 221 opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"} 222 ) 223 # ext pillar function doesn't have the extra_minion_data arg 224 with patch( 225 "salt.utils.args.get_function_argspec", 226 MagicMock(return_value=MagicMock(args=[])), 227 ): 228 pillar._external_pillar_data( 229 "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" 230 ) 231 mock_ext_pillar_func.assert_called_once_with( 232 "mocked-minion", "fake_pillar", arg="foo" 233 ) 234 # ext pillar function has the extra_minion_data arg 235 mock_ext_pillar_func.reset_mock() 236 with patch( 237 "salt.utils.args.get_function_argspec", 238 MagicMock(return_value=MagicMock(args=["extra_minion_data"])), 239 ): 240 pillar._external_pillar_data( 241 "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" 242 ) 243 mock_ext_pillar_func.assert_called_once_with( 244 "mocked-minion", 245 "fake_pillar", 246 arg="foo", 247 extra_minion_data={"fake_key": "foo"}, 248 ) 249 250 def test_ext_pillar_with_extra_minion_data_val_list(self): 251 opts = { 252 "optimization_order": [0, 1, 2], 253 "renderer": "json", 254 "renderer_blacklist": [], 255 "renderer_whitelist": [], 256 "state_top": "", 257 "pillar_roots": {"dev": [], "base": []}, 258 "file_roots": {"dev": [], "base": []}, 259 "extension_modules": "", 260 "pillarenv_from_saltenv": True, 261 } 262 mock_ext_pillar_func = MagicMock() 263 with patch( 264 "salt.loader.pillars", 265 MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), 266 ): 267 pillar = salt.pillar.Pillar( 268 opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"} 269 ) 270 # ext pillar function doesn't have the extra_minion_data arg 271 with patch( 272 "salt.utils.args.get_function_argspec", 273 MagicMock(return_value=MagicMock(args=[])), 274 ): 275 pillar._external_pillar_data("fake_pillar", ["bar"], "fake_ext_pillar") 276 mock_ext_pillar_func.assert_called_once_with( 277 "mocked-minion", "fake_pillar", "bar" 278 ) 279 # ext pillar function has the extra_minion_data arg 280 mock_ext_pillar_func.reset_mock() 281 with patch( 282 "salt.utils.args.get_function_argspec", 283 MagicMock(return_value=MagicMock(args=["extra_minion_data"])), 284 ): 285 pillar._external_pillar_data("fake_pillar", ["bar"], "fake_ext_pillar") 286 mock_ext_pillar_func.assert_called_once_with( 287 "mocked-minion", "fake_pillar", "bar", extra_minion_data={"fake_key": "foo"} 288 ) 289 290 def test_ext_pillar_with_extra_minion_data_val_elem(self): 291 opts = { 292 "optimization_order": [0, 1, 2], 293 "renderer": "json", 294 "renderer_blacklist": [], 295 "renderer_whitelist": [], 296 "state_top": "", 297 "pillar_roots": {"dev": [], "base": []}, 298 "file_roots": {"dev": [], "base": []}, 299 "extension_modules": "", 300 "pillarenv_from_saltenv": True, 301 } 302 mock_ext_pillar_func = MagicMock() 303 with patch( 304 "salt.loader.pillars", 305 MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), 306 ): 307 pillar = salt.pillar.Pillar( 308 opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"} 309 ) 310 # ext pillar function doesn't have the extra_minion_data arg 311 with patch( 312 "salt.utils.args.get_function_argspec", 313 MagicMock(return_value=MagicMock(args=[])), 314 ): 315 pillar._external_pillar_data("fake_pillar", "bar", "fake_ext_pillar") 316 mock_ext_pillar_func.assert_called_once_with( 317 "mocked-minion", "fake_pillar", "bar" 318 ) 319 # ext pillar function has the extra_minion_data arg 320 mock_ext_pillar_func.reset_mock() 321 with patch( 322 "salt.utils.args.get_function_argspec", 323 MagicMock(return_value=MagicMock(args=["extra_minion_data"])), 324 ): 325 pillar._external_pillar_data("fake_pillar", "bar", "fake_ext_pillar") 326 mock_ext_pillar_func.assert_called_once_with( 327 "mocked-minion", "fake_pillar", "bar", extra_minion_data={"fake_key": "foo"} 328 ) 329 330 def test_ext_pillar_first(self): 331 """ 332 test when using ext_pillar and ext_pillar_first 333 """ 334 opts = { 335 "optimization_order": [0, 1, 2], 336 "renderer": "yaml", 337 "renderer_blacklist": [], 338 "renderer_whitelist": [], 339 "state_top": "", 340 "pillar_roots": [], 341 "extension_modules": "", 342 "saltenv": "base", 343 "file_roots": [], 344 "ext_pillar_first": True, 345 } 346 grains = { 347 "os": "Ubuntu", 348 "os_family": "Debian", 349 "oscodename": "raring", 350 "osfullname": "Ubuntu", 351 "osrelease": "13.04", 352 "kernel": "Linux", 353 } 354 355 tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) 356 try: 357 sls_files = self._setup_test_topfile_sls_pillar_match( 358 tempdir, 359 ) 360 fc_mock = MockFileclient( 361 cache_file=sls_files["top"]["dest"], 362 list_states=["top", "ssh", "ssh.minion", "generic", "generic.minion"], 363 get_state=sls_files, 364 ) 365 with patch.object( 366 salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock) 367 ), patch( 368 "salt.pillar.Pillar.ext_pillar", 369 MagicMock( 370 return_value=( 371 {"id": "minion", "phase": "alpha", "role": "database"}, 372 [], 373 ) 374 ), 375 ): 376 pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") 377 self.assertEqual(pillar.compile_pillar()["generic"]["key1"], "value1") 378 finally: 379 shutil.rmtree(tempdir, ignore_errors=True) 380 381 def test_dynamic_pillarenv(self): 382 opts = { 383 "optimization_order": [0, 1, 2], 384 "renderer": "json", 385 "renderer_blacklist": [], 386 "renderer_whitelist": [], 387 "state_top": "", 388 "pillar_roots": { 389 "__env__": ["/srv/pillar/__env__"], 390 "base": ["/srv/pillar/base"], 391 }, 392 "file_roots": {"base": ["/srv/salt/base"], "dev": ["/svr/salt/dev"]}, 393 "extension_modules": "", 394 } 395 pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "base", pillarenv="dev") 396 self.assertEqual( 397 pillar.opts["pillar_roots"], 398 {"base": ["/srv/pillar/base"], "dev": ["/srv/pillar/__env__"]}, 399 ) 400 401 def test_ignored_dynamic_pillarenv(self): 402 opts = { 403 "optimization_order": [0, 1, 2], 404 "renderer": "json", 405 "renderer_blacklist": [], 406 "renderer_whitelist": [], 407 "state_top": "", 408 "pillar_roots": { 409 "__env__": ["/srv/pillar/__env__"], 410 "base": ["/srv/pillar/base"], 411 }, 412 "file_roots": {"base": ["/srv/salt/base"], "dev": ["/svr/salt/dev"]}, 413 "extension_modules": "", 414 } 415 pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "base", pillarenv="base") 416 self.assertEqual(pillar.opts["pillar_roots"], {"base": ["/srv/pillar/base"]}) 417 418 @patch("salt.fileclient.Client.list_states") 419 def test_malformed_pillar_sls(self, mock_list_states): 420 with patch("salt.pillar.compile_template") as compile_template: 421 opts = { 422 "optimization_order": [0, 1, 2], 423 "renderer": "json", 424 "renderer_blacklist": [], 425 "renderer_whitelist": [], 426 "state_top": "", 427 "pillar_roots": [], 428 "file_roots": [], 429 "extension_modules": "", 430 } 431 grains = { 432 "os": "Ubuntu", 433 "os_family": "Debian", 434 "oscodename": "raring", 435 "osfullname": "Ubuntu", 436 "osrelease": "13.04", 437 "kernel": "Linux", 438 } 439 440 mock_list_states.return_value = ["foo", "blah"] 441 pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") 442 # Mock getting the proper template files 443 pillar.client.get_state = MagicMock( 444 return_value={ 445 "dest": "/path/to/pillar/files/foo.sls", 446 "source": "salt://foo.sls", 447 } 448 ) 449 450 # Template compilation returned a string 451 compile_template.return_value = "BAHHH" 452 self.assertEqual( 453 pillar.render_pillar({"base": ["foo.sls"]}), 454 ({}, ["SLS 'foo.sls' does not render to a dictionary"]), 455 ) 456 457 # Template compilation returned a list 458 compile_template.return_value = ["BAHHH"] 459 self.assertEqual( 460 pillar.render_pillar({"base": ["foo.sls"]}), 461 ({}, ["SLS 'foo.sls' does not render to a dictionary"]), 462 ) 463 464 # Template compilation returned a dictionary, which is what's expected 465 compile_template.return_value = {"foo": "bar"} 466 self.assertEqual( 467 pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) 468 ) 469 470 # Test improper includes 471 compile_template.side_effect = [ 472 {"foo": "bar", "include": "blah"}, 473 {"foo2": "bar2"}, 474 ] 475 self.assertEqual( 476 pillar.render_pillar({"base": ["foo.sls"]}), 477 ( 478 {"foo": "bar", "include": "blah"}, 479 ["Include Declaration in SLS 'foo.sls' is not formed as a list"], 480 ), 481 ) 482 483 # Test includes as a list, which is what's expected 484 compile_template.side_effect = [ 485 {"foo": "bar", "include": ["blah"]}, 486 {"foo2": "bar2"}, 487 ] 488 self.assertEqual( 489 pillar.render_pillar({"base": ["foo.sls"]}), 490 ({"foo": "bar", "foo2": "bar2"}, []), 491 ) 492 493 # Test includes as a list overriding data 494 compile_template.side_effect = [ 495 {"foo": "bar", "include": ["blah"]}, 496 {"foo": "bar2"}, 497 ] 498 self.assertEqual( 499 pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) 500 ) 501 502 # Test includes using empty key directive 503 compile_template.side_effect = [ 504 {"foo": "bar", "include": [{"blah": {"key": ""}}]}, 505 {"foo": "bar2"}, 506 ] 507 self.assertEqual( 508 pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) 509 ) 510 511 # Test includes using simple non-nested key 512 compile_template.side_effect = [ 513 {"foo": "bar", "include": [{"blah": {"key": "nested"}}]}, 514 {"foo": "bar2"}, 515 ] 516 self.assertEqual( 517 pillar.render_pillar({"base": ["foo.sls"]}), 518 ({"foo": "bar", "nested": {"foo": "bar2"}}, []), 519 ) 520 521 # Test includes using nested key 522 compile_template.side_effect = [ 523 {"foo": "bar", "include": [{"blah": {"key": "nested:level"}}]}, 524 {"foo": "bar2"}, 525 ] 526 self.assertEqual( 527 pillar.render_pillar({"base": ["foo.sls"]}), 528 ({"foo": "bar", "nested": {"level": {"foo": "bar2"}}}, []), 529 ) 530 531 def test_includes_override_sls(self): 532 opts = { 533 "optimization_order": [0, 1, 2], 534 "renderer": "json", 535 "renderer_blacklist": [], 536 "renderer_whitelist": [], 537 "state_top": "", 538 "pillar_roots": {}, 539 "file_roots": {}, 540 "extension_modules": "", 541 } 542 grains = { 543 "os": "Ubuntu", 544 "os_family": "Debian", 545 "oscodename": "raring", 546 "osfullname": "Ubuntu", 547 "osrelease": "13.04", 548 "kernel": "Linux", 549 } 550 with patch("salt.pillar.compile_template") as compile_template, patch.object( 551 salt.pillar.Pillar, 552 "_Pillar__gather_avail", 553 MagicMock(return_value={"base": ["blah", "foo"]}), 554 ): 555 556 # Test with option set to True 557 opts["pillar_includes_override_sls"] = True 558 pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") 559 # Mock getting the proper template files 560 pillar.client.get_state = MagicMock( 561 return_value={ 562 "dest": "/path/to/pillar/files/foo.sls", 563 "source": "salt://foo.sls", 564 } 565 ) 566 567 compile_template.side_effect = [ 568 {"foo": "bar", "include": ["blah"]}, 569 {"foo": "bar2"}, 570 ] 571 self.assertEqual( 572 pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar2"}, []) 573 ) 574 575 # Test with option set to False 576 opts["pillar_includes_override_sls"] = False 577 pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") 578 # Mock getting the proper template files 579 pillar.client.get_state = MagicMock( 580 return_value={ 581 "dest": "/path/to/pillar/files/foo.sls", 582 "source": "salt://foo.sls", 583 } 584 ) 585 586 compile_template.side_effect = [ 587 {"foo": "bar", "include": ["blah"]}, 588 {"foo": "bar2"}, 589 ] 590 self.assertEqual( 591 pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) 592 ) 593 594 def test_topfile_order(self): 595 opts = { 596 "optimization_order": [0, 1, 2], 597 "renderer": "yaml", 598 "renderer_blacklist": [], 599 "renderer_whitelist": [], 600 "state_top": "", 601 "pillar_roots": [], 602 "extension_modules": "", 603 "saltenv": "base", 604 "file_roots": [], 605 } 606 grains = { 607 "os": "Ubuntu", 608 "os_family": "Debian", 609 "oscodename": "raring", 610 "osfullname": "Ubuntu", 611 "osrelease": "13.04", 612 "kernel": "Linux", 613 } 614 615 def _run_test(nodegroup_order, glob_order, expected): 616 tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) 617 try: 618 sls_files = self._setup_test_topfile_sls( 619 tempdir, nodegroup_order, glob_order 620 ) 621 fc_mock = MockFileclient( 622 cache_file=sls_files["top"]["dest"], 623 list_states=[ 624 "top", 625 "ssh", 626 "ssh.minion", 627 "generic", 628 "generic.minion", 629 ], 630 get_state=sls_files, 631 ) 632 with patch.object( 633 salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock) 634 ): 635 pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") 636 # Make sure that confirm_top.confirm_top returns True 637 pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True 638 self.assertEqual(pillar.compile_pillar()["ssh"], expected) 639 finally: 640 shutil.rmtree(tempdir, ignore_errors=True) 641 642 # test case where glob match happens second and therefore takes 643 # precedence over nodegroup match. 644 _run_test(nodegroup_order=1, glob_order=2, expected="bar") 645 # test case where nodegroup match happens second and therefore takes 646 # precedence over glob match. 647 _run_test(nodegroup_order=2, glob_order=1, expected="foo") 648 649 def _setup_test_topfile_sls_pillar_match(self, tempdir): 650 # Write a simple topfile and two pillar state files 651 top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 652 s = """ 653base: 654 'phase:alpha': 655 - match: pillar 656 - generic 657""" 658 top_file.write(salt.utils.stringutils.to_bytes(s)) 659 top_file.flush() 660 generic_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 661 generic_file.write( 662 b""" 663generic: 664 key1: value1 665""" 666 ) 667 generic_file.flush() 668 return { 669 "top": {"path": "", "dest": top_file.name}, 670 "generic": {"path": "", "dest": generic_file.name}, 671 } 672 673 def _setup_test_topfile_sls(self, tempdir, nodegroup_order, glob_order): 674 # Write a simple topfile and two pillar state files 675 top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 676 s = """ 677base: 678 group: 679 - match: nodegroup 680 - order: {nodegroup_order} 681 - ssh 682 - generic 683 '*': 684 - generic 685 minion: 686 - order: {glob_order} 687 - ssh.minion 688 - generic.minion 689""".format( 690 nodegroup_order=nodegroup_order, glob_order=glob_order 691 ) 692 top_file.write(salt.utils.stringutils.to_bytes(s)) 693 top_file.flush() 694 ssh_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 695 ssh_file.write( 696 b""" 697ssh: 698 foo 699""" 700 ) 701 ssh_file.flush() 702 ssh_minion_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 703 ssh_minion_file.write( 704 b""" 705ssh: 706 bar 707""" 708 ) 709 ssh_minion_file.flush() 710 generic_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 711 generic_file.write( 712 b""" 713generic: 714 key1: 715 - value1 716 - value2 717 key2: 718 sub_key1: [] 719""" 720 ) 721 generic_file.flush() 722 generic_minion_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 723 generic_minion_file.write( 724 b""" 725generic: 726 key1: 727 - value3 728 key2: 729 sub_key2: [] 730""" 731 ) 732 generic_minion_file.flush() 733 734 return { 735 "top": {"path": "", "dest": top_file.name}, 736 "ssh": {"path": "", "dest": ssh_file.name}, 737 "ssh.minion": {"path": "", "dest": ssh_minion_file.name}, 738 "generic": {"path": "", "dest": generic_file.name}, 739 "generic.minion": {"path": "", "dest": generic_minion_file.name}, 740 } 741 742 @with_tempdir() 743 def test_include(self, tempdir): 744 opts = { 745 "optimization_order": [0, 1, 2], 746 "renderer": "yaml", 747 "renderer_blacklist": [], 748 "renderer_whitelist": [], 749 "state_top": "", 750 "pillar_roots": [], 751 "extension_modules": "", 752 "saltenv": "base", 753 "file_roots": [], 754 } 755 grains = { 756 "os": "Ubuntu", 757 "os_family": "Debian", 758 "oscodename": "raring", 759 "osfullname": "Ubuntu", 760 "osrelease": "13.04", 761 "kernel": "Linux", 762 } 763 sls_files = self._setup_test_include_sls(tempdir) 764 fc_mock = MockFileclient( 765 cache_file=sls_files["top"]["dest"], 766 get_state=sls_files, 767 list_states=[ 768 "top", 769 "test.init", 770 "test.sub1", 771 "test.sub2", 772 "test.sub_wildcard_1", 773 "test.sub_with_init_dot", 774 "test.sub.with.slashes", 775 ], 776 ) 777 with patch.object( 778 salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock) 779 ): 780 pillar = salt.pillar.Pillar(opts, grains, "minion", "base") 781 # Make sure that confirm_top.confirm_top returns True 782 pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True 783 compiled_pillar = pillar.compile_pillar() 784 self.assertEqual(compiled_pillar["foo_wildcard"], "bar_wildcard") 785 self.assertEqual(compiled_pillar["foo1"], "bar1") 786 self.assertEqual(compiled_pillar["foo2"], "bar2") 787 self.assertEqual(compiled_pillar["sub_with_slashes"], "sub_slashes_worked") 788 self.assertEqual( 789 compiled_pillar["sub_init_dot"], "sub_with_init_dot_worked" 790 ) 791 792 def _setup_test_include_sls(self, tempdir): 793 top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 794 top_file.write( 795 b""" 796base: 797 '*': 798 - order: 1 799 - test.sub2 800 minion: 801 - order: 2 802 - test 803""" 804 ) 805 top_file.flush() 806 init_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 807 init_sls.write( 808 b""" 809include: 810 - test.sub1 811 - test.sub_wildcard* 812 - .test.sub_with_init_dot 813 - test/sub/with/slashes 814""" 815 ) 816 init_sls.flush() 817 sub1_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 818 sub1_sls.write( 819 b""" 820foo1: 821 bar1 822""" 823 ) 824 sub1_sls.flush() 825 sub2_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 826 sub2_sls.write( 827 b""" 828foo2: 829 bar2 830""" 831 ) 832 sub2_sls.flush() 833 834 sub_wildcard_1_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 835 sub_wildcard_1_sls.write( 836 b""" 837foo_wildcard: 838 bar_wildcard 839""" 840 ) 841 sub_wildcard_1_sls.flush() 842 843 sub_with_init_dot_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 844 sub_with_init_dot_sls.write( 845 b""" 846sub_init_dot: 847 sub_with_init_dot_worked 848""" 849 ) 850 sub_with_init_dot_sls.flush() 851 852 sub_with_slashes_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) 853 sub_with_slashes_sls.write( 854 b""" 855sub_with_slashes: 856 sub_slashes_worked 857""" 858 ) 859 sub_with_slashes_sls.flush() 860 861 return { 862 "top": {"path": "", "dest": top_file.name}, 863 "test": {"path": "", "dest": init_sls.name}, 864 "test.sub1": {"path": "", "dest": sub1_sls.name}, 865 "test.sub2": {"path": "", "dest": sub2_sls.name}, 866 "test.sub_wildcard_1": {"path": "", "dest": sub_wildcard_1_sls.name}, 867 "test.sub_with_init_dot": {"path": "", "dest": sub_with_init_dot_sls.name}, 868 "test.sub.with.slashes": {"path": "", "dest": sub_with_slashes_sls.name}, 869 } 870 871 @with_tempdir() 872 def test_relative_include(self, tempdir): 873 join = os.path.join 874 with fopen(join(tempdir, "top.sls"), "w") as f: 875 print( 876 textwrap.dedent( 877 """ 878 base: 879 '*': 880 - includer 881 - simple_includer 882 - includes.with.more.depth 883 """ 884 ), 885 file=f, 886 ) 887 includer_dir = join(tempdir, "includer") 888 os.makedirs(includer_dir) 889 with fopen(join(includer_dir, "init.sls"), "w") as f: 890 print( 891 textwrap.dedent( 892 """ 893 include: 894 - .this 895 - includer.that 896 """ 897 ), 898 file=f, 899 ) 900 with fopen(join(includer_dir, "this.sls"), "w") as f: 901 print( 902 textwrap.dedent( 903 """ 904 this: 905 is all good 906 """ 907 ), 908 file=f, 909 ) 910 with fopen(join(includer_dir, "that.sls"), "w") as f: 911 print( 912 textwrap.dedent( 913 """ 914 that: 915 is also all good 916 """ 917 ), 918 file=f, 919 ) 920 921 with fopen(join(tempdir, "simple_includer.sls"), "w") as simpleincluder: 922 print( 923 textwrap.dedent( 924 """ 925 include: 926 - .simple 927 - super_simple 928 """ 929 ), 930 file=simpleincluder, 931 ) 932 with fopen(join(tempdir, "simple.sls"), "w") as f: 933 print( 934 textwrap.dedent( 935 """ 936 simple: 937 simon 938 """ 939 ), 940 file=f, 941 ) 942 with fopen(join(tempdir, "super_simple.sls"), "w") as f: 943 print( 944 textwrap.dedent( 945 """ 946 super simple: 947 a caveman 948 """ 949 ), 950 file=f, 951 ) 952 953 depth_dir = join(tempdir, "includes", "with", "more") 954 os.makedirs(depth_dir) 955 with fopen(join(depth_dir, "depth.sls"), "w") as f: 956 print( 957 textwrap.dedent( 958 """ 959 include: 960 - .ramble 961 - includes.with.more.doors 962 963 mordor: 964 has dark depths 965 """ 966 ), 967 file=f, 968 ) 969 970 with fopen(join(depth_dir, "ramble.sls"), "w") as f: 971 print( 972 textwrap.dedent( 973 """ 974 found: 975 my precious 976 """ 977 ), 978 file=f, 979 ) 980 981 with fopen(join(depth_dir, "doors.sls"), "w") as f: 982 print( 983 textwrap.dedent( 984 """ 985 mojo: 986 bad risin' 987 """ 988 ), 989 file=f, 990 ) 991 opts = { 992 "optimization_order": [0, 1, 2], 993 "renderer": "yaml", 994 "renderer_blacklist": [], 995 "renderer_whitelist": [], 996 "state_top": "top.sls", 997 "pillar_roots": {"base": [tempdir]}, 998 "extension_modules": "", 999 "saltenv": "base", 1000 "file_roots": [], 1001 "file_ignore_regex": None, 1002 "file_ignore_glob": None, 1003 } 1004 grains = { 1005 "os": "Ubuntu", 1006 "os_family": "Debian", 1007 "oscodename": "raring", 1008 "osfullname": "Ubuntu", 1009 "osrelease": "13.04", 1010 "kernel": "Linux", 1011 } 1012 pillar = salt.pillar.Pillar(opts, grains, "minion", "base") 1013 # Make sure that confirm_top.confirm_top returns True 1014 pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True 1015 1016 # Act 1017 compiled_pillar = pillar.compile_pillar() 1018 1019 # Assert 1020 self.assertEqual(compiled_pillar["this"], "is all good") 1021 self.assertEqual(compiled_pillar["that"], "is also all good") 1022 self.assertEqual(compiled_pillar["simple"], "simon") 1023 self.assertEqual(compiled_pillar["super simple"], "a caveman") 1024 self.assertEqual(compiled_pillar["mordor"], "has dark depths") 1025 self.assertEqual(compiled_pillar["found"], "my precious") 1026 self.assertEqual(compiled_pillar["mojo"], "bad risin'") 1027 1028 @with_tempdir() 1029 def test_missing_include(self, tempdir): 1030 opts = { 1031 "optimization_order": [0, 1, 2], 1032 "renderer": "yaml", 1033 "renderer_blacklist": [], 1034 "renderer_whitelist": [], 1035 "state_top": "top.sls", 1036 "pillar_roots": {"base": [tempdir]}, 1037 "extension_modules": "", 1038 "saltenv": "base", 1039 "file_roots": [], 1040 "file_ignore_regex": None, 1041 "file_ignore_glob": None, 1042 } 1043 grains = { 1044 "os": "Ubuntu", 1045 "os_family": "Debian", 1046 "oscodename": "raring", 1047 "osfullname": "Ubuntu", 1048 "osrelease": "13.04", 1049 "kernel": "Linux", 1050 } 1051 1052 join = os.path.join 1053 with fopen(join(tempdir, "top.sls"), "w") as f: 1054 print( 1055 textwrap.dedent( 1056 """ 1057 base: 1058 '*': 1059 - simple_include 1060 """ 1061 ), 1062 file=f, 1063 ) 1064 include_dir = join(tempdir, "simple_include") 1065 os.makedirs(include_dir) 1066 with fopen(join(include_dir, "init.sls"), "w") as f: 1067 print( 1068 textwrap.dedent( 1069 """ 1070 include: 1071 - simple_include.missing_include 1072 simple_include: is ok 1073 """ 1074 ), 1075 file=f, 1076 ) 1077 1078 pillar = salt.pillar.Pillar(opts, grains, "minion", "base") 1079 # Make sure that confirm_top.confirm_top returns True 1080 pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True 1081 1082 # Act 1083 compiled_pillar = pillar.compile_pillar() 1084 1085 # Assert 1086 self.assertEqual(compiled_pillar["simple_include"], "is ok") 1087 self.assertTrue("_errors" in compiled_pillar) 1088 self.assertTrue( 1089 "simple_include.missing_include" in compiled_pillar["_errors"][0] 1090 ) 1091 1092 1093@patch("salt.transport.client.ReqChannel.factory", MagicMock()) 1094class RemotePillarTestCase(TestCase): 1095 """ 1096 Tests for instantiating a RemotePillar in salt.pillar 1097 """ 1098 1099 def setUp(self): 1100 self.grains = {} 1101 1102 def tearDown(self): 1103 for attr in ("grains",): 1104 try: 1105 delattr(self, attr) 1106 except AttributeError: 1107 continue 1108 1109 def test_get_opts_in_pillar_override_call(self): 1110 mock_get_extra_minion_data = MagicMock(return_value={}) 1111 with patch( 1112 "salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data", 1113 mock_get_extra_minion_data, 1114 ): 1115 1116 salt.pillar.RemotePillar({}, self.grains, "mocked-minion", "dev") 1117 mock_get_extra_minion_data.assert_called_once_with({"saltenv": "dev"}) 1118 1119 def test_multiple_keys_in_opts_added_to_pillar(self): 1120 opts = { 1121 "renderer": "json", 1122 "path_to_add": "fake_data", 1123 "path_to_add2": {"fake_data2": ["fake_data3", "fake_data4"]}, 1124 "pass_to_ext_pillars": ["path_to_add", "path_to_add2"], 1125 } 1126 pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") 1127 self.assertEqual( 1128 pillar.extra_minion_data, 1129 { 1130 "path_to_add": "fake_data", 1131 "path_to_add2": {"fake_data2": ["fake_data3", "fake_data4"]}, 1132 }, 1133 ) 1134 1135 def test_subkey_in_opts_added_to_pillar(self): 1136 opts = { 1137 "renderer": "json", 1138 "path_to_add": "fake_data", 1139 "path_to_add2": { 1140 "fake_data5": "fake_data6", 1141 "fake_data2": ["fake_data3", "fake_data4"], 1142 }, 1143 "pass_to_ext_pillars": ["path_to_add2:fake_data5"], 1144 } 1145 pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") 1146 self.assertEqual( 1147 pillar.extra_minion_data, {"path_to_add2": {"fake_data5": "fake_data6"}} 1148 ) 1149 1150 def test_non_existent_leaf_opt_in_add_to_pillar(self): 1151 opts = { 1152 "renderer": "json", 1153 "path_to_add": "fake_data", 1154 "path_to_add2": { 1155 "fake_data5": "fake_data6", 1156 "fake_data2": ["fake_data3", "fake_data4"], 1157 }, 1158 "pass_to_ext_pillars": ["path_to_add2:fake_data_non_exist"], 1159 } 1160 pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") 1161 self.assertEqual(pillar.pillar_override, {}) 1162 1163 def test_non_existent_intermediate_opt_in_add_to_pillar(self): 1164 opts = { 1165 "renderer": "json", 1166 "path_to_add": "fake_data", 1167 "path_to_add2": { 1168 "fake_data5": "fake_data6", 1169 "fake_data2": ["fake_data3", "fake_data4"], 1170 }, 1171 "pass_to_ext_pillars": ["path_to_add_no_exist"], 1172 } 1173 pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") 1174 self.assertEqual(pillar.pillar_override, {}) 1175 1176 def test_malformed_add_to_pillar(self): 1177 opts = { 1178 "renderer": "json", 1179 "path_to_add": "fake_data", 1180 "path_to_add2": { 1181 "fake_data5": "fake_data6", 1182 "fake_data2": ["fake_data3", "fake_data4"], 1183 }, 1184 "pass_to_ext_pillars": MagicMock(), 1185 } 1186 with self.assertRaises(salt.exceptions.SaltClientError) as excinfo: 1187 salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") 1188 self.assertEqual( 1189 excinfo.exception.strerror, "'pass_to_ext_pillars' config is malformed." 1190 ) 1191 1192 def test_pillar_send_extra_minion_data_from_config(self): 1193 opts = { 1194 "renderer": "json", 1195 "pillarenv": "fake_pillar_env", 1196 "path_to_add": "fake_data", 1197 "path_to_add2": { 1198 "fake_data5": "fake_data6", 1199 "fake_data2": ["fake_data3", "fake_data4"], 1200 }, 1201 "pass_to_ext_pillars": ["path_to_add"], 1202 } 1203 mock_channel = MagicMock( 1204 crypted_transfer_decode_dictentry=MagicMock(return_value={}) 1205 ) 1206 with patch( 1207 "salt.transport.client.ReqChannel.factory", 1208 MagicMock(return_value=mock_channel), 1209 ): 1210 pillar = salt.pillar.RemotePillar( 1211 opts, self.grains, "mocked_minion", "fake_env" 1212 ) 1213 1214 ret = pillar.compile_pillar() 1215 self.assertEqual(pillar.channel, mock_channel) 1216 mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with( 1217 { 1218 "cmd": "_pillar", 1219 "ver": "2", 1220 "id": "mocked_minion", 1221 "grains": {}, 1222 "saltenv": "fake_env", 1223 "pillarenv": "fake_pillar_env", 1224 "pillar_override": {}, 1225 "extra_minion_data": {"path_to_add": "fake_data"}, 1226 }, 1227 dictkey="pillar", 1228 ) 1229 1230 def test_pillar_file_client_master_remote(self): 1231 """ 1232 Test condition where local file_client and use_master_when_local option 1233 returns a remote file client. 1234 """ 1235 mocked_minion = MagicMock() 1236 opts = { 1237 "file_client": "local", 1238 "use_master_when_local": True, 1239 "pillar_cache": None, 1240 } 1241 pillar = salt.pillar.get_pillar(opts, self.grains, mocked_minion) 1242 self.assertEqual(type(pillar), salt.pillar.RemotePillar) 1243 self.assertNotEqual(type(pillar), salt.pillar.PillarCache) 1244 1245 1246@patch("salt.transport.client.AsyncReqChannel.factory", MagicMock()) 1247class AsyncRemotePillarTestCase(TestCase): 1248 """ 1249 Tests for instantiating a AsyncRemotePillar in salt.pillar 1250 """ 1251 1252 def setUp(self): 1253 self.grains = {} 1254 1255 def tearDown(self): 1256 for attr in ("grains",): 1257 try: 1258 delattr(self, attr) 1259 except AttributeError: 1260 continue 1261 1262 def test_get_opts_in_pillar_override_call(self): 1263 mock_get_extra_minion_data = MagicMock(return_value={}) 1264 with patch( 1265 "salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data", 1266 mock_get_extra_minion_data, 1267 ): 1268 1269 salt.pillar.RemotePillar({}, self.grains, "mocked-minion", "dev") 1270 mock_get_extra_minion_data.assert_called_once_with({"saltenv": "dev"}) 1271 1272 def test_pillar_send_extra_minion_data_from_config(self): 1273 opts = { 1274 "renderer": "json", 1275 "pillarenv": "fake_pillar_env", 1276 "path_to_add": "fake_data", 1277 "path_to_add2": { 1278 "fake_data5": "fake_data6", 1279 "fake_data2": ["fake_data3", "fake_data4"], 1280 }, 1281 "pass_to_ext_pillars": ["path_to_add"], 1282 } 1283 mock_channel = MagicMock( 1284 crypted_transfer_decode_dictentry=MagicMock(return_value={}) 1285 ) 1286 with patch( 1287 "salt.transport.client.AsyncReqChannel.factory", 1288 MagicMock(return_value=mock_channel), 1289 ): 1290 pillar = salt.pillar.RemotePillar( 1291 opts, self.grains, "mocked_minion", "fake_env" 1292 ) 1293 1294 ret = pillar.compile_pillar() 1295 mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with( 1296 { 1297 "cmd": "_pillar", 1298 "ver": "2", 1299 "id": "mocked_minion", 1300 "grains": {}, 1301 "saltenv": "fake_env", 1302 "pillarenv": "fake_pillar_env", 1303 "pillar_override": {}, 1304 "extra_minion_data": {"path_to_add": "fake_data"}, 1305 }, 1306 dictkey="pillar", 1307 ) 1308 1309 1310@patch("salt.transport.client.ReqChannel.factory", MagicMock()) 1311class PillarCacheTestCase(TestCase): 1312 """ 1313 Tests for instantiating a PillarCache in salt.pillar 1314 """ 1315 1316 def setUp(self): 1317 self.grains = {} 1318 1319 @classmethod 1320 def setUpClass(cls): 1321 cls.mock_master_default_opts = salt.config.DEFAULT_MASTER_OPTS.copy() 1322 cls.mock_master_default_opts["cachedir"] = tempfile.mkdtemp( 1323 dir=RUNTIME_VARS.TMP 1324 ) 1325 1326 def tearDown(self): 1327 for attr in ("grains",): 1328 try: 1329 delattr(self, attr) 1330 except AttributeError: 1331 continue 1332 1333 def test_compile_pillar_disk_cache(self): 1334 self.mock_master_default_opts.update( 1335 {"pillar_cache_backend": "disk", "pillar_cache_ttl": 3600} 1336 ) 1337 1338 pillar = salt.pillar.PillarCache( 1339 self.mock_master_default_opts, 1340 self.grains, 1341 "mocked_minion", 1342 "fake_env", 1343 pillarenv="base", 1344 ) 1345 1346 with patch("salt.utils.cache.CacheDisk._write", MagicMock()): 1347 with patch( 1348 "salt.pillar.PillarCache.fetch_pillar", 1349 side_effect=[{"foo": "bar"}, {"foo": "baz"}], 1350 ): 1351 # Run once for pillarenv base 1352 ret = pillar.compile_pillar() 1353 expected_cache = {"mocked_minion": {"base": {"foo": "bar"}}} 1354 self.assertEqual(pillar.cache._dict, expected_cache) 1355 1356 # Run a second time for pillarenv base 1357 ret = pillar.compile_pillar() 1358 expected_cache = {"mocked_minion": {"base": {"foo": "bar"}}} 1359 self.assertEqual(pillar.cache._dict, expected_cache) 1360 1361 # Change the pillarenv 1362 pillar.pillarenv = "dev" 1363 1364 # Run once for pillarenv dev 1365 ret = pillar.compile_pillar() 1366 expected_cache = { 1367 "mocked_minion": {"base": {"foo": "bar"}, "dev": {"foo": "baz"}} 1368 } 1369 self.assertEqual(pillar.cache._dict, expected_cache) 1370 1371 # Run a second time for pillarenv dev 1372 ret = pillar.compile_pillar() 1373 expected_cache = { 1374 "mocked_minion": {"base": {"foo": "bar"}, "dev": {"foo": "baz"}} 1375 } 1376 self.assertEqual(pillar.cache._dict, expected_cache) 1377 1378 def test_compile_pillar_memory_cache(self): 1379 self.mock_master_default_opts.update( 1380 {"pillar_cache_backend": "memory", "pillar_cache_ttl": 3600} 1381 ) 1382 1383 pillar = salt.pillar.PillarCache( 1384 self.mock_master_default_opts, 1385 self.grains, 1386 "mocked_minion", 1387 "fake_env", 1388 pillarenv="base", 1389 ) 1390 1391 with patch( 1392 "salt.pillar.PillarCache.fetch_pillar", 1393 side_effect=[{"foo": "bar"}, {"foo": "baz"}], 1394 ): 1395 # Run once for pillarenv base 1396 ret = pillar.compile_pillar() 1397 expected_cache = {"base": {"foo": "bar"}} 1398 self.assertIn("mocked_minion", pillar.cache) 1399 self.assertEqual(pillar.cache["mocked_minion"], expected_cache) 1400 1401 # Run a second time for pillarenv base 1402 ret = pillar.compile_pillar() 1403 expected_cache = {"base": {"foo": "bar"}} 1404 self.assertIn("mocked_minion", pillar.cache) 1405 self.assertEqual(pillar.cache["mocked_minion"], expected_cache) 1406 1407 # Change the pillarenv 1408 pillar.pillarenv = "dev" 1409 1410 # Run once for pillarenv dev 1411 ret = pillar.compile_pillar() 1412 expected_cache = {"base": {"foo": "bar"}, "dev": {"foo": "baz"}} 1413 self.assertIn("mocked_minion", pillar.cache) 1414 self.assertEqual(pillar.cache["mocked_minion"], expected_cache) 1415 1416 # Run a second time for pillarenv dev 1417 ret = pillar.compile_pillar() 1418 expected_cache = {"base": {"foo": "bar"}, "dev": {"foo": "baz"}} 1419 self.assertIn("mocked_minion", pillar.cache) 1420 self.assertEqual(pillar.cache["mocked_minion"], expected_cache) 1421