Merge branch '2015.5' into '2015.8'

Conflicts:
  - doc/glossary.rst
  - salt/config.py
  - tests/unit/modules/gpg_test.py
This commit is contained in:
rallytime 2016-03-06 10:59:46 -07:00
commit 6559ea15b0
8 changed files with 95 additions and 515 deletions

View file

@ -84,6 +84,11 @@ Glossary
command are a single job. *See also*: :py:mod:`jobs runner
<salt.runners.jobs>`.
Job Cache
A storage location for job results, which may then be queried by a
salt runner or an external system. May be local to a salt master
or stored externally.
Job ID
A unique identifier to represent a given :term:`job`.
@ -252,3 +257,4 @@ Glossary
A master process which can send notices and receive replies from
minions. *See also*:
:conf_master:`worker_threads`.

View file

@ -13,6 +13,21 @@ There are a number of ways to contribute to Salt development.
For details on how to contribute documentation improvements please review
:ref:`Writing Salt Documentation <salt-docs>`.
Salt Coding Style
-----------------
SaltStack has its own coding style guide that informs contributors on various coding
approaches. Please review the :ref:`Salt Coding Style<coding-style>`_ documentation
for information about Salt's particular coding patterns.
Within the :ref:`Salt Coding Style<coding-style>`_ documentation, there is a section
about running Salt's ``.pylintrc`` file. SaltStack recommends running the ``.pylintrc``
file on any files you are changing with your code contribution before submitting a
pull request to Salt's repository. Please see the :ref:`Linting<pylint-instructions>`_
documentation for more information.
.. _github-pull-request:
Sending a GitHub pull request

View file

@ -1,3 +1,5 @@
.. _coding-style:
=================
Salt Coding Style
=================
@ -15,18 +17,29 @@ no grounds to treat others without respect, especially people working to
improve Salt)!!
.. _pylint-instructions:
Linting
=======
Most Salt style conventions are codified in Salt's ``.pylintrc`` file. This file
is found in the root of the Salt project and can be passed as an argument to the
pylint_ program as follows:
Most Salt style conventions are codified in Salt's ``.pylintrc`` file. Salt's
pylint file has two dependencies: pylint_ and saltpylint_. You can install
these dependencies with ``pip``:
.. code-block:: bash
pip install pylint
pip install saltpylint
The ``.pylintrc`` file is found in the root of the Salt project and can be passed
as an argument to the pylint_ program as follows:
.. code-block:: bash
pylint --rcfile=/path/to/salt/.pylintrc salt/dir/to/lint
.. _pylint: http://www.pylint.org
.. _saltpylint: https://github.com/saltstack/salt-pylint
Variables
=========

View file

@ -558,7 +558,7 @@ VALID_OPTS = {
'autosign_timeout': int,
# A mapping of external systems that can be used to generate topfile data.
'master_tops': bool, # FIXME Should be dict?
'master_tops': dict,
# A flag that should be set on a top-level master when it is ordering around subordinate masters
# via the use of a salt syndic

View file

@ -1292,6 +1292,13 @@ def install(name=None,
# not None, since the only way version_num is not None is if RPM
# metadata parsing was successful.
if pkg_type == 'repository':
if _yum() == 'yum':
# yum install does not support epoch without the arch, and
# we won't know what the arch will be when it's not
# provided. It could either be the OS architecture, or
# 'noarch', and we don't make that distinction in the
# pkg.list_pkgs return data.
version_num = version_num.split(':', 1)[-1]
arch = ''
try:
namepart, archpart = pkgname.rsplit('.', 1)

View file

@ -56,7 +56,8 @@ def state(
fail_minions=None,
allow_fail=0,
concurrent=False,
timeout=None):
timeout=None,
queue=False):
'''
Invoke a state run on a given target
@ -117,6 +118,9 @@ def state(
for use when multiple state runs can safely be run at the same
Do not use this flag for performance optimization.
queue
Pass ``queue=true`` through to the state function
Examples:
Run a list of sls files via :py:func:`state.sls <salt.state.sls>` on target
@ -205,6 +209,7 @@ def state(
cmd_kw['kwarg']['pillar'] = pillar
cmd_kw['kwarg']['saltenv'] = __env__
cmd_kw['kwarg']['queue'] = queue
if isinstance(concurrent, bool):
cmd_kw['kwarg']['concurrent'] = concurrent

View file

@ -45,14 +45,20 @@ _PKG_TARGETS_DOT = {
'7': 'tomcat-el-2.2-api'}
}
# Test packages with epoch in version
# (https://github.com/saltstack/salt/issues/31619)
_PKG_TARGETS_EPOCH = {
'RedHat': {'7': 'comps-extras'},
}
@destructiveTest
@requires_salt_modules('pkg.version', 'pkg.latest_version')
class PkgTest(integration.ModuleCase,
integration.SaltReturnAssertsMixIn):
'''
pkg.installed state tests
'''
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_001_installed(self, grains=None):
@ -80,7 +86,6 @@ class PkgTest(integration.ModuleCase,
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_002_installed_with_version(self, grains=None):
@ -122,7 +127,6 @@ class PkgTest(integration.ModuleCase,
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_003_installed_multipkg(self, grains=None):
@ -148,7 +152,6 @@ class PkgTest(integration.ModuleCase,
ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_004_installed_multipkg_with_version(self, grains=None):
@ -166,7 +169,7 @@ class PkgTest(integration.ModuleCase,
# Make sure that we have targets that match the os_family. If this
# fails then the _PKG_TARGETS dict above needs to have an entry added,
# with two packages that are not installed before these tests are run
self.assertTrue(pkg_targets)
self.assertTrue(bool(pkg_targets))
if os_family == 'Arch':
for idx in range(13):
@ -182,7 +185,7 @@ class PkgTest(integration.ModuleCase,
# If this assert fails, we need to find new targets, this test needs to
# be able to test successful installation of packages, so these
# packages need to not be installed before we run the states below
self.assertTrue(version)
self.assertTrue(bool(version))
pkgs = [{pkg_targets[0]: version}, pkg_targets[1]]
@ -191,7 +194,6 @@ class PkgTest(integration.ModuleCase,
ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_005_installed_32bit(self, grains=None):
@ -216,14 +218,13 @@ class PkgTest(integration.ModuleCase,
# needs to be able to test successful installation of packages, so
# the target needs to not be installed before we run the states
# below
self.assertFalse(version)
self.assertFalse(bool(version))
ret = self.run_state('pkg.installed', name=target)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_006_installed_32bit_with_version(self, grains=None):
@ -257,14 +258,13 @@ class PkgTest(integration.ModuleCase,
# needs to be able to test successful installation of the package, so
# the target needs to not be installed before we run the states
# below
self.assertTrue(version)
self.assertTrue(bool(version))
ret = self.run_state('pkg.installed', name=target, version=version)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_007_with_dot_in_pkgname(self, grains=None):
@ -276,13 +276,42 @@ class PkgTest(integration.ModuleCase,
'''
os_family = grains.get('os_family', '')
os_version = grains.get('osmajorrelease', [''])[0]
if os_family in _PKG_TARGETS_DOT:
target = _PKG_TARGETS_DOT.get(os_family, '').get(os_version, '')
else:
target = None
target = _PKG_TARGETS_DOT.get(os_family, {}).get(os_version)
if target:
version = self.run_function('pkg.latest_version', [target])
# If this assert fails, we need to find a new target. This test
# needs to be able to test successful installation of the package, so
# the target needs to not be installed before we run the
# pkg.installed state below
self.assertTrue(bool(version))
ret = self.run_state('pkg.installed', name=target)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_with_epoch_in_version(self, grains=None):
'''
This tests for the regression found in the following issue:
https://github.com/saltstack/salt/issues/8614
This is a destructive test as it installs a package
'''
os_family = grains.get('os_family', '')
os_version = grains.get('osmajorrelease', [''])[0]
target = _PKG_TARGETS_EPOCH.get(os_family, {}).get(os_version)
if target:
version = self.run_function('pkg.latest_version', [target])
# If this assert fails, we need to find a new target. This test
# needs to be able to test successful installation of the package, so
# the target needs to not be installed before we run the
# pkg.installed state below
self.assertTrue(bool(version))
ret = self.run_state('pkg.installed', name=target, version=version)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')

View file

@ -1,495 +0,0 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python Libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.mock import (
MagicMock,
mock_open,
patch,
NO_MOCK,
NO_MOCK_REASON
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.modules import gpg
from salt.exceptions import SaltInvocationError
gpg.__salt__ = {}
RET = [{'created': '2014-07-25',
'fingerprint': u'F321F',
'keyLength': u'1024',
'keyid': u'3F0C8E90D459D89A',
'ownerTrust': 'Ultimately Trusted',
'trust': 'u',
'uids': [u'Autogenerated Key (Generated by SaltStack)']}]
class Mockgnupg(object):
'''
Mock gnupg class
'''
__version__ = '1.3.1'
fingerprint = u'F321F'
counts = {}
count = ''
imported = False
imported_rsa = False
results = [{'ok': '1', 'fingerprint': u'F321F'}]
data = True
trust_level = None
ok = True
unchanged = False
not_imported = False
class GPG(object):
'''
Mock gnupg class
'''
def __init__(self, gnupghome='/tmp/salt/.gnupg',
homedir='/tmp/salt/.gnupg'):
self.gnupghome = gnupghome
self.homedir = homedir
self.text = None
self.keyserver = None
self.kwargs = None
self.obj = None
self.fingerprints = None
self.keyserver = None
self.secret = None
self.keyids = None
self.default_key = None
self.recipients = None
self.passphrase = None
def search_keys(self, text, keyserver):
'''
Mock of search_keys method
'''
self.text = text
self.keyserver = keyserver
return RET
def gen_key_input(self, **kwargs):
'''
Mock of gen_key_input method
'''
self.kwargs = kwargs
return Mockgnupg
def gen_key(self, obj):
'''
Mock of gen_key method
'''
self.obj = obj
return Mockgnupg
def list_keys(self, obj):
'''
Mock of list_keys method
'''
self.obj = obj
return RET
def delete_keys(self, fingerprints, secret=False):
'''
Mock of delete_keys method
'''
self.fingerprints = fingerprints
self.secret = secret
return 'ok'
def import_keys(self, text):
'''
Mock of import_keys method
'''
self.text = text
return Mockgnupg
def export_keys(self, keyids, secret):
'''
Mock of export_keys method
'''
self.secret = secret
self.keyids = keyids
return (keyids, secret)
def recv_keys(self, keyserver, *keyids):
'''
Mock of recv_keys method
'''
self.keyserver = keyserver
self.keyids = keyids
return Mockgnupg
def sign(self, text, default_key, passphrase):
'''
Mock of sign method
'''
self.text = text
self.default_key = default_key
self.passphrase = passphrase
return Mockgnupg
def verify(self, text):
'''
Mock of verify method
'''
self.text = text
return Mockgnupg
def encrypt(self, text, recipients, passphrase):
'''
Mock of encrypt method
'''
self.text = text
self.recipients = recipients
self.passphrase = passphrase
return Mockgnupg
def decrypt(self, text, passphrase):
'''
Mock of decrypt method
'''
self.text = text
self.passphrase = passphrase
return Mockgnupg
gpg.gnupg = Mockgnupg()
@skipIf(NO_MOCK, NO_MOCK_REASON)
class GpgTestCase(TestCase):
'''
TestCase for salt.modules.gpg
'''
# 'search_keys' function tests: 1
def test_search_keys(self):
'''
Tests if it search keys from keyserver.
'''
ret = [{'keyid': u'3F0C8E90D459D89A',
'uids': [u'Autogenerated Key (Generated by SaltStack)']}]
mock = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'user.info': mock}):
self.assertListEqual(gpg.search_keys('user@example.com',
user='username'), ret)
gpg.GPG_1_3_1 = True
self.assertRaises(SaltInvocationError, gpg.search_keys,
'user@example.com')
# 'list_keys' function tests: 1
def test_list_keys(self):
'''
Tests if it list keys in GPG keychain
'''
ret = [{'fingerprint': u'F321F', 'keyid': u'3F0C8E90D459D89A',
'trust': 'Ultimately Trusted',
'uids': [u'Autogenerated Key (Generated by SaltStack)']}]
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertListEqual(gpg.list_keys(), ret)
# 'list_secret_keys' function tests: 1
def test_list_secret_keys(self):
'''
Tests if it list secret keys in GPG keychain
'''
ret = [{'fingerprint': u'F321F', 'keyid': u'3F0C8E90D459D89A',
'trust': 'Ultimately Trusted',
'uids': [u'Autogenerated Key (Generated by SaltStack)']}]
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertListEqual(gpg.list_secret_keys(), ret)
# 'create_key' function tests: 1
def test_create_key(self):
'''
Tests if it create a key in the GPG keychain
'''
ret = {'res': True, 'fingerprint': u'F321F',
'message': 'GPG key pair successfully generated.'}
ret1 = {'fingerprint': '', 'res': False,
'message': 'gpg_passphrase not available in pillar.'}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
mock_item = MagicMock(return_value=False)
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user,
'pillar.item': mock_item}):
self.assertDictEqual(gpg.create_key(), ret)
self.assertDictEqual(gpg.create_key(use_passphrase=True), ret1)
# 'delete_key' function tests: 1
def test_delete_key(self):
'''
Tests if it delete a key from the GPG keychain
'''
ret = {'message': 'Only specify one argument, fingerprint or keyid',
'res': False}
ret1 = {'message': 'Required argument, fingerprint or keyid',
'res': False}
ret2 = {'message': ('Secret key exists, delete first'
' or pass delete_secret=True.'), 'res': False}
ret3 = {'message': ('Secret key for F321F deleted\nPublic'
' key for F321F deleted'), 'res': True}
ret4 = {'message': 'Key not available in keychain.', 'res': False}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertDictEqual(gpg.delete_key(keyid='3FAD9F1E',
fingerprint='53C'), ret)
self.assertDictEqual(gpg.delete_key(), ret1)
self.assertDictEqual(gpg.delete_key(keyid='3F0C8E90D459D89A'), ret2)
self.assertDictEqual(gpg.delete_key(keyid='3F0C8E90D459D89A',
delete_secret=True), ret3)
self.assertDictEqual(gpg.delete_key(keyid='3F0C'), ret4)
# 'get_key' function tests: 1
def test_get_key(self):
'''
Tests if it get a key from the GPG keychain
'''
ret = {'fingerprint': u'F321F', 'keyid': u'3F0C8E90D459D89A',
'trust': 'Ultimately Trusted',
'uids': [u'Autogenerated Key (Generated by SaltStack)']}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertFalse(gpg.get_key())
self.assertDictEqual(gpg.get_key(keyid='3F0C8E90D459D89A'), ret)
# 'get_secret_key' function tests: 1
def test_get_secret_key(self):
'''
Tests if it get a secret key from the GPG keychain
'''
ret = {'fingerprint': u'F321F', 'keyid': u'3F0C8E90D459D89A',
'trust': 'Ultimately Trusted',
'uids': [u'Autogenerated Key (Generated by SaltStack)']}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertFalse(gpg.get_secret_key())
self.assertDictEqual(gpg.get_secret_key(keyid='3F0C8E90D459D89A'),
ret)
# 'import_key' function tests: 1
def test_import_key(self):
'''
Tests if it import a key from text or file.
'''
ret = {'message': 'Unable to import key.', 'res': False}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertRaises(SaltInvocationError, gpg.import_key)
with patch('salt.utils.flopen', mock_open(read_data='')) as fp:
fp.side_effect = IOError()
self.assertRaises(SaltInvocationError, gpg.import_key,
filename='/path/to/public-key-file')
gpg.GPG_1_3_1 = True
self.assertDictEqual(gpg.import_key(text='-BEGIN PGP PUBLIC KEY BLOCK-'), ret)
gpg.GPG_1_3_1 = False
self.assertDictEqual(gpg.import_key(text='-BEGIN PGP PUBLIC KEY BLOCK-'), ret)
# 'export_key' function tests: 1
def test_export_key(self):
'''
Tests if it export a key from the GPG keychain
'''
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertTrue(gpg.export_key(keyids='3F0C8E90D459D89A'))
# 'receive_keys' function tests: 1
def test_receive_keys(self):
'''
Tests if it receive key(s) from keyserver and add them to keychain
'''
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user}):
self.assertDictEqual(gpg.receive_keys(keys=['3F0C8E90D459D89A']),
{'res': True,
'message': ['Key F321F added to keychain'],
'changes': {}})
# 'trust_key' function tests: 1
def test_trust_key(self):
'''
Tests if it set the trust level for a key in GPG keychain
'''
ret = {'message': 'Only specify one argument, fingerprint or keyid',
'res': False}
ret1 = {'message': 'KeyID 3F0C8 not in GPG keychain', 'res': False}
ret2 = {'message': 'Required argument, fingerprint or keyid',
'res': False}
ret3 = ('ERROR: Valid trust levels - expired,unknown,'
'not_trusted,marginally,fully,ultimately')
ret4 = {'res': False,
'message': 'Fingerprint not found for keyid 3F0C8E90D459D89A'}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
mock_cmd = MagicMock(return_value={'retcode': 1, 'stderr': 'error'})
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user,
'cmd.run_all': mock_cmd}):
self.assertDictEqual(gpg.trust_key(keyid='3F0C8E90D459D89A',
fingerprint='53C'), ret)
self.assertDictEqual(gpg.trust_key(keyid='3F0C8'), ret1)
self.assertDictEqual(gpg.trust_key(), ret2)
self.assertEqual(gpg.trust_key(fingerprint='53C9'), ret3)
self.assertEqual(gpg.trust_key(fingerprint='53C96',
trust_level='not_trusted'),
{'res': False, 'message': 'error'})
with patch.object(gpg, 'get_key', MagicMock(return_value=RET)):
self.assertDictEqual(gpg.trust_key(keyid='3F0C8E90D459D89A'),
ret4)
# 'sign' function tests: 1
def test_sign(self):
'''
Tests if it sign message or file
'''
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
mock_pillar = MagicMock(return_value=False)
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user,
'pillar.item': mock_pillar}):
self.assertRaises(SaltInvocationError, gpg.sign,
use_passphrase=True)
self.assertRaises(SaltInvocationError, gpg.sign)
self.assertTrue(gpg.sign(text='Hello there. How are you?'))
# 'verify' function tests: 1
def test_verify(self):
'''
Tests if it verify a message or file
'''
ret = {'message': 'The signature could not be verified.', 'res': False}
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
mock_pillar = MagicMock(return_value=False)
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user,
'pillar.item': mock_pillar}):
self.assertRaises(SaltInvocationError, gpg.verify)
self.assertDictEqual(gpg.verify(text='Hello there. How are you?'),
ret)
# 'encrypt' function tests: 1
def test_encrypt(self):
'''
Tests if it encrypt a message or file
'''
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
mock_pillar = MagicMock(return_value=False)
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user,
'pillar.item': mock_pillar}):
self.assertRaises(SaltInvocationError, gpg.encrypt,
use_passphrase=True)
self.assertRaises(SaltInvocationError, gpg.encrypt)
self.assertDictEqual(gpg.encrypt(text='Hello there. How are you?'),
{'comment': True, 'res': True})
# 'decrypt' function tests: 1
def test_decrypt(self):
'''
Tests if it decrypt a message or file
'''
mock_conf = MagicMock(return_value='')
mock_user = MagicMock(return_value={'home': 'salt'})
mock_pillar = MagicMock(return_value=False)
with patch.dict(gpg.__salt__, {'config.option': mock_conf,
'user.info': mock_user,
'pillar.item': mock_pillar}):
self.assertRaises(SaltInvocationError, gpg.decrypt,
use_passphrase=True)
self.assertRaises(SaltInvocationError, gpg.decrypt)
self.assertDictEqual(gpg.decrypt(text='Hello there. How are you?'),
{'comment': True, 'res': True})
if __name__ == '__main__':
from integration import run_tests
run_tests(GpgTestCase, needs_daemon=False)