Use atomic open to write cache

Otherwise a race can happen between Salt processes and results in unpack error:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/salt/pillar/__init__.py", line 745, in render_pstate
    **defaults)
  File "/usr/lib/python3/dist-packages/salt/template.py", line 101, in compile_template
    ret = render(input_data, saltenv, sls, **render_kwargs)
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 378, in render
    return _decrypt_object(gpg_data, translate_newlines=translate_newlines, encoding=kwargs.get('encoding', None))
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 357, in _decrypt_object
   translate_newlines=translate_newlines)
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 353, in _decrypt_object
    return _decrypt_ciphertexts(obj, translate_newlines=translate_newlines, encoding=encoding)
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 328, in _decrypt_ciphertexts
    ret, num = GPG_CIPHERTEXT.subn(replace, to_bytes(cipher))
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 325, in replace
    result = to_bytes(_decrypt_ciphertext(match.group()))
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 298, in _decrypt_ciphertext
    cache = _get_cache()
  File "/usr/lib/python3/dist-packages/salt/renderers/gpg.py", line 280, in _get_cache
    minion_cache_path=os.path.join(cachedir, 'gpg_cache')
  File "/usr/lib/python3/dist-packages/salt/utils/cache.py", line 37, in factory
    return CacheDisk(ttl, kwargs['minion_cache_path'], *args, **kwargs)
  File "/usr/lib/python3/dist-packages/salt/utils/cache.py", line 90, in __init__
    self._read()
  File "/usr/lib/python3/dist-packages/salt/utils/cache.py", line 138, in _read
    cache = salt.utils.data.decode(salt.utils.msgpack.load(fp_, encoding=__salt_system_encoding__))
  File "/usr/lib/python3/dist-packages/salt/utils/msgpack.py", line 116, in unpack
    return msgpack.unpack(stream, **_sanitize_msgpack_kwargs(kwargs))
  File "/usr/lib/python3/dist-packages/msgpack/__init__.py", line 58, in unpack
    return unpackb(data, **kwargs)
  File "msgpack/_unpacker.pyx", line 211, in msgpack._unpacker.unpackb
msgpack.exceptions.UnpackValueError: Unpack failed: error = 0
This commit is contained in:
Mathieu Parent 2020-06-25 16:17:15 +02:00 committed by Daniel Wozniak
parent 219932c4be
commit fec3c93e4e
3 changed files with 34 additions and 18 deletions

1
changelog/57798.fixed Normal file
View file

@ -0,0 +1 @@
Fixes UnpackValueError when using GPG cache by using atomic open.

View file

@ -13,6 +13,7 @@ import time
# Import salt libs
import salt.config
import salt.payload
import salt.utils.atomicfile
import salt.utils.data
import salt.utils.dictupdate
import salt.utils.files
@ -161,7 +162,7 @@ class CacheDisk(CacheDict):
return
# TODO Add check into preflight to ensure dir exists
# TODO Dir hashing?
with salt.utils.files.fopen(self._path, "wb+") as fp_:
with salt.utils.atomicfile.atomic_open(self._path, "wb+") as fp_:
cache = {
"CacheDisk_data": self._dict,
"CacheDisk_cachetime": self._key_cache_time,

View file

@ -3,6 +3,7 @@
# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals
import os
from subprocess import PIPE
from textwrap import dedent
@ -277,20 +278,33 @@ class GPGTestCase(TestCase, LoaderModuleMockMixin, AdaptedConfigurationTestCaseM
"salt.renderers.gpg._get_key_dir",
MagicMock(return_value=key_dir),
):
self.assertEqual(gpg.render(crypted), expected)
gpg_call = call(
[
"/usr/bin/gpg",
"--homedir",
"/etc/salt/gpgkeys",
"--status-fd",
"2",
"--no-tty",
"-d",
],
shell=False,
stderr=PIPE,
stdin=PIPE,
stdout=PIPE,
)
popen_mock.assert_has_calls([gpg_call] * 1)
with patch(
"salt.utils.atomicfile.atomic_open", MagicMock(),
) as atomic_open_mock:
self.assertEqual(gpg.render(crypted), expected)
gpg_call = call(
[
"/usr/bin/gpg",
"--homedir",
"/etc/salt/gpgkeys",
"--status-fd",
"2",
"--no-tty",
"-d",
],
shell=False,
stderr=PIPE,
stdin=PIPE,
stdout=PIPE,
)
popen_mock.assert_has_calls([gpg_call] * 1)
atomic_open_mock.assert_has_calls(
[
call(
os.path.join(
minion_opts["cachedir"], "gpg_cache"
),
"wb+",
)
]
)