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 <cheimes@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Christian Heimes 2023-09-01 07:18:15 +02:00 committed by Florence Blanc-Renaud
parent 5c8614157d
commit 5deeee31c0
6 changed files with 22 additions and 19 deletions

View File

@ -550,10 +550,8 @@ class vault_mod(Local):
class _KraConfigCache: class _KraConfigCache:
"""The KRA config cache stores vaultconfig-show result. """The KRA config cache stores vaultconfig-show result.
""" """
def __init__(self): def __init__(self, api):
self._dirname = os.path.join( self._dirname = os.path.join(api.env.cache_dir, 'kra-config')
constants.USER_CACHE_PATH, 'ipa', 'kra-config'
)
def _get_filename(self, domain): def _get_filename(self, domain):
basename = DNSName(domain).ToASCII() + '.json' basename = DNSName(domain).ToASCII() + '.json'
@ -629,7 +627,7 @@ class _KraConfigCache:
return True return True
_kra_config_cache = _KraConfigCache() _kra_config_cache = _KraConfigCache(api)
@register(override=True, no_fail=True) @register(override=True, no_fail=True)

View File

@ -13,18 +13,16 @@ import time
from . import compat from . import compat
from . import schema from . import schema
from ipaclient.plugins.rpcclient import rpcclient from ipaclient.plugins.rpcclient import rpcclient
from ipalib.constants import USER_CACHE_PATH
from ipapython.dnsutil import DNSName from ipapython.dnsutil import DNSName
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ServerInfo(MutableMapping): class ServerInfo(MutableMapping):
_DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'servers')
def __init__(self, api): def __init__(self, api):
self._dir = os.path.join(api.env.cache_dir, "servers")
hostname = DNSName(api.env.server).ToASCII() 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._force_check = api.env.force_schema_check
self._now = time.time() self._now = time.time()
self._dict = {} self._dict = {}
@ -41,7 +39,7 @@ class ServerInfo(MutableMapping):
def _read(self): def _read(self):
try: try:
with open(self._path, 'r') as sc: with open(self._filename, 'r') as sc:
self._dict = json.load(sc) self._dict = json.load(sc)
except Exception as e: except Exception as e:
if (isinstance(e, EnvironmentError) and if (isinstance(e, EnvironmentError) and
@ -56,11 +54,11 @@ class ServerInfo(MutableMapping):
def _write(self): def _write(self):
try: try:
try: try:
os.makedirs(self._DIR) os.makedirs(self._dir)
except EnvironmentError as e: except EnvironmentError as e:
if e.errno != errno.EEXIST: if e.errno != errno.EEXIST:
raise raise
with open(self._path, 'w') as sc: with open(self._filename, 'w') as sc:
json.dump(self._dict, sc) json.dump(self._dict, sc)
except EnvironmentError as e: except EnvironmentError as e:
logger.warning('Failed to write server info: %s', e) logger.warning('Failed to write server info: %s', e)

View File

@ -18,7 +18,6 @@ import six
from ipaclient.frontend import ClientCommand, ClientMethod from ipaclient.frontend import ClientCommand, ClientMethod
from ipalib import errors, parameters, plugable from ipalib import errors, parameters, plugable
from ipalib.constants import USER_CACHE_PATH
from ipalib.errors import SchemaUpToDate from ipalib.errors import SchemaUpToDate
from ipalib.frontend import Object from ipalib.frontend import Object
from ipalib.output import Output from ipalib.output import Output
@ -367,9 +366,9 @@ class Schema:
""" """
namespaces = {'classes', 'commands', 'topics'} namespaces = {'classes', 'commands', 'topics'}
_DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'schema', FORMAT)
def __init__(self, client, fingerprint=None, ttl=0): def __init__(self, client, fingerprint=None, ttl=0):
self._dir = os.path.join(client.api.env.cache_dir, 'schema', FORMAT)
self._dict = {} self._dict = {}
self._namespaces = {} self._namespaces = {}
self._help = None self._help = None
@ -409,7 +408,7 @@ class Schema:
fps = [] fps = []
if not ignore_cache: if not ignore_cache:
try: try:
fps = [fsdecode(f) for f in os.listdir(self._DIR)] fps = [fsdecode(f) for f in os.listdir(self._dir)]
except EnvironmentError: except EnvironmentError:
pass pass
@ -439,7 +438,7 @@ class Schema:
def _read_schema(self, fingerprint): def _read_schema(self, fingerprint):
# It's more efficient to read zip file members at once than to open # It's more efficient to read zip file members at once than to open
# the zip file a couple of times, see #6690. # 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: with zipfile.ZipFile(filename, 'r') as schema:
for name in schema.namelist(): for name in schema.namelist():
ns, _slash, key = name.partition('/') ns, _slash, key = name.partition('/')
@ -477,13 +476,13 @@ class Schema:
def _write_schema(self, fingerprint): def _write_schema(self, fingerprint):
try: try:
os.makedirs(self._DIR) os.makedirs(self._dir)
except EnvironmentError as e: except EnvironmentError as e:
if e.errno != errno.EEXIST: if e.errno != errno.EEXIST:
raise raise
with tempfile.NamedTemporaryFile('wb', prefix=fingerprint, with tempfile.NamedTemporaryFile('wb', prefix=fingerprint,
dir=self._DIR, delete=False) as f: dir=self._dir, delete=False) as f:
try: try:
self._write_schema_data(f) self._write_schema_data(f)
ipautil.flush_sync(f) ipautil.flush_sync(f)
@ -492,7 +491,7 @@ class Schema:
os.unlink(f.name) os.unlink(f.name)
raise raise
else: 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): def _write_schema_data(self, fileobj):
with zipfile.ZipFile(fileobj, 'w', zipfile.ZIP_DEFLATED) as schema: with zipfile.ZipFile(fileobj, 'w', zipfile.ZIP_DEFLATED) as schema:

View File

@ -45,6 +45,7 @@ from ipalib.constants import (
CONFIG_SECTION, CONFIG_SECTION,
OVERRIDE_ERROR, SET_ERROR, DEL_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR,
TLS_VERSIONS, TLS_VERSION_DEFAULT_MIN, TLS_VERSION_DEFAULT_MAX, TLS_VERSIONS, TLS_VERSION_DEFAULT_MIN, TLS_VERSION_DEFAULT_MAX,
USER_CACHE_PATH
) )
from ipalib import errors from ipalib import errors
@ -507,6 +508,11 @@ class Env:
if 'nss_dir' not in self: if 'nss_dir' not in self:
self.nss_dir = self._join('confdir', 'nssdb') 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: if 'tls_ca_cert' not in self:
self.tls_ca_cert = self._join('confdir', 'ca.crt') self.tls_ca_cert = self._join('confdir', 'ca.crt')

View File

@ -273,6 +273,7 @@ DEFAULT_CONFIG = (
('conf_default', object), # File containing context independent config ('conf_default', object), # File containing context independent config
('plugins_on_demand', object), # Whether to finalize plugins on-demand (bool) ('plugins_on_demand', object), # Whether to finalize plugins on-demand (bool)
('nss_dir', object), # Path to nssdb, default {confdir}/nssdb ('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 ('tls_ca_cert', object), # Path to CA cert file
# Set in Env._finalize_core(): # Set in Env._finalize_core():

View File

@ -395,6 +395,7 @@ AstroidBuilder(MANAGER).string_build(textwrap.dedent(
api.env.ca_host = '' api.env.ca_host = ''
api.env.ca_install_port = None api.env.ca_install_port = None
api.env.ca_port = 0 api.env.ca_port = 0
api.env.cache_dir = '' # object
api.env.certmonger_wait_timeout = 0 api.env.certmonger_wait_timeout = 0
api.env.conf = '' # object api.env.conf = '' # object
api.env.conf_default = '' # object api.env.conf_default = '' # object