mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Migrate integration.minion.test_pillar
to PyTest
This commit is contained in:
parent
0c349e0f83
commit
19c72464df
9 changed files with 707 additions and 732 deletions
|
@ -180,6 +180,9 @@ salt/minion.py:
|
|||
- integration.minion.test_pillar
|
||||
- integration.minion.test_timeout
|
||||
- integration.shell.test_matcher
|
||||
- pytests.functional.pillar.test_top
|
||||
- pytests.functional.pillar.test_gpg
|
||||
- pytests.integration.modules.test_pillar
|
||||
|
||||
salt/modules/*_sysctl.py:
|
||||
- unit.states.test_sysctl
|
||||
|
@ -199,8 +202,10 @@ salt/output/*:
|
|||
- integration.output.test_output
|
||||
|
||||
salt/pillar/__init__.py:
|
||||
- integration.minion.test_pillar
|
||||
- integration.pillar.test_pillar_include
|
||||
- pytests.functional.pillar.test_top
|
||||
- pytests.functional.pillar.test_gpg
|
||||
- pytests.integration.modules.test_pillar
|
||||
|
||||
salt/(cli/run\.py|runner\.py):
|
||||
- pytests.integration.cli.test_salt_run
|
||||
|
|
|
@ -1,729 +0,0 @@
|
|||
# -*- 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
|
0
tests/pytests/functional/pillar/__init__.py
Normal file
0
tests/pytests/functional/pillar/__init__.py
Normal file
55
tests/pytests/functional/pillar/conftest.py
Normal file
55
tests/pytests/functional/pillar/conftest.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
import shutil
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def pillar_state_tree(tmp_path_factory):
|
||||
_pillar_state_tree = tmp_path_factory.mktemp("pillar")
|
||||
try:
|
||||
yield _pillar_state_tree
|
||||
finally:
|
||||
shutil.rmtree(str(_pillar_state_tree), ignore_errors=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def extension_modules(tmp_path_factory):
|
||||
_extension_modules = tmp_path_factory.mktemp("pillar")
|
||||
try:
|
||||
yield _extension_modules
|
||||
finally:
|
||||
shutil.rmtree(str(_extension_modules), ignore_errors=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def salt_master(salt_factories, pillar_state_tree, extension_modules):
|
||||
config_defaults = {
|
||||
"pillar_roots": {"base": [str(pillar_state_tree)]},
|
||||
"open_mode": True,
|
||||
"extension_modules": str(extension_modules),
|
||||
"ext_pillar_first": False,
|
||||
"ext_pillar": [],
|
||||
"decrypt_pillar_default": "gpg",
|
||||
"decrypt_pillar_delimiter": ":",
|
||||
"decrypt_pillar_renderers": ["gpg"],
|
||||
}
|
||||
factory = salt_factories.get_salt_master_daemon(
|
||||
"pillar-functional-master", config_defaults=config_defaults
|
||||
)
|
||||
return factory
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def salt_minion_1(salt_master):
|
||||
factory = salt_master.get_salt_minion_daemon(
|
||||
"pillar-functional-minion-1", config_defaults={"open_mode": True}
|
||||
)
|
||||
return factory
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def salt_minion_2(salt_master):
|
||||
factory = salt_master.get_salt_minion_daemon(
|
||||
"pillar-functional-minion-2", config_defaults={"open_mode": True}
|
||||
)
|
||||
return factory
|
389
tests/pytests/functional/pillar/test_gpg.py
Normal file
389
tests/pytests/functional/pillar/test_gpg.py
Normal file
|
@ -0,0 +1,389 @@
|
|||
import copy
|
||||
import logging
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
import salt.pillar
|
||||
import salt.utils.stringutils
|
||||
from saltfactories.utils.processes import ProcessResult
|
||||
from tests.support.helpers import slowTest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.windows_whitelisted,
|
||||
pytest.mark.skip_if_binaries_missing("gpg"),
|
||||
]
|
||||
|
||||
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"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="package", autouse=True)
|
||||
def gpg_homedir(salt_master, pillar_state_tree):
|
||||
_gpg_homedir = pathlib.Path(salt_master.config_dir) / "gpgkeys"
|
||||
_gpg_homedir.mkdir(0o700)
|
||||
agent_started = False
|
||||
try:
|
||||
|
||||
cmd_prefix = ["gpg", "--homedir", str(_gpg_homedir)]
|
||||
|
||||
cmd = cmd_prefix + ["--list-keys"]
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=True,
|
||||
universal_newlines=True,
|
||||
)
|
||||
ret = ProcessResult(
|
||||
exitcode=proc.returncode,
|
||||
stdout=proc.stdout,
|
||||
stderr=proc.stderr,
|
||||
cmdline=proc.args,
|
||||
)
|
||||
log.debug("Instantiating gpg keyring...\n%s", ret)
|
||||
|
||||
cmd = cmd_prefix + ["--import", "--allow-secret-key-import"]
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=True,
|
||||
universal_newlines=True,
|
||||
input=TEST_KEY,
|
||||
)
|
||||
ret = ProcessResult(
|
||||
exitcode=proc.returncode,
|
||||
stdout=proc.stdout,
|
||||
stderr=proc.stderr,
|
||||
cmdline=proc.args,
|
||||
)
|
||||
log.debug("Importing keypair...:\n%s", ret)
|
||||
agent_started = True
|
||||
|
||||
top_file_contents = """
|
||||
base:
|
||||
'*':
|
||||
- gpg
|
||||
"""
|
||||
with pytest.helpers.temp_file(
|
||||
"top.sls", top_file_contents, pillar_state_tree
|
||||
), pytest.helpers.temp_file("gpg.sls", GPG_PILLAR_YAML, pillar_state_tree):
|
||||
yield _gpg_homedir
|
||||
finally:
|
||||
if agent_started:
|
||||
try:
|
||||
cmd = ["gpg-connect-agent", "--homedir", str(_gpg_homedir)]
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=True,
|
||||
universal_newlines=True,
|
||||
input="KILLAGENT",
|
||||
)
|
||||
ret = ProcessResult(
|
||||
exitcode=proc.returncode,
|
||||
stdout=proc.stdout,
|
||||
stderr=proc.stderr,
|
||||
cmdline=proc.args,
|
||||
)
|
||||
log.debug("Killed gpg-agent...\n%s", ret)
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
log.debug("No need to kill: old gnupg doesn't start the agent.")
|
||||
shutil.rmtree(str(_gpg_homedir), ignore_errors=True)
|
||||
|
||||
|
||||
def test_decrypt_pillar_default_renderer(salt_master, grains):
|
||||
"""
|
||||
Test recursive decryption of secrets:vault as well as the fallback to
|
||||
default decryption renderer.
|
||||
"""
|
||||
opts = salt_master.config.copy()
|
||||
opts["decrypt_pillar"] = ["secrets:vault"]
|
||||
pillar_obj = salt.pillar.Pillar(opts, grains, "test", "base")
|
||||
ret = pillar_obj.compile_pillar()
|
||||
assert ret == GPG_PILLAR_DECRYPTED
|
||||
|
||||
|
||||
@slowTest
|
||||
def test_decrypt_pillar_alternate_delimiter(salt_master, grains):
|
||||
"""
|
||||
Test recursive decryption of secrets:vault using a pipe instead of a
|
||||
colon as the nesting delimiter.
|
||||
|
||||
decrypt_pillar_delimiter: '|'
|
||||
decrypt_pillar:
|
||||
- 'secrets|vault'
|
||||
"""
|
||||
opts = salt_master.config.copy()
|
||||
opts["decrypt_pillar"] = ["secrets|vault"]
|
||||
opts["decrypt_pillar_delimiter"] = "|"
|
||||
pillar_obj = salt.pillar.Pillar(opts, grains, "test", "base")
|
||||
ret = pillar_obj.compile_pillar()
|
||||
assert ret == GPG_PILLAR_DECRYPTED
|
||||
|
||||
|
||||
def test_decrypt_pillar_deeper_nesting(salt_master, grains):
|
||||
"""
|
||||
Test recursive decryption, only with a more deeply-nested target. This
|
||||
should leave the other keys in secrets:vault encrypted.
|
||||
|
||||
decrypt_pillar:
|
||||
- 'secrets:vault:qux'
|
||||
"""
|
||||
opts = salt_master.config.copy()
|
||||
opts["decrypt_pillar"] = ["secrets:vault:qux"]
|
||||
pillar_obj = salt.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]
|
||||
assert ret == expected
|
||||
|
||||
|
||||
def test_decrypt_pillar_explicit_renderer(salt_master, grains):
|
||||
"""
|
||||
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_default: asdf
|
||||
decrypt_pillar_renderers:
|
||||
- asdf
|
||||
- gpg
|
||||
decrypt_pillar:
|
||||
- 'secrets:vault': gpg
|
||||
"""
|
||||
opts = salt_master.config.copy()
|
||||
opts["decrypt_pillar"] = [{"secrets:vault": "gpg"}]
|
||||
opts["decrypt_pillar_default"] = "asdf"
|
||||
opts["decrypt_pillar_renderers"] = ["asdf", "gpg"]
|
||||
pillar_obj = salt.pillar.Pillar(opts, grains, "test", "base")
|
||||
ret = pillar_obj.compile_pillar()
|
||||
assert ret == GPG_PILLAR_DECRYPTED
|
||||
|
||||
|
||||
def test_decrypt_pillar_missing_renderer(salt_master, grains):
|
||||
"""
|
||||
Test decryption using a missing renderer. It should fail, leaving the
|
||||
encrypted keys intact, and add an error to the pillar dictionary.
|
||||
|
||||
decrypt_pillar_default: asdf
|
||||
decrypt_pillar_renderers:
|
||||
- asdf
|
||||
decrypt_pillar:
|
||||
- 'secrets:vault'
|
||||
"""
|
||||
opts = salt_master.config.copy()
|
||||
opts["decrypt_pillar"] = ["secrets:vault"]
|
||||
opts["decrypt_pillar_default"] = "asdf"
|
||||
opts["decrypt_pillar_renderers"] = ["asdf"]
|
||||
pillar_obj = salt.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"
|
||||
]
|
||||
assert ret["_errors"] == expected["_errors"]
|
||||
assert ret["secrets"]["vault"]["foo"] == expected["secrets"]["vault"]["foo"]
|
||||
assert ret["secrets"]["vault"]["bar"] == expected["secrets"]["vault"]["bar"]
|
||||
assert ret["secrets"]["vault"]["baz"] == expected["secrets"]["vault"]["baz"]
|
||||
assert ret["secrets"]["vault"]["qux"] == expected["secrets"]["vault"]["qux"]
|
||||
|
||||
|
||||
def test_decrypt_pillar_invalid_renderer(salt_master, grains):
|
||||
"""
|
||||
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_default: foo
|
||||
decrypt_pillar_renderers:
|
||||
- foo
|
||||
- bar
|
||||
decrypt_pillar:
|
||||
- 'secrets:vault': gpg
|
||||
"""
|
||||
opts = salt_master.config.copy()
|
||||
opts["decrypt_pillar"] = [{"secrets:vault": "gpg"}]
|
||||
opts["decrypt_pillar_default"] = "foo"
|
||||
opts["decrypt_pillar_renderers"] = ["foo", "bar"]
|
||||
pillar_obj = salt.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"
|
||||
]
|
||||
assert ret["_errors"] == expected["_errors"]
|
||||
assert ret["secrets"]["vault"]["foo"] == expected["secrets"]["vault"]["foo"]
|
||||
assert ret["secrets"]["vault"]["bar"] == expected["secrets"]["vault"]["bar"]
|
||||
assert ret["secrets"]["vault"]["baz"] == expected["secrets"]["vault"]["baz"]
|
||||
assert ret["secrets"]["vault"]["qux"] == expected["secrets"]["vault"]["qux"]
|
45
tests/pytests/functional/pillar/test_top.py
Normal file
45
tests/pytests/functional/pillar/test_top.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
import pytest
|
||||
import salt.pillar
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.windows_whitelisted,
|
||||
]
|
||||
|
||||
|
||||
def test_pillar_top_compound_match(salt_master, pillar_state_tree, grains):
|
||||
"""
|
||||
Test that a compound match topfile that refers to a nodegroup via N@ works
|
||||
as expected.
|
||||
"""
|
||||
top_file_contents = """
|
||||
base:
|
||||
'N@mins not L@minion':
|
||||
- ng1
|
||||
'N@missing_minion':
|
||||
- ng2
|
||||
"""
|
||||
ng1_pillar_contents = "pillar_from_nodegroup: True"
|
||||
ng2_pillar_contents = "pillar_from_nodegroup_with_ghost: True"
|
||||
with pytest.helpers.temp_file(
|
||||
"top.sls", top_file_contents, pillar_state_tree
|
||||
), pytest.helpers.temp_file(
|
||||
"ng1.sls", ng1_pillar_contents, pillar_state_tree
|
||||
), pytest.helpers.temp_file(
|
||||
"ng2.sls", ng2_pillar_contents, pillar_state_tree
|
||||
):
|
||||
opts = salt_master.config.copy()
|
||||
opts["nodegroups"] = {
|
||||
"min": "minion",
|
||||
"sub_min": "sub_minion",
|
||||
"mins": "N@min or N@sub_min",
|
||||
"missing_minion": "L@minion,ghostminion",
|
||||
}
|
||||
pillar_obj = salt.pillar.Pillar(opts, grains, "minion", "base")
|
||||
ret = pillar_obj.compile_pillar()
|
||||
assert ret.get("pillar_from_nodegroup_with_ghost") is True
|
||||
assert ret.get("pillar_from_nodegroup") is None
|
||||
|
||||
sub_pillar_obj = salt.pillar.Pillar(opts, grains, "sub_minion", "base")
|
||||
sub_ret = sub_pillar_obj.compile_pillar()
|
||||
assert sub_ret.get("pillar_from_nodegroup_with_ghost") is None
|
||||
assert sub_ret.get("pillar_from_nodegroup") is True
|
|
@ -1,5 +1,7 @@
|
|||
import pathlib
|
||||
import textwrap
|
||||
|
||||
import attr
|
||||
import pytest
|
||||
from tests.support.helpers import slowTest
|
||||
|
||||
|
@ -154,3 +156,213 @@ def test_pillar_command_line(salt_call_cli, pillar_tree):
|
|||
pillar_items = ret.json
|
||||
assert "new" in pillar_items
|
||||
assert pillar_items["new"] == "additional"
|
||||
|
||||
|
||||
@attr.s
|
||||
class PillarRefresh:
|
||||
pillar_state_tree = attr.ib(repr=False)
|
||||
salt_cli = attr.ib(repr=False)
|
||||
top_file = attr.ib(init=False)
|
||||
minion_1_id = attr.ib(repr=False)
|
||||
minion_1_pillar = attr.ib(init=False)
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
self.top_file = self.pillar_state_tree / "top.sls"
|
||||
top_file_contents = textwrap.dedent(
|
||||
"""\
|
||||
base:
|
||||
{}:
|
||||
- minion-1-pillar
|
||||
""".format(
|
||||
self.minion_1_id
|
||||
)
|
||||
)
|
||||
self.top_file.write_text(top_file_contents)
|
||||
self.minion_1_pillar = self.pillar_state_tree / "minion-1-pillar.sls"
|
||||
|
||||
def refresh_pillar(self, timeout=60, sleep=0.5):
|
||||
ret = self.salt_cli.run(
|
||||
"saltutil.refresh_pillar", wait=True, minion_tgt=self.minion_1_id
|
||||
)
|
||||
assert ret.exitcode == 0
|
||||
assert ret.json is True
|
||||
|
||||
def __call__(self, pillar_key):
|
||||
pillar_contents = "{}: true".format(pillar_key)
|
||||
self.minion_1_pillar.write_text(pillar_contents)
|
||||
return self
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.minion_1_pillar.unlink()
|
||||
self.refresh_pillar()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def key_pillar(salt_minion, salt_cli, base_env_pillar_tree_root_dir):
|
||||
return PillarRefresh(
|
||||
pillar_state_tree=base_env_pillar_tree_root_dir,
|
||||
salt_cli=salt_cli,
|
||||
minion_1_id=salt_minion.id,
|
||||
)
|
||||
|
||||
|
||||
@slowTest
|
||||
def test_pillar_refresh_pillar_raw(salt_cli, salt_minion, key_pillar):
|
||||
"""
|
||||
Validate the minion's pillar.raw call behavior for new pillars
|
||||
"""
|
||||
key = "issue-54941-raw"
|
||||
|
||||
# We do not expect to see the pillar because it does not exist yet
|
||||
ret = salt_cli.run("pillar.raw", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val == {}
|
||||
|
||||
with key_pillar(key):
|
||||
# The pillar exists now but raw reads it from in-memory pillars
|
||||
ret = salt_cli.run("pillar.raw", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val == {}
|
||||
|
||||
# Calling refresh_pillar to update in-memory pillars
|
||||
key_pillar.refresh_pillar()
|
||||
|
||||
# The pillar can now be read from in-memory pillars
|
||||
ret = salt_cli.run("pillar.raw", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val is True, repr(val)
|
||||
|
||||
|
||||
@slowTest
|
||||
def test_pillar_refresh_pillar_get(salt_cli, salt_minion, key_pillar):
|
||||
"""
|
||||
Validate the minion's pillar.get call behavior for new pillars
|
||||
"""
|
||||
key = "issue-54941-get"
|
||||
|
||||
# We do not expect to see the pillar because it does not exist yet
|
||||
ret = salt_cli.run("pillar.get", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val == ""
|
||||
|
||||
with key_pillar(key):
|
||||
# The pillar exists now but get reads it from in-memory pillars, no
|
||||
# refresh happens
|
||||
ret = salt_cli.run("pillar.get", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val == ""
|
||||
|
||||
# Calling refresh_pillar to update in-memory pillars
|
||||
key_pillar.refresh_pillar()
|
||||
|
||||
# The pillar can now be read from in-memory pillars
|
||||
ret = salt_cli.run("pillar.get", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val is True, repr(val)
|
||||
|
||||
|
||||
@slowTest
|
||||
def test_pillar_refresh_pillar_item(salt_cli, salt_minion, key_pillar):
|
||||
"""
|
||||
Validate the minion's pillar.item call behavior for new pillars
|
||||
"""
|
||||
key = "issue-54941-item"
|
||||
|
||||
# We do not expect to see the pillar because it does not exist yet
|
||||
ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] == ""
|
||||
|
||||
with key_pillar(key):
|
||||
# The pillar exists now but get reads it from in-memory pillars, no
|
||||
# refresh happens
|
||||
ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] == ""
|
||||
|
||||
# Calling refresh_pillar to update in-memory pillars
|
||||
key_pillar.refresh_pillar()
|
||||
|
||||
# The pillar can now be read from in-memory pillars
|
||||
ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] is True
|
||||
|
||||
|
||||
@slowTest
|
||||
def test_pillar_refresh_pillar_items(salt_cli, salt_minion, key_pillar):
|
||||
"""
|
||||
Validate the minion's pillar.item call behavior for new pillars
|
||||
"""
|
||||
key = "issue-54941-items"
|
||||
|
||||
# We do not expect to see the pillar because it does not exist yet
|
||||
ret = salt_cli.run("pillar.items", minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key not in val
|
||||
|
||||
with key_pillar(key):
|
||||
# A pillar.items call sees the pillar right away because a
|
||||
# refresh_pillar event is fired.
|
||||
ret = salt_cli.run("pillar.items", minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] is True
|
||||
|
||||
|
||||
@slowTest
|
||||
def test_pillar_refresh_pillar_ping(salt_cli, salt_minion, key_pillar):
|
||||
"""
|
||||
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 because it does not exist yet
|
||||
ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] == ""
|
||||
|
||||
with key_pillar(key):
|
||||
ret = salt_cli.run("test.ping", minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert val is True
|
||||
|
||||
# The pillar exists now but get reads it from in-memory pillars, no
|
||||
# refresh happens
|
||||
ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] == ""
|
||||
|
||||
# Calling refresh_pillar to update in-memory pillars
|
||||
key_pillar.refresh_pillar()
|
||||
|
||||
# The pillar can now be read from in-memory pillars
|
||||
ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id)
|
||||
assert ret.exitcode == 0
|
||||
val = ret.json
|
||||
assert key in val
|
||||
assert val[key] is True
|
||||
|
|
|
@ -127,7 +127,6 @@ class BadTestModuleNamesTestCase(TestCase):
|
|||
"integration.master.test_event_return",
|
||||
"integration.minion.test_executor",
|
||||
"integration.minion.test_minion_cache",
|
||||
"integration.minion.test_pillar",
|
||||
"integration.minion.test_timeout",
|
||||
"integration.modules.test_decorators",
|
||||
"integration.modules.test_pkg",
|
||||
|
|
|
@ -10,7 +10,6 @@ integration.grains.test_custom
|
|||
integration.loader.test_ext_grains
|
||||
integration.loader.test_ext_modules
|
||||
integration.master.test_clear_funcs
|
||||
integration.minion.test_pillar
|
||||
integration.minion.test_timeout
|
||||
integration.modules.test_aliases
|
||||
integration.modules.test_archive
|
||||
|
|
Loading…
Add table
Reference in a new issue