diff options
author | Scott Moser <smoser@ubuntu.com> | 2018-06-28 15:36:50 -0400 |
---|---|---|
committer | Scott Moser <smoser@brickies.net> | 2018-06-28 15:36:50 -0400 |
commit | bb2cc5dde5f2c70c3a6b6c1c1834fa8780677038 (patch) | |
tree | 415eaa12b1d1f2429420dc8ec6d930bd13fd0486 /cloudinit/tests/test_gpg.py | |
parent | c42a926ae730994f66fe87c264b65f6e4dca69a1 (diff) | |
download | vyos-cloud-init-bb2cc5dde5f2c70c3a6b6c1c1834fa8780677038.tar.gz vyos-cloud-init-bb2cc5dde5f2c70c3a6b6c1c1834fa8780677038.zip |
Retry on failed import of gpg receive keys.
When cloud-init tries to read a key from a keyserver, it will now
retry twice with 1 second in between each.
Retries of import are done by default because keyservers can be
unreliable. Additionally, there is no way to determine the difference
between a non-existant key and a failure. In both cases gpg (at least
2.2.4) exits with status 2 and stderr: "keyserver receive failed: No data"
It is assumed that a key provided to cloud-init exists on the keyserver so
re-trying makes better sense than failing.
Examples of things that made receive keys particularly unreliable:
https://bitbucket.org/skskeyserver/sks-keyserver/issues/57
https://bitbucket.org/skskeyserver/sks-keyserver/issues/60
There is also a change here from 'gpg --recv' to the longer
'gpg --recv-keys'. That option is functional and working back to
centos 6 (gpg 2.0.14) and ubuntu 14.04 (gpg 1.4.16).
Diffstat (limited to 'cloudinit/tests/test_gpg.py')
-rw-r--r-- | cloudinit/tests/test_gpg.py | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/cloudinit/tests/test_gpg.py b/cloudinit/tests/test_gpg.py new file mode 100644 index 00000000..0562b966 --- /dev/null +++ b/cloudinit/tests/test_gpg.py @@ -0,0 +1,54 @@ +# This file is part of cloud-init. See LICENSE file for license information. +"""Test gpg module.""" + +from cloudinit import gpg +from cloudinit import util +from cloudinit.tests.helpers import CiTestCase + +import mock + + +@mock.patch("cloudinit.gpg.time.sleep") +@mock.patch("cloudinit.gpg.util.subp") +class TestReceiveKeys(CiTestCase): + """Test the recv_key method.""" + + def test_retries_on_subp_exc(self, m_subp, m_sleep): + """retry should be done on gpg receive keys failure.""" + retries = (1, 2, 4) + my_exc = util.ProcessExecutionError( + stdout='', stderr='', exit_code=2, cmd=['mycmd']) + m_subp.side_effect = (my_exc, my_exc, ('', '')) + gpg.recv_key("ABCD", "keyserver.example.com", retries=retries) + self.assertEqual([mock.call(1), mock.call(2)], m_sleep.call_args_list) + + def test_raises_error_after_retries(self, m_subp, m_sleep): + """If the final run fails, error should be raised.""" + naplen = 1 + keyid, keyserver = ("ABCD", "keyserver.example.com") + m_subp.side_effect = util.ProcessExecutionError( + stdout='', stderr='', exit_code=2, cmd=['mycmd']) + with self.assertRaises(ValueError) as rcm: + gpg.recv_key(keyid, keyserver, retries=(naplen,)) + self.assertIn(keyid, str(rcm.exception)) + self.assertIn(keyserver, str(rcm.exception)) + m_sleep.assert_called_with(naplen) + + def test_no_retries_on_none(self, m_subp, m_sleep): + """retry should not be done if retries is None.""" + m_subp.side_effect = util.ProcessExecutionError( + stdout='', stderr='', exit_code=2, cmd=['mycmd']) + with self.assertRaises(ValueError): + gpg.recv_key("ABCD", "keyserver.example.com", retries=None) + m_sleep.assert_not_called() + + def test_expected_gpg_command(self, m_subp, m_sleep): + """Verify gpg is called with expected args.""" + key, keyserver = ("DEADBEEF", "keyserver.example.com") + retries = (1, 2, 4) + m_subp.return_value = ('', '') + gpg.recv_key(key, keyserver, retries=retries) + m_subp.assert_called_once_with( + ['gpg', '--keyserver=%s' % keyserver, '--recv-keys', key], + capture=True) + m_sleep.assert_not_called() |