URL: https://github.com/freeipa/freeipa/pull/6049 Author: rcritten Title: #6049: Make the schema cache TTL user-configurable Action: opened
PR body: """ The API schema is not checked for changes until after a TTL is expired. A one-hour TTL was hardcoded which makes development tedious because the only way to force a schema update is to remember to remove files between invocations. This adds a new environment variable, schema_ttl, to configure the TTL returned by the server to schema() calls. This can be set low to ensure a frequent refresh during development. If the client is in compat mode, that is if client is working against a server that doesn't support the schema() command, then use the client's schema_ttl instead so that the user still has control. https://pagure.io/freeipa/issue/8492 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/6049/head:pr6049 git checkout pr6049
From fa4e6c8bb17d935dcddc02d4d4170a017c15962a Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Thu, 14 Oct 2021 17:07:32 -0400 Subject: [PATCH] Make the schema cache TTL user-configurable The API schema is not checked for changes until after a TTL is expired. A one-hour TTL was hardcoded which makes development tedious because the only way to force a schema update is to remember to remove files between invocations. This adds a new environment variable, schema_ttl, to configure the TTL returned by the server to schema() calls. This can be set low to ensure a frequent refresh during development. If the client is in compat mode, that is if client is working against a server that doesn't support the schema() command, then use the client's schema_ttl instead so that the user still has control. https://pagure.io/freeipa/issue/8492 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> --- client/man/default.conf.5 | 3 ++ ipaclient/remote_plugins/__init__.py | 4 +- ipaclient/remote_plugins/compat.py | 5 ++- ipaclient/remote_plugins/schema.py | 2 +- ipalib/constants.py | 3 ++ ipaserver/plugins/schema.py | 8 +--- ipatests/test_cmdline/test_schema.py | 66 ++++++++++++++++++++++++++++ 7 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 ipatests/test_cmdline/test_schema.py diff --git a/client/man/default.conf.5 b/client/man/default.conf.5 index 1fdceb122ed..9b1f76438e4 100644 --- a/client/man/default.conf.5 +++ b/client/man/default.conf.5 @@ -172,6 +172,9 @@ Specifies the Kerberos realm. .B replication_wait_timeout <seconds> The time to wait for a new entry to be replicated during replica installation. The default value is 300 seconds. .TP +.B schema_ttl <seconds> +The number of seconds for the ipa tool to cache the IPA API and help schema. Reducing this value during development is helpful so that API changes are seen sooner in the tool. Setting this on a server will define the TTL for all client versions > 4.3.1. Client veresions > 4.3.1 that connect to IPA servers older than 4.3.1 will use the client-side configuration value. The default is 3600 seconds. +.TP .B server <hostname> Specifies the IPA Server hostname. .TP diff --git a/ipaclient/remote_plugins/__init__.py b/ipaclient/remote_plugins/__init__.py index fe75b81b882..acbc2ddf214 100644 --- a/ipaclient/remote_plugins/__init__.py +++ b/ipaclient/remote_plugins/__init__.py @@ -87,9 +87,7 @@ def __iter__(self): def __len__(self): return len(self._dict) - def update_validity(self, ttl=None): - if ttl is None: - ttl = 3600 + def update_validity(self, ttl): self['expiration'] = time.time() + ttl self['language'] = self._language self._write() diff --git a/ipaclient/remote_plugins/compat.py b/ipaclient/remote_plugins/compat.py index 2a600fccc89..351669a961c 100644 --- a/ipaclient/remote_plugins/compat.py +++ b/ipaclient/remote_plugins/compat.py @@ -58,7 +58,10 @@ def get_package(server_info, client): else: server_version = '2.0' server_info['version'] = server_version - server_info.update_validity() + + # in compat mode we don't get the schema TTL from the server + # so use the client context value. + server_info.update_validity(client.api.env.schema_ttl) server_version = APIVersion(server_version) diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py index 7bf714371f0..0ac3c94f187 100644 --- a/ipaclient/remote_plugins/schema.py +++ b/ipaclient/remote_plugins/schema.py @@ -557,7 +557,7 @@ def get_package(server_info, client): schema = Schema(client, e.fingerprint) except NotAvailable: fingerprint = None - ttl = None + ttl = 3600 # set a ttl so we don't hammer the remote server except SchemaUpToDate as e: fingerprint = e.fingerprint ttl = e.ttl diff --git a/ipalib/constants.py b/ipalib/constants.py index 2aeafac7ab8..ad610e05741 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -182,6 +182,9 @@ # How long to wait for a certmonger request to finish ('certmonger_wait_timeout', 300), + # Number of seconds before client should check for schema update. + ('schema_ttl', 3600), + # Web Application mount points ('mount_ipa', '/ipa/'), diff --git a/ipaserver/plugins/schema.py b/ipaserver/plugins/schema.py index d56cf28e177..4dac82c4f12 100644 --- a/ipaserver/plugins/schema.py +++ b/ipaserver/plugins/schema.py @@ -20,12 +20,6 @@ from ipalib.text import _ from ipapython.version import API_VERSION -# Schema TTL sent to clients in response to schema call. -# Number of seconds before client should check for schema update. -# This should be long enough to not slow down regular work or skripts -# but also short enough to ensure schema will be retvieved soon after -# it was updated -SCHEMA_TTL = 3600 # default: 1 hour __doc__ = _(""" API Schema @@ -855,7 +849,7 @@ def execute(self, *args, **kwargs): schema = self._generate_schema(**kwargs) self.api._schema[langs] = schema - schema['ttl'] = SCHEMA_TTL + schema['ttl'] = self.api.env.schema_ttl if schema['fingerprint'] in kwargs.get('known_fingerprints', []): raise errors.SchemaUpToDate( diff --git a/ipatests/test_cmdline/test_schema.py b/ipatests/test_cmdline/test_schema.py new file mode 100644 index 00000000000..99533e10a1a --- /dev/null +++ b/ipatests/test_cmdline/test_schema.py @@ -0,0 +1,66 @@ +# +# Copyright (C) 2021 FreeIPA Contributors see COPYING for license +# +import pytest +import time + +from ipaclient.remote_plugins import ServerInfo + + +class TestServerInfo(ServerInfo): + """Simplified ServerInfo class with hardcoded values""" + def __init__(self, fingerprint='deadbeef', hostname='ipa.example.test', + force_check=False, language='en_us', + version='2.0', expiration=None): + self._force_check = force_check + self._language = language + self._dict = { + 'fingerprint': fingerprint, + 'expiration': expiration or time.time() + 3600, + 'language': language, + 'version': version, + } + + def _read(self): + """Running on test controller, this is a no-op""" + + def _write(self): + """Running on test controller, this is a no-op""" + + +@pytest.mark.tier0 +class TestIPAServerInfo: + """Test that ServerInfo detects changes in remote configuration""" + + def test_valid(self): + server_info = TestServerInfo() + assert server_info.is_valid() is True + + def test_force_check(self): + server_info = TestServerInfo(force_check=True) + assert server_info.is_valid() is False + + def test_language_change(self): + server_info = TestServerInfo() + assert server_info.is_valid() is True + server_info._language = 'fr' + assert server_info.is_valid() is False + + def test_expired(self): + server_info = TestServerInfo(expiration=time.time() - 3600) + assert server_info.is_valid() is False + + def test_wait_for_expired(self): + server_info = TestServerInfo() + assert server_info.is_valid() is True + # force the expiration date to now + 1, wait, should be expired + server_info.update_validity(1) + time.sleep(2) + assert server_info.is_valid() is False + + def test_update_validity(self): + server_info = TestServerInfo() + expiration = server_info['expiration'] + server_info.update_validity(3600) + assert server_info['expiration'] > expiration + assert server_info.is_valid() is True
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org Do not reply to spam on the list, report it: https://pagure.io/fedora-infrastructure