From be9ba85ab61723c7fbd148e870e515bca91e0f38 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Fri, 21 Aug 2020 15:33:38 +0300 Subject: [PATCH] Support older installations on upgrade --- ...pa-4.8.9-support-older-installations.patch | 282 ++++++++++++++++++ freeipa.spec | 6 +- 2 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 freeipa-4.8.9-support-older-installations.patch diff --git a/freeipa-4.8.9-support-older-installations.patch b/freeipa-4.8.9-support-older-installations.patch new file mode 100644 index 0000000..04561fa --- /dev/null +++ b/freeipa-4.8.9-support-older-installations.patch @@ -0,0 +1,282 @@ +From 071e283b19e925bea596a25b4758ab2cbc657914 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 11 Aug 2020 10:47:05 -0400 +Subject: [PATCH 1/3] Fall back to old server installation detection when + needed + +If there is no installation section the the install pre-dated +this new method of detecting a successful installation, fall back +to that. + +https://pagure.io/freeipa/issue/8458 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Stanislav Levin +Reviewed-By: Alexander Bokovoy +--- + ipalib/facts.py | 31 ++++++++++++++++++++++++++++- + ipaserver/install/installutils.py | 4 ---- + ipaserver/install/server/install.py | 3 ++- + ipaserver/install/server/upgrade.py | 7 +++++-- + 4 files changed, 37 insertions(+), 8 deletions(-) + +diff --git a/ipalib/facts.py b/ipalib/facts.py +index 5106fc2ac5..d78c1a2275 100644 +--- a/ipalib/facts.py ++++ b/ipalib/facts.py +@@ -6,17 +6,46 @@ + Facts about the installation + """ + ++import logging + import os + from . import sysrestore + from ipaplatform.paths import paths + ++logger = logging.getLogger(__name__) ++ ++# Used to determine install status ++IPA_MODULES = [ ++ 'httpd', 'kadmin', 'dirsrv', 'pki-tomcatd', 'install', 'krb5kdc', 'named'] ++ + + def is_ipa_configured(): + """ + Use the state to determine if IPA has been configured. + """ + sstore = sysrestore.StateFile(paths.SYSRESTORE) +- return sstore.get_state('installation', 'complete') ++ if sstore.has_state('installation'): ++ return sstore.get_state('installation', 'complete') ++ ++ # Fall back to older method in case this is an existing installation ++ ++ installed = False ++ ++ fstore = sysrestore.FileStore(paths.SYSRESTORE) ++ ++ for module in IPA_MODULES: ++ if sstore.has_state(module): ++ logger.debug('%s is configured', module) ++ installed = True ++ else: ++ logger.debug('%s is not configured', module) ++ ++ if fstore.has_files(): ++ logger.debug('filestore has files') ++ installed = True ++ else: ++ logger.debug('filestore is tracking no files') ++ ++ return installed + + + def is_ipa_client_configured(on_master=False): +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 583b1aca0b..13baf494cd 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -63,10 +63,6 @@ + + logger = logging.getLogger(__name__) + +-# Used to determine install status +-IPA_MODULES = [ +- 'httpd', 'kadmin', 'dirsrv', 'pki-tomcatd', 'install', 'krb5kdc', 'named'] +- + + class BadHostError(Exception): + pass +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index b86c3fec15..4d8e3ad78f 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -37,13 +37,14 @@ + validate_domain_name, + no_matching_interface_for_ip_address_warning, + ) ++from ipalib.facts import IPA_MODULES + from ipaserver.install import ( + adtrust, adtrustinstance, bindinstance, ca, dns, dsinstance, + httpinstance, installutils, kra, krbinstance, + otpdinstance, custodiainstance, replication, service, + sysupgrade) + from ipaserver.install.installutils import ( +- IPA_MODULES, BadHostError, get_fqdn, get_server_ip_address, ++ BadHostError, get_fqdn, get_server_ip_address, + load_pkcs12, read_password, verify_fqdn, update_hosts_file, + validate_mask) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index f0d9b746cd..109d1e100e 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1455,8 +1455,11 @@ def upgrade_configuration(): + fstore = sysrestore.FileStore(paths.SYSRESTORE) + sstore = sysrestore.StateFile(paths.SYSRESTORE) + +- if is_ipa_configured() is None: +- sstore.backup_state('installation', 'complete', True) ++ if not sstore.has_state('installation'): ++ if is_ipa_configured(): ++ sstore.backup_state('installation', 'complete', True) ++ else: ++ sstore.backup_state('installation', 'complete', False) + + fqdn = api.env.host + + +From 7d84d919a8f5767ade1dcc380ce4eebadac6a8b5 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 11 Aug 2020 11:12:55 -0400 +Subject: [PATCH 2/3] Use is_ipa_configured from ipalib.facts + +A couple of places still used the deprecated installutils version. + +https://pagure.io/freeipa/issue/8458 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Stanislav Levin +Reviewed-By: Alexander Bokovoy +--- + ipaserver/install/installutils.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py +index 13baf494cd..a3274d5797 100644 +--- a/ipaserver/install/installutils.py ++++ b/ipaserver/install/installutils.py +@@ -665,7 +665,7 @@ def check_server_configuration(): + Most convenient use case for the function is in install tools that require + configured IPA for its function. + """ +- if not is_ipa_configured(): ++ if not facts.is_ipa_configured(): + raise ScriptError("IPA is not configured on this system.", + rval=SERVER_NOT_CONFIGURED) + + +From 36ecfdbfe4ceedcfe056816cbb22162842fae975 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 11 Aug 2020 13:55:54 -0400 +Subject: [PATCH 3/3] ipatests: Add test for is_ipa_configured + +Validate that is_ipa_configured() returns True when using either +the original and the new configuration methods. This will allow +older installs to successfully upgrade. + +https://pagure.io/freeipa/issue/8458 + +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Stanislav Levin +Reviewed-By: Alexander Bokovoy +--- + .../test_integration/test_installation.py | 88 +++++++++++++++++++ + 1 file changed, 88 insertions(+) + +diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py +index fb19900838..98bdc98ab8 100644 +--- a/ipatests/test_integration/test_installation.py ++++ b/ipatests/test_integration/test_installation.py +@@ -21,6 +21,7 @@ + + from ipalib import x509 + from ipalib.constants import DOMAIN_LEVEL_0 ++from ipalib.sysrestore import SYSRESTORE_STATEFILE, SYSRESTORE_INDEXFILE + from ipapython.dn import DN + from ipaplatform.constants import constants + from ipaplatform.osinfo import osinfo +@@ -357,6 +358,93 @@ def test_ipa_ca_crt_permissions(self): + assert owner == "root" + assert group == "root" + ++ def test_is_ipa_configured(self): ++ """Verify that the old and new methods of is_ipa_installed works ++ ++ If there is an installation section then it is the status. ++ ++ If not then it will fall back to looking for configured ++ services and files and use that for determination. ++ """ ++ def set_installation_state(host, state): ++ """ ++ Update the complete value in the installation section ++ """ ++ host.run_command( ++ ['python3', '-c', ++ 'from ipalib.install import sysrestore; ' ++ 'from ipaplatform.paths import paths;' ++ 'sstore = sysrestore.StateFile(paths.SYSRESTORE); ' ++ 'sstore.backup_state("installation", "complete", ' ++ '{state})'.format(state=state)]) ++ ++ def get_installation_state(host): ++ """ ++ Retrieve the installation state from new install method ++ """ ++ result = host.run_command( ++ ['python3', '-c', ++ 'from ipalib.install import sysrestore; ' ++ 'from ipaplatform.paths import paths;' ++ 'sstore = sysrestore.StateFile(paths.SYSRESTORE); ' ++ 'print(sstore.get_state("installation", "complete"))']) ++ return result.stdout_text.strip() # a string ++ ++ # This comes from freeipa.spec and is used to determine whether ++ # an upgrade is required. ++ cmd = ['python3', '-c', ++ 'import sys; from ipalib import facts; sys.exit(0 ' ++ 'if facts.is_ipa_configured() else 1);'] ++ ++ # This will use the new method since this is a fresh install, ++ # verify that it is true. ++ self.master.run_command(cmd) ++ assert get_installation_state(self.master) == 'True' ++ ++ # Set complete to False which should cause the command to fail ++ # This tests the state of a failed or in-process installation. ++ set_installation_state(self.master, False) ++ result = self.master.run_command(cmd, raiseonerr=False) ++ assert result.returncode == 1 ++ set_installation_state(self.master, True) ++ ++ # Tweak sysrestore.state to drop installation section ++ self.master.run_command( ++ ['sed','-i', r's/\[installation\]/\[badinstallation\]/', ++ os.path.join(paths.SYSRESTORE, SYSRESTORE_STATEFILE)]) ++ ++ # Re-run installation check and it should fall back to old method ++ # and be successful. ++ self.master.run_command(cmd) ++ assert get_installation_state(self.master) == 'None' ++ ++ # Restore installation section. ++ self.master.run_command( ++ ['sed','-i', r's/\[badinstallation\]/\[installation\]/', ++ os.path.join(paths.SYSRESTORE, SYSRESTORE_STATEFILE)]) ++ ++ # Uninstall and confirm that the old method reports correctly ++ # on uninstalled servers. It will exercise the old method since ++ # there is no state. ++ tasks.uninstall_master(self.master) ++ ++ # ensure there is no stale state ++ result = self.master.run_command(r'test -f {}'.format( ++ os.path.join(paths.SYSRESTORE, SYSRESTORE_STATEFILE)), ++ raiseonerr=False ++ ) ++ assert result.returncode == 1 ++ result = self.master.run_command(r'test -f {}'.format( ++ os.path.join(paths.SYSRESTORE, SYSRESTORE_INDEXFILE)), ++ raiseonerr=False ++ ) ++ assert result.returncode == 1 ++ ++ # Now run is_ipa_configured() and it should be False ++ result = self.master.run_command(cmd, raiseonerr=False) ++ assert result.returncode == 1 ++ ++ + class TestInstallWithCA_KRA1(InstallTestBase1): + + @classmethod diff --git a/freeipa.spec b/freeipa.spec index 7f21362..de3f736 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -156,13 +156,14 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 1%{?dist} +Release: 2%{?dist} Summary: The Identity, Policy and Audit system License: GPLv3+ URL: http://www.freeipa.org/ Source0: https://releases.pagure.org/freeipa/freeipa-%{version}.tar.gz Source1: https://releases.pagure.org/freeipa/freeipa-%{version}.tar.gz.asc +Patch0001: freeipa-4.8.9-support-older-installations.patch # For the timestamp trick in patch application BuildRequires: diffstat @@ -1513,6 +1514,9 @@ fi %endif %changelog +* Fri Aug 21 2020 Alexander Bokovoy - 4.8.9-2 +- Backport fix for detecting older installations on upgrade + * Thu Aug 20 2020 François Cami - 4.8.9-1 - Upstream release FreeIPA 4.8.9