123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- # -*- coding: utf-8 -*-
- """
- :codeauthor: Erik Johnson <erik@saltstack.com>
- """
- from __future__ import absolute_import
- import copy
- import errno
- import logging
- import os
- import shutil
- import subprocess
- import textwrap
- import pytest
- import salt.pillar as pillar
- import salt.utils.files
- import salt.utils.path
- import salt.utils.stringutils
- import salt.utils.yaml
- from tests.support.case import ModuleCase
- from tests.support.helpers import dedent, requires_system_grains, slowTest
- from tests.support.runtests import RUNTIME_VARS
- from tests.support.unit import skipIf
- log = logging.getLogger(__name__)
- TEST_KEY = """\
- -----BEGIN PGP PRIVATE KEY BLOCK-----
- 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
- =06Tz
- -----END PGP PRIVATE KEY BLOCK-----
- """
- GPG_PILLAR_YAML = """\
- secrets:
- vault:
- foo: |
- -----BEGIN PGP MESSAGE-----
- hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th
- W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74
- ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7
- +KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb
- VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73
- zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06
- KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh
- iFndxegN9w==
- =bAuo
- -----END PGP MESSAGE-----
- bar: this was unencrypted already
- baz: |
- -----BEGIN PGP MESSAGE-----
- hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz
- gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf
- 9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7
- Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2
- q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V
- kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl
- JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY
- 1OZi
- =7epf
- -----END PGP MESSAGE-----
- qux:
- - foo
- - bar
- - |
- -----BEGIN PGP MESSAGE-----
- hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS
- ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI
- gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA
- YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF
- f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE
- uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd
- 4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=
- =LrCQ
- -----END PGP MESSAGE-----
- """
- GPG_PILLAR_ENCRYPTED = {
- "secrets": {
- "vault": {
- "foo": "-----BEGIN PGP MESSAGE-----\n"
- "\n"
- "hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th\n"
- "W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74\n"
- "ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7\n"
- "+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb\n"
- "VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73\n"
- "zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06\n"
- "KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh\n"
- "iFndxegN9w==\n"
- "=bAuo\n"
- "-----END PGP MESSAGE-----\n",
- "bar": "this was unencrypted already",
- "baz": "-----BEGIN PGP MESSAGE-----\n"
- "\n"
- "hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz\n"
- "gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf\n"
- "9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7\n"
- "Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2\n"
- "q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V\n"
- "kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl\n"
- "JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY\n"
- "1OZi\n"
- "=7epf\n"
- "-----END PGP MESSAGE-----\n",
- "qux": [
- "foo",
- "bar",
- "-----BEGIN PGP MESSAGE-----\n"
- "\n"
- "hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS\n"
- "ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI\n"
- "gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA\n"
- "YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF\n"
- "f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE\n"
- "uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd\n"
- "4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=\n"
- "=LrCQ\n"
- "-----END PGP MESSAGE-----\n",
- ],
- },
- },
- }
- GPG_PILLAR_DECRYPTED = {
- "secrets": {
- "vault": {
- "foo": "supersecret",
- "bar": "this was unencrypted already",
- "baz": "rosebud",
- "qux": ["foo", "bar", "baz"],
- },
- },
- }
- class _CommonBase(ModuleCase):
- @classmethod
- def setUpClass(cls):
- cls.pillar_base = os.path.join(
- RUNTIME_VARS.TMP, "test-decrypt-pillar", "pillar"
- )
- cls.top_sls = os.path.join(cls.pillar_base, "top.sls")
- cls.gpg_sls = os.path.join(cls.pillar_base, "gpg.sls")
- cls.default_opts = {
- "cachedir": os.path.join(RUNTIME_VARS.TMP, "rootdir", "cache"),
- "optimization_order": [0, 1, 2],
- "extension_modules": os.path.join(
- RUNTIME_VARS.TMP, "test-decrypt-pillar", "extmods"
- ),
- "pillar_roots": {"base": [cls.pillar_base]},
- "ext_pillar_first": False,
- "ext_pillar": [],
- "decrypt_pillar_default": "gpg",
- "decrypt_pillar_delimiter": ":",
- "decrypt_pillar_renderers": ["gpg"],
- }
- cls.additional_opts = (
- "conf_file",
- "file_roots",
- "state_top",
- "renderer",
- "renderer_whitelist",
- "renderer_blacklist",
- )
- cls.gpg_homedir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "gpgkeys")
- def _build_opts(self, opts):
- ret = copy.deepcopy(self.default_opts)
- for item in self.additional_opts:
- ret[item] = self.master_opts[item]
- ret.update(opts)
- return ret
- @pytest.mark.windows_whitelisted
- class BasePillarTest(_CommonBase):
- """
- Tests for pillar decryption
- """
- @classmethod
- def setUpClass(cls):
- super(BasePillarTest, cls).setUpClass()
- os.makedirs(cls.pillar_base)
- with salt.utils.files.fopen(cls.top_sls, "w") as fp_:
- fp_.write(
- textwrap.dedent(
- """\
- base:
- 'N@mins not L@minion':
- - ng1
- 'N@missing_minion':
- - ng2
- """
- )
- )
- with salt.utils.files.fopen(
- os.path.join(cls.pillar_base, "ng1.sls"), "w"
- ) as fp_:
- fp_.write("pillar_from_nodegroup: True")
- with salt.utils.files.fopen(
- os.path.join(cls.pillar_base, "ng2.sls"), "w"
- ) as fp_:
- fp_.write("pillar_from_nodegroup_with_ghost: True")
- @classmethod
- def tearDownClass(cls):
- shutil.rmtree(cls.pillar_base)
- def test_pillar_top_compound_match(self, grains=None):
- """
- Test that a compound match topfile that refers to a nodegroup via N@ works
- as expected.
- """
- if not grains:
- grains = {}
- grains["os"] = "Fedora"
- nodegroup_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- nodegroups:
- min: minion
- sub_min: sub_minion
- mins: N@min or N@sub_min
- missing_minion: L@minion,ghostminion
- """
- )
- )
- opts = self._build_opts(nodegroup_opts)
- pillar_obj = pillar.Pillar(opts, grains, "minion", "base")
- ret = pillar_obj.compile_pillar()
- self.assertEqual(ret.get("pillar_from_nodegroup_with_ghost"), True)
- self.assertEqual(ret.get("pillar_from_nodegroup"), None)
- sub_pillar_obj = pillar.Pillar(opts, grains, "sub_minion", "base")
- sub_ret = sub_pillar_obj.compile_pillar()
- self.assertEqual(sub_ret.get("pillar_from_nodegroup_with_ghost"), None)
- self.assertEqual(sub_ret.get("pillar_from_nodegroup"), True)
- @skipIf(not salt.utils.path.which("gpg"), "GPG is not installed")
- @pytest.mark.windows_whitelisted
- class DecryptGPGPillarTest(_CommonBase):
- """
- Tests for pillar decryption
- """
- maxDiff = None
- @classmethod
- def setUpClass(cls):
- super(DecryptGPGPillarTest, cls).setUpClass()
- try:
- os.makedirs(cls.gpg_homedir, mode=0o700)
- except Exception: # pylint: disable=broad-except
- cls.created_gpg_homedir = False
- raise
- else:
- cls.created_gpg_homedir = True
- cmd_prefix = ["gpg", "--homedir", cls.gpg_homedir]
- cmd = cmd_prefix + ["--list-keys"]
- log.debug("Instantiating gpg keyring using: %s", cmd)
- output = subprocess.Popen(
- cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False
- ).communicate()[0]
- log.debug("Result:\n%s", output)
- cmd = cmd_prefix + ["--import", "--allow-secret-key-import"]
- log.debug("Importing keypair using: %s", cmd)
- output = subprocess.Popen(
- cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- shell=False,
- ).communicate(input=salt.utils.stringutils.to_bytes(TEST_KEY))[0]
- log.debug("Result:\n%s", output)
- os.makedirs(cls.pillar_base)
- with salt.utils.files.fopen(cls.top_sls, "w") as fp_:
- fp_.write(
- textwrap.dedent(
- """\
- base:
- '*':
- - gpg
- """
- )
- )
- with salt.utils.files.fopen(cls.gpg_sls, "w") as fp_:
- fp_.write(GPG_PILLAR_YAML)
- @classmethod
- def tearDownClass(cls):
- cmd = ["gpg-connect-agent", "--homedir", cls.gpg_homedir]
- try:
- log.debug("Killing gpg-agent using: %s", cmd)
- output = subprocess.Popen(
- cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- shell=False,
- ).communicate(input=b"KILLAGENT")[0]
- log.debug("Result:\n%s", output)
- except OSError:
- log.debug("No need to kill: old gnupg doesn't start the agent.")
- if cls.created_gpg_homedir:
- try:
- shutil.rmtree(cls.gpg_homedir)
- except OSError as exc:
- # GPG socket can disappear before rmtree gets to this point
- if exc.errno != errno.ENOENT:
- raise
- shutil.rmtree(cls.pillar_base)
- @requires_system_grains
- def test_decrypt_pillar_default_renderer(self, grains=None):
- """
- Test recursive decryption of secrets:vault as well as the fallback to
- default decryption renderer.
- """
- decrypt_pillar_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- decrypt_pillar:
- - 'secrets:vault'
- """
- )
- )
- opts = self._build_opts(decrypt_pillar_opts)
- pillar_obj = pillar.Pillar(opts, grains, "test", "base")
- ret = pillar_obj.compile_pillar()
- self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
- @requires_system_grains
- @slowTest
- def test_decrypt_pillar_alternate_delimiter(self, grains=None):
- """
- Test recursive decryption of secrets:vault using a pipe instead of a
- colon as the nesting delimiter.
- """
- decrypt_pillar_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- decrypt_pillar_delimiter: '|'
- decrypt_pillar:
- - 'secrets|vault'
- """
- )
- )
- opts = self._build_opts(decrypt_pillar_opts)
- pillar_obj = pillar.Pillar(opts, grains, "test", "base")
- ret = pillar_obj.compile_pillar()
- self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
- @requires_system_grains
- def test_decrypt_pillar_deeper_nesting(self, grains=None):
- """
- Test recursive decryption, only with a more deeply-nested target. This
- should leave the other keys in secrets:vault encrypted.
- """
- decrypt_pillar_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- decrypt_pillar:
- - 'secrets:vault:qux'
- """
- )
- )
- opts = self._build_opts(decrypt_pillar_opts)
- pillar_obj = pillar.Pillar(opts, grains, "test", "base")
- ret = pillar_obj.compile_pillar()
- expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
- expected["secrets"]["vault"]["qux"][-1] = GPG_PILLAR_DECRYPTED["secrets"][
- "vault"
- ]["qux"][-1]
- self.assertEqual(ret, expected)
- @requires_system_grains
- def test_decrypt_pillar_explicit_renderer(self, grains=None):
- """
- Test recursive decryption of secrets:vault, with the renderer
- explicitly defined, overriding the default. Setting the default to a
- nonexistent renderer so we can be sure that the override happened.
- """
- decrypt_pillar_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- decrypt_pillar_default: asdf
- decrypt_pillar_renderers:
- - asdf
- - gpg
- decrypt_pillar:
- - 'secrets:vault': gpg
- """
- )
- )
- opts = self._build_opts(decrypt_pillar_opts)
- pillar_obj = pillar.Pillar(opts, grains, "test", "base")
- ret = pillar_obj.compile_pillar()
- self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
- @requires_system_grains
- def test_decrypt_pillar_missing_renderer(self, grains=None):
- """
- Test decryption using a missing renderer. It should fail, leaving the
- encrypted keys intact, and add an error to the pillar dictionary.
- """
- decrypt_pillar_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- decrypt_pillar_default: asdf
- decrypt_pillar_renderers:
- - asdf
- decrypt_pillar:
- - 'secrets:vault'
- """
- )
- )
- opts = self._build_opts(decrypt_pillar_opts)
- pillar_obj = pillar.Pillar(opts, grains, "test", "base")
- ret = pillar_obj.compile_pillar()
- expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
- expected["_errors"] = [
- "Failed to decrypt pillar key 'secrets:vault': Decryption "
- "renderer 'asdf' is not available"
- ]
- self.assertEqual(ret["_errors"], expected["_errors"])
- self.assertEqual(
- ret["secrets"]["vault"]["foo"], expected["secrets"]["vault"]["foo"]
- )
- self.assertEqual(
- ret["secrets"]["vault"]["bar"], expected["secrets"]["vault"]["bar"]
- )
- self.assertEqual(
- ret["secrets"]["vault"]["baz"], expected["secrets"]["vault"]["baz"]
- )
- self.assertEqual(
- ret["secrets"]["vault"]["qux"], expected["secrets"]["vault"]["qux"]
- )
- @requires_system_grains
- def test_decrypt_pillar_invalid_renderer(self, grains=None):
- """
- Test decryption using a renderer which is not permitted. It should
- fail, leaving the encrypted keys intact, and add an error to the pillar
- dictionary.
- """
- decrypt_pillar_opts = salt.utils.yaml.safe_load(
- textwrap.dedent(
- """\
- decrypt_pillar_default: foo
- decrypt_pillar_renderers:
- - foo
- - bar
- decrypt_pillar:
- - 'secrets:vault': gpg
- """
- )
- )
- opts = self._build_opts(decrypt_pillar_opts)
- pillar_obj = pillar.Pillar(opts, grains, "test", "base")
- ret = pillar_obj.compile_pillar()
- expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
- expected["_errors"] = [
- "Failed to decrypt pillar key 'secrets:vault': 'gpg' is "
- "not a valid decryption renderer. Valid choices are: foo, bar"
- ]
- self.assertEqual(ret["_errors"], expected["_errors"])
- self.assertEqual(
- ret["secrets"]["vault"]["foo"], expected["secrets"]["vault"]["foo"]
- )
- self.assertEqual(
- ret["secrets"]["vault"]["bar"], expected["secrets"]["vault"]["bar"]
- )
- self.assertEqual(
- ret["secrets"]["vault"]["baz"], expected["secrets"]["vault"]["baz"]
- )
- self.assertEqual(
- ret["secrets"]["vault"]["qux"], expected["secrets"]["vault"]["qux"]
- )
- @pytest.mark.windows_whitelisted
- class RefreshPillarTest(ModuleCase):
- """
- These tests validate the behavior defined in the documentation:
- https://docs.saltstack.com/en/latest/topics/pillar/#in-memory-pillar-data-vs-on-demand-pillar-data
- These tests also serve as a regression test for:
- https://github.com/saltstack/salt/issues/54941
- """
- def cleanup_pillars(self, top_path, pillar_path):
- os.remove(top_path)
- os.remove(pillar_path)
- self.run_function("saltutil.refresh_pillar", arg=(True,))
- def create_pillar(self, key):
- """
- Utility method to create a pillar for the minion and a value of true,
- this method also removes and cleans up the pillar at the end of the
- test.
- """
- top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls")
- pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "test_pillar.sls")
- with salt.utils.files.fopen(top_path, "w") as fd:
- fd.write(
- dedent(
- """
- base:
- 'minion':
- - test_pillar
- """
- )
- )
- with salt.utils.files.fopen(pillar_path, "w") as fd:
- fd.write(
- dedent(
- """
- {}: true
- """.format(
- key
- )
- )
- )
- self.addCleanup(self.cleanup_pillars, top_path, pillar_path)
- @slowTest
- def test_pillar_refresh_pillar_raw(self):
- """
- Validate the minion's pillar.raw call behavior for new pillars
- """
- key = "issue-54941-raw"
- # We do not expect to see the pillar beacuse it does not exist yet
- val = self.run_function("pillar.raw", arg=(key,))
- assert val == {}
- self.create_pillar(key)
- # The pillar exists now but raw reads it from in-memory pillars
- val = self.run_function("pillar.raw", arg=(key,))
- assert val == {}
- # Calling refresh_pillar to update in-memory pillars
- ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
- # The pillar can now be read from in-memory pillars
- val = self.run_function("pillar.raw", arg=(key,))
- assert val is True, repr(val)
- @slowTest
- def test_pillar_refresh_pillar_get(self):
- """
- Validate the minion's pillar.get call behavior for new pillars
- """
- key = "issue-54941-get"
- # We do not expect to see the pillar beacuse it does not exist yet
- val = self.run_function("pillar.get", arg=(key,))
- assert val == ""
- top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls")
- pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "test_pillar.sls")
- self.create_pillar(key)
- # The pillar exists now but get reads it from in-memory pillars, no
- # refresh happens
- val = self.run_function("pillar.get", arg=(key,))
- assert val == ""
- # Calling refresh_pillar to update in-memory pillars
- ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
- assert ret is True
- # The pillar can now be read from in-memory pillars
- val = self.run_function("pillar.get", arg=(key,))
- assert val is True, repr(val)
- @slowTest
- def test_pillar_refresh_pillar_item(self):
- """
- Validate the minion's pillar.item call behavior for new pillars
- """
- key = "issue-54941-item"
- # We do not expect to see the pillar beacuse it does not exist yet
- val = self.run_function("pillar.item", arg=(key,))
- assert key in val
- assert val[key] == ""
- self.create_pillar(key)
- # The pillar exists now but get reads it from in-memory pillars, no
- # refresh happens
- val = self.run_function("pillar.item", arg=(key,))
- assert key in val
- assert val[key] == ""
- # Calling refresh_pillar to update in-memory pillars
- ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
- assert ret is True
- # The pillar can now be read from in-memory pillars
- val = self.run_function("pillar.item", arg=(key,))
- assert key in val
- assert val[key] is True
- @slowTest
- def test_pillar_refresh_pillar_items(self):
- """
- Validate the minion's pillar.item call behavior for new pillars
- """
- key = "issue-54941-items"
- # We do not expect to see the pillar beacuse it does not exist yet
- val = self.run_function("pillar.items")
- assert key not in val
- self.create_pillar(key)
- # A pillar.items call sees the pillar right away because a
- # refresh_pillar event is fired.
- val = self.run_function("pillar.items")
- assert key in val
- assert val[key] is True
- @slowTest
- def test_pillar_refresh_pillar_ping(self):
- """
- Validate the minion's test.ping does not update pillars
- See: https://github.com/saltstack/salt/issues/54941
- """
- key = "issue-54941-ping"
- # We do not expect to see the pillar beacuse it does not exist yet
- val = self.run_function("pillar.item", arg=(key,))
- assert key in val
- assert val[key] == ""
- self.create_pillar(key)
- val = self.run_function("test.ping")
- assert val is True
- # The pillar exists now but get reads it from in-memory pillars, no
- # refresh happens
- val = self.run_function("pillar.item", arg=(key,))
- assert key in val
- assert val[key] == ""
- # Calling refresh_pillar to update in-memory pillars
- ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
- assert ret is True
- # The pillar can now be read from in-memory pillars
- val = self.run_function("pillar.item", arg=(key,))
- assert key in val
- assert val[key] is True
|