From 5deeee31c0ebbbf15642a928d9c30e42150bbfc2 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 1 Sep 2023 07:18:15 +0200 Subject: [PATCH] Add 'cache_dir' option to api.env `api.env` now has a `cache_dir` option, which defaults to `os.path.join(USER_CACHE_PATH, 'ipa')`. Schema cache, server info, and KRA key cache use `api.env.cache_dir` as base directory. The option allows application to set a custom cache directory. Related: https://pagure.io/freeipa/issue/9438 Related: https://bugzilla.redhat.com/show_bug.cgi?id=1513934 Signed-off-by: Christian Heimes Reviewed-By: Alexander Bokovoy Reviewed-By: Rob Crittenden --- ipaclient/plugins/vault.py | 8 +++----- ipaclient/remote_plugins/__init__.py | 12 +++++------- ipaclient/remote_plugins/schema.py | 13 ++++++------- ipalib/config.py | 6 ++++++ ipalib/constants.py | 1 + pylint_plugins.py | 1 + 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py index 664278a1d..bdd988ad1 100644 --- a/ipaclient/plugins/vault.py +++ b/ipaclient/plugins/vault.py @@ -550,10 +550,8 @@ class vault_mod(Local): class _KraConfigCache: """The KRA config cache stores vaultconfig-show result. """ - def __init__(self): - self._dirname = os.path.join( - constants.USER_CACHE_PATH, 'ipa', 'kra-config' - ) + def __init__(self, api): + self._dirname = os.path.join(api.env.cache_dir, 'kra-config') def _get_filename(self, domain): basename = DNSName(domain).ToASCII() + '.json' @@ -629,7 +627,7 @@ class _KraConfigCache: return True -_kra_config_cache = _KraConfigCache() +_kra_config_cache = _KraConfigCache(api) @register(override=True, no_fail=True) diff --git a/ipaclient/remote_plugins/__init__.py b/ipaclient/remote_plugins/__init__.py index 5c0836aff..f1c4347fc 100644 --- a/ipaclient/remote_plugins/__init__.py +++ b/ipaclient/remote_plugins/__init__.py @@ -13,18 +13,16 @@ import time from . import compat from . import schema from ipaclient.plugins.rpcclient import rpcclient -from ipalib.constants import USER_CACHE_PATH from ipapython.dnsutil import DNSName logger = logging.getLogger(__name__) class ServerInfo(MutableMapping): - _DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'servers') - def __init__(self, api): + self._dir = os.path.join(api.env.cache_dir, "servers") hostname = DNSName(api.env.server).ToASCII() - self._path = os.path.join(self._DIR, hostname) + self._filename = os.path.join(self._dir, hostname) self._force_check = api.env.force_schema_check self._now = time.time() self._dict = {} @@ -41,7 +39,7 @@ class ServerInfo(MutableMapping): def _read(self): try: - with open(self._path, 'r') as sc: + with open(self._filename, 'r') as sc: self._dict = json.load(sc) except Exception as e: if (isinstance(e, EnvironmentError) and @@ -56,11 +54,11 @@ class ServerInfo(MutableMapping): def _write(self): try: try: - os.makedirs(self._DIR) + os.makedirs(self._dir) except EnvironmentError as e: if e.errno != errno.EEXIST: raise - with open(self._path, 'w') as sc: + with open(self._filename, 'w') as sc: json.dump(self._dict, sc) except EnvironmentError as e: logger.warning('Failed to write server info: %s', e) diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py index 2433bf268..324763c46 100644 --- a/ipaclient/remote_plugins/schema.py +++ b/ipaclient/remote_plugins/schema.py @@ -18,7 +18,6 @@ import six from ipaclient.frontend import ClientCommand, ClientMethod from ipalib import errors, parameters, plugable -from ipalib.constants import USER_CACHE_PATH from ipalib.errors import SchemaUpToDate from ipalib.frontend import Object from ipalib.output import Output @@ -367,9 +366,9 @@ class Schema: """ namespaces = {'classes', 'commands', 'topics'} - _DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'schema', FORMAT) def __init__(self, client, fingerprint=None, ttl=0): + self._dir = os.path.join(client.api.env.cache_dir, 'schema', FORMAT) self._dict = {} self._namespaces = {} self._help = None @@ -409,7 +408,7 @@ class Schema: fps = [] if not ignore_cache: try: - fps = [fsdecode(f) for f in os.listdir(self._DIR)] + fps = [fsdecode(f) for f in os.listdir(self._dir)] except EnvironmentError: pass @@ -439,7 +438,7 @@ class Schema: def _read_schema(self, fingerprint): # It's more efficient to read zip file members at once than to open # the zip file a couple of times, see #6690. - filename = os.path.join(self._DIR, fingerprint) + filename = os.path.join(self._dir, fingerprint) with zipfile.ZipFile(filename, 'r') as schema: for name in schema.namelist(): ns, _slash, key = name.partition('/') @@ -477,13 +476,13 @@ class Schema: def _write_schema(self, fingerprint): try: - os.makedirs(self._DIR) + os.makedirs(self._dir) except EnvironmentError as e: if e.errno != errno.EEXIST: raise with tempfile.NamedTemporaryFile('wb', prefix=fingerprint, - dir=self._DIR, delete=False) as f: + dir=self._dir, delete=False) as f: try: self._write_schema_data(f) ipautil.flush_sync(f) @@ -492,7 +491,7 @@ class Schema: os.unlink(f.name) raise else: - os.rename(f.name, os.path.join(self._DIR, fingerprint)) + os.rename(f.name, os.path.join(self._dir, fingerprint)) def _write_schema_data(self, fileobj): with zipfile.ZipFile(fileobj, 'w', zipfile.ZIP_DEFLATED) as schema: diff --git a/ipalib/config.py b/ipalib/config.py index e21394f14..7df491ecd 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -45,6 +45,7 @@ from ipalib.constants import ( CONFIG_SECTION, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR, TLS_VERSIONS, TLS_VERSION_DEFAULT_MIN, TLS_VERSION_DEFAULT_MAX, + USER_CACHE_PATH ) from ipalib import errors @@ -507,6 +508,11 @@ class Env: if 'nss_dir' not in self: self.nss_dir = self._join('confdir', 'nssdb') + # user cache dir for IPA, defaults to '$XDG_CACHE_HOME/ipa' or + # '~/.cache/ipa'. XDG_CACHE_HOME env var is cached at import time. + if 'cache_dir' not in self: + self.cache_dir = os.path.join(USER_CACHE_PATH, 'ipa') + if 'tls_ca_cert' not in self: self.tls_ca_cert = self._join('confdir', 'ca.crt') diff --git a/ipalib/constants.py b/ipalib/constants.py index ee3e85aa2..cbfd603e4 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -273,6 +273,7 @@ DEFAULT_CONFIG = ( ('conf_default', object), # File containing context independent config ('plugins_on_demand', object), # Whether to finalize plugins on-demand (bool) ('nss_dir', object), # Path to nssdb, default {confdir}/nssdb + ('cache_dir', object), # ~/.cache/ipa directory, may use XDG_CACHE_HOME env ('tls_ca_cert', object), # Path to CA cert file # Set in Env._finalize_core(): diff --git a/pylint_plugins.py b/pylint_plugins.py index 0b393fbe3..c101a478f 100644 --- a/pylint_plugins.py +++ b/pylint_plugins.py @@ -395,6 +395,7 @@ AstroidBuilder(MANAGER).string_build(textwrap.dedent( api.env.ca_host = '' api.env.ca_install_port = None api.env.ca_port = 0 + api.env.cache_dir = '' # object api.env.certmonger_wait_timeout = 0 api.env.conf = '' # object api.env.conf_default = '' # object