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