URL: https://github.com/freeipa/freeipa/pull/182 Author: tiran Title: #182: Use env var IPA_CONFDIR to get confdir for 'cli' context Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/182/head:pr182 git checkout pr182
From 686ade0be3bffd8bda3795728163d5d27df0b9ad Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Mon, 28 Nov 2016 16:24:33 +0100 Subject: [PATCH 1/2] Set explicit confdir option for global contexts Some API contexts are used to modify global state (e.g. files in /etc and /var). These contexts do not support confdir overrides. Initialize the API with an explicit confdir argument to paths.ETC_IPA. The special contexts are: * backup * cli_installer * installer * ipctl * renew * restore * server * updates The patch also corrects the context of the ipa-httpd-kdcproxy script to 'server'. https://fedorahosted.org/freeipa/ticket/6389 Signed-off-by: Christian Heimes <chei...@redhat.com> --- client/ipa-client-automount | 1 + install/certmonger/dogtag-ipa-ca-renew-agent-submit | 2 +- install/migration/migration.py | 3 ++- install/oddjob/com.redhat.idm.trust-fetch-domains | 4 +++- install/restart_scripts/renew_ca_cert | 2 +- install/restart_scripts/restart_dirsrv | 3 ++- install/restart_scripts/stop_pkicad | 3 ++- install/share/copy-schema-to-ca.py | 3 ++- install/share/wsgi.py | 6 ++++-- install/tools/ipa-httpd-kdcproxy | 3 ++- install/tools/ipa-replica-conncheck | 4 +++- install/tools/ipactl | 5 ++++- ipaclient/install/client.py | 1 + ipaclient/ipa_certupdate.py | 2 +- ipaserver/install/ipa_backup.py | 2 +- ipaserver/install/ipa_ldap_updater.py | 2 +- ipaserver/install/ipa_restore.py | 1 + ipaserver/install/ipa_server_upgrade.py | 2 +- ipaserver/install/ipa_winsync_migrate.py | 3 ++- ipaserver/install/ldapupdate.py | 4 +++- ipaserver/install/server/install.py | 2 ++ ipaserver/install/server/replicainstall.py | 19 +++++++++++++------ 22 files changed, 53 insertions(+), 24 deletions(-) diff --git a/client/ipa-client-automount b/client/ipa-client-automount index 53c0537..93b1eaf 100755 --- a/client/ipa-client-automount +++ b/client/ipa-client-automount @@ -383,6 +383,7 @@ def main(): cfg = dict( context='cli_installer', + confdir=paths.ETC_IPA, in_server=False, debug=options.debug, verbose=0, diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit index 7389a5e..2e137ad 100755 --- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit +++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit @@ -494,7 +494,7 @@ def main(): 'ipaCACertRenewal': renew_ca_cert, } - api.bootstrap(in_server=True, context='renew') + api.bootstrap(in_server=True, context='renew', confdir=paths.ETC_IPA) api.finalize() api.Backend.ldap2.connect() diff --git a/install/migration/migration.py b/install/migration/migration.py index 4743279..73e4777 100644 --- a/install/migration/migration.py +++ b/install/migration/migration.py @@ -24,6 +24,7 @@ import errno from wsgiref.util import request_uri +from ipaplatform.paths import paths from ipapython.ipa_log_manager import root_logger from ipapython.dn import DN from ipapython import ipaldap @@ -72,7 +73,7 @@ def application(environ, start_response): # API object only for configuration, finalize() not needed api = create_api(mode=None) - api.bootstrap(context='server', in_server=True) + api.bootstrap(context='server', confdir=paths.ETC_IPA, in_server=True) try: bind(api.env.ldap_uri, api.env.basedn, form_data['username'].value, form_data['password'].value) diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains index b663daa..073e254 100755 --- a/install/oddjob/com.redhat.idm.trust-fetch-domains +++ b/install/oddjob/com.redhat.idm.trust-fetch-domains @@ -9,6 +9,7 @@ from ipalib.config import Env from ipalib.constants import DEFAULT_CONFIG from ipapython.ipautil import kinit_keytab from ipaplatform.constants import constants +from ipaplatform.paths import paths import sys import os import pwd @@ -94,7 +95,8 @@ env._bootstrap(debug=options.debug, log=None) env._finalize_core(**dict(DEFAULT_CONFIG)) # Initialize the API with the proper debug level -api.bootstrap(in_server=True, debug=env.debug, log=None, context='server') +api.bootstrap(in_server=True, debug=env.debug, log=None, + context='server', confdir=paths.ETC_IPA) api.finalize() # Only import trust plugin after api is initialized or internal imports diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert index 0f0a690..5d8af43 100644 --- a/install/restart_scripts/renew_ca_cert +++ b/install/restart_scripts/renew_ca_cert @@ -38,7 +38,7 @@ from ipaplatform.paths import paths def _main(): nickname = sys.argv[1] - api.bootstrap(in_server=True, context='restart') + api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) api.finalize() api.Backend.ldap2.connect() diff --git a/install/restart_scripts/restart_dirsrv b/install/restart_scripts/restart_dirsrv index 72d3c54..b4c9490 100644 --- a/install/restart_scripts/restart_dirsrv +++ b/install/restart_scripts/restart_dirsrv @@ -24,6 +24,7 @@ import syslog import traceback from ipalib import api from ipaplatform import services +from ipaplatform.paths import paths from ipaserver.install import certs @@ -33,7 +34,7 @@ def _main(): except IndexError: instance = "" - api.bootstrap(in_server=True, context='restart') + api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) api.finalize() syslog.syslog(syslog.LOG_NOTICE, "certmonger restarted dirsrv instance '%s'" % instance) diff --git a/install/restart_scripts/stop_pkicad b/install/restart_scripts/stop_pkicad index ae07dcd..133a4ef 100644 --- a/install/restart_scripts/stop_pkicad +++ b/install/restart_scripts/stop_pkicad @@ -23,11 +23,12 @@ import syslog import traceback from ipalib import api from ipaplatform import services +from ipaplatform.paths import paths from ipaserver.install import certs def main(): - api.bootstrap(in_server=True, context='restart') + api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) api.finalize() dogtag_service = services.knownservices['pki_tomcatd'] diff --git a/install/share/copy-schema-to-ca.py b/install/share/copy-schema-to-ca.py index 9edc0cd..15e4b8d 100755 --- a/install/share/copy-schema-to-ca.py +++ b/install/share/copy-schema-to-ca.py @@ -17,6 +17,7 @@ from hashlib import sha1 +from ipaplatform.paths import paths from ipapython import ipautil from ipapython.ipa_log_manager import root_logger, standard_logging_setup from ipaserver.install.dsinstance import schema_dirname @@ -113,7 +114,7 @@ def main(): standard_logging_setup(verbose=True) # In 3.0, restarting needs access to api.env - api.bootstrap_with_global_options(context='server') + api.bootstrap_with_global_options(context='server', confdir=paths.ETC_IPA) add_ca_schema() restart_pki_ds() diff --git a/install/share/wsgi.py b/install/share/wsgi.py index ee9311e..ca97d1e 100644 --- a/install/share/wsgi.py +++ b/install/share/wsgi.py @@ -23,6 +23,7 @@ """ WSGI appliction for IPA server. """ +from ipaplatform.paths import paths from ipalib import api from ipalib.config import Env from ipalib.constants import DEFAULT_CONFIG @@ -31,11 +32,12 @@ # by reading in the configuration file(s). The server always reads # default.conf and will also read in `context'.conf. env = Env() -env._bootstrap(context='server', log=None) +env._bootstrap(context='server', log=None, confdir=paths.ETC_IPA) env._finalize_core(**dict(DEFAULT_CONFIG)) # Initialize the API with the proper debug level -api.bootstrap(context='server', debug=env.debug, log=None) +api.bootstrap(context='server', confdir=paths.ETC_IPA, + debug=env.debug, log=None) try: api.finalize() except Exception as e: diff --git a/install/tools/ipa-httpd-kdcproxy b/install/tools/ipa-httpd-kdcproxy index 329565c..bb2949b 100755 --- a/install/tools/ipa-httpd-kdcproxy +++ b/install/tools/ipa-httpd-kdcproxy @@ -184,7 +184,8 @@ class KDCProxyConfig(object): def main(debug=DEBUG, time_limit=TIME_LIMIT): # initialize API without file logging if not api.isdone('bootstrap'): - api.bootstrap(context='ipa-httpd-kdcproxy', log=None, debug=debug) + api.bootstrap(context='server', confdir=paths.ETC_IPA, + log=None, debug=debug) standard_logging_setup(verbose=True, debug=debug) try: diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck index 7ec1ef8..7981093 100755 --- a/install/tools/ipa-replica-conncheck +++ b/install/tools/ipa-replica-conncheck @@ -480,7 +480,9 @@ def main(): else: nss_dir = None - api.bootstrap(context='client', xmlrpc_uri=xmlrpc_uri, + api.bootstrap(context='client', + confdir=paths.ETC_IPA, + xmlrpc_uri=xmlrpc_uri, nss_dir=nss_db.secdir) api.finalize() try: diff --git a/install/tools/ipactl b/install/tools/ipactl index 13a1a27..f24d64d 100755 --- a/install/tools/ipactl +++ b/install/tools/ipactl @@ -561,7 +561,10 @@ def main(): else: raise e - api.bootstrap(in_server=True, context='ipactl', debug=options.debug) + api.bootstrap(in_server=True, + context='ipactl', + confdir=paths.ETC_IPA, + debug=options.debug) api.finalize() if '.' not in api.env.host: diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py index d18d8bb..c5ff75b 100644 --- a/ipaclient/install/client.py +++ b/ipaclient/install/client.py @@ -2586,6 +2586,7 @@ def _install(options): with certdb.NSSDatabase() as tmp_db: api.bootstrap(context='cli_installer', + confdir=paths.ETC_IPA, debug=options.debug, delegate=False, nss_dir=tmp_db.secdir) diff --git a/ipaclient/ipa_certupdate.py b/ipaclient/ipa_certupdate.py index 6e41de9..159371c 100644 --- a/ipaclient/ipa_certupdate.py +++ b/ipaclient/ipa_certupdate.py @@ -52,7 +52,7 @@ def run(self): raise admintool.ScriptError( "IPA client is not configured on this system.") - api.bootstrap(context='cli_installer') + api.bootstrap(context='cli_installer', confdir=paths.ETC_IPA) api.finalize() server = urlsplit(api.env.jsonrpc_uri).hostname diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py index 6fc1870..36d5086 100644 --- a/ipaserver/install/ipa_backup.py +++ b/ipaserver/install/ipa_backup.py @@ -257,7 +257,7 @@ def run(self): options = self.options super(Backup, self).run() - api.bootstrap(in_server=True, context='backup') + api.bootstrap(in_server=True, context='backup', confdir=paths.ETC_IPA) api.finalize() self.log.info("Preparing backup on %s", api.env.host) diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py index e6e6b5a..f3e05b8 100644 --- a/ipaserver/install/ipa_ldap_updater.py +++ b/ipaserver/install/ipa_ldap_updater.py @@ -89,7 +89,7 @@ def setup_logging(self): def run(self): super(LDAPUpdater, self).run() - api.bootstrap(in_server=True, context='updates') + api.bootstrap(in_server=True, context='updates', confdir=paths.ETC_IPA) api.finalize() def handle_error(self, exception): diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py index 3dc6522..c87e427 100644 --- a/ipaserver/install/ipa_restore.py +++ b/ipaserver/install/ipa_restore.py @@ -840,6 +840,7 @@ def cert_restore(self): services.knownservices.certmonger.restart() def init_api(self, **overrides): + overrides.setdefault('confdir', paths.ETC_IPA) api.bootstrap(in_server=True, context='restore', **overrides) api.finalize() diff --git a/ipaserver/install/ipa_server_upgrade.py b/ipaserver/install/ipa_server_upgrade.py index c384704..ef31068 100644 --- a/ipaserver/install/ipa_server_upgrade.py +++ b/ipaserver/install/ipa_server_upgrade.py @@ -38,7 +38,7 @@ def setup_logging(self): def run(self): super(ServerUpgrade, self).run() - api.bootstrap(in_server=True, context='updates') + api.bootstrap(in_server=True, context='updates', confdir=paths.ETC_IPA) api.finalize() try: diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py index d0653c9..cb77de6 100644 --- a/ipaserver/install/ipa_winsync_migrate.py +++ b/ipaserver/install/ipa_winsync_migrate.py @@ -24,6 +24,7 @@ from ipalib import api from ipalib import errors +from ipaplatform.paths import paths from ipapython import admintool from ipapython.dn import DN from ipapython.ipautil import realm_to_suffix, posixify @@ -346,7 +347,7 @@ def main(cls, argv): sys.exit(e) # Finalize API - api.bootstrap(in_server=True, context='server') + api.bootstrap(in_server=True, context='server', confdir=paths.ETC_IPA) api.finalize() # Setup LDAP connection diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py index 1e49e7a..b11cbc1 100644 --- a/ipaserver/install/ldapupdate.py +++ b/ipaserver/install/ldapupdate.py @@ -311,7 +311,9 @@ def __init__(self, dm_password=None, sub_dict={}, self.sub_dict["TOTAL_EXCLUDES"] = "(objectclass=*) $ EXCLUDE " + \ " ".join(constants.REPL_AGMT_TOTAL_EXCLUDES) self.api = create_api(mode=None) - self.api.bootstrap(in_server=True, context='updates', + self.api.bootstrap(in_server=True, + context='updates', + confdir=paths.ETC_IPA, ldap_uri=self.ldapuri) self.api.finalize() if online: diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index b8a46f5..431ca5c 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -562,6 +562,7 @@ def install_check(installer): # we are sure we have the configuration file ready. cfg = dict( context='installer', + confdir=paths.ETC_IPA, in_server=True, # make sure host name specified by user is used instead of default host=host_name, @@ -958,6 +959,7 @@ def uninstall_check(installer): # we are sure we have the configuration file ready. cfg = dict( context='installer', + confdir=paths.ETC_IPA, in_server=True, ) diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index f3aa482..b7dba9d 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -690,7 +690,7 @@ def install_check(installer): # Note: We must do this before bootstraping and finalizing ipalib.api create_ipa_conf(fstore, config, ca_enabled) - api.bootstrap(in_server=True, context='installer') + api.bootstrap(in_server=True, context='installer', confdir=paths.ETC_IPA) api.finalize() installutils.verify_fqdn(config.master_host_name, options.no_host_dns) @@ -729,7 +729,9 @@ def install_check(installer): ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name) remote_api = create_api(mode=None) - remote_api.bootstrap(in_server=True, context='installer', + remote_api.bootstrap(in_server=True, + context='installer', + confdir=paths.ETC_IPA, ldap_uri=ldapuri) remote_api.finalize() conn = remote_api.Backend.ldap2 @@ -952,12 +954,14 @@ def promote_check(installer): fstore = sysrestore.FileStore(paths.SYSRESTORE) env = Env() - env._bootstrap(context='installer', log=None) + env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None) env._finalize_core(**dict(constants.DEFAULT_CONFIG)) # pylint: disable=no-member xmlrpc_uri = 'https://{}/ipa/xml'.format(ipautil.format_netloc(env.host)) - api.bootstrap(in_server=True, context='installer', + api.bootstrap(in_server=True, + context='installer', + confdir=paths.ETC_IPA, ldap_uri=installutils.realm_to_ldapi_uri(env.realm), xmlrpc_uri=xmlrpc_uri) # pylint: enable=no-member @@ -1054,8 +1058,11 @@ def promote_check(installer): xmlrpc_uri = 'https://{}/ipa/xml'.format( ipautil.format_netloc(config.master_host_name)) remote_api = create_api(mode=None) - remote_api.bootstrap(in_server=True, context='installer', - ldap_uri=ldapuri, xmlrpc_uri=xmlrpc_uri) + remote_api.bootstrap(in_server=True, + context='installer', + confdir=paths.ETC_IPA, + ldap_uri=ldapuri, + xmlrpc_uri=xmlrpc_uri) remote_api.finalize() check_remote_version(remote_api) From 43040dbd56256f09a438a69c0d1b8d59fcbdaf15 Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Mon, 24 Oct 2016 10:35:41 +0200 Subject: [PATCH 2/2] Use env var IPA_CONFDIR to get confdir The environment variable IPA_CONFDIR overrides the default confdir path. The value of the environment variable must be an absolute path to an existing directory. The new variable makes it much simpler to use the 'ipa' command and ipalib with a local configuration directory. Some contexts like server, installer, and upgrades set the confdir explicitly and do not support the env var. Signed-off-by: Christian Heimes <chei...@redhat.com> --- client/man/ipa.1 | 4 ++++ ipalib/config.py | 12 ++++++++++- ipalib/plugable.py | 4 ++++ ipatests/test_ipalib/test_plugable.py | 40 +++++++++++++++++++++++++++++++++-- ipatests/util.py | 6 ++++++ 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/client/man/ipa.1 b/client/man/ipa.1 index cc5641b..f35f557 100644 --- a/client/man/ipa.1 +++ b/client/man/ipa.1 @@ -190,6 +190,10 @@ The ipa client will determine which server to connect to in this order: .TP If a kerberos error is raised by any of the requests then it will stop processing and display the error message. +.SH "ENVIRONMENT VARIABLES" +.TP +\fBIPA_CONFDIR\fR +Override path to confdir (default: \fB/etc/ipa\fR). .SH "FILES" .TP \fB/etc/ipa/default.conf\fR diff --git a/ipalib/config.py b/ipalib/config.py index 1075d62..c46d033 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -43,6 +43,7 @@ from ipalib.base import check_name from ipalib.constants import CONFIG_SECTION from ipalib.constants import OVERRIDE_ERROR, SET_ERROR, DEL_ERROR +from ipapython.admintool import ScriptError if six.PY3: unicode = str @@ -460,8 +461,17 @@ def _bootstrap(self, **overrides): self.context = 'default' # Set confdir: + self.confdir_env = False if 'confdir' not in self: - if self.in_tree: + ipa_confdir = os.environ.get('IPA_CONFDIR') + if ipa_confdir is not None: + if not path.isabs(ipa_confdir) or not path.isdir(ipa_confdir): + raise ScriptError( + 'IPA_CONFDIR env var must be an absolute path to an ' + 'existing directory.') + self.confdir = ipa_confdir + self.confdir_env = True + elif self.in_tree: self.confdir = self.dot_ipa else: self.confdir = path.join('/', 'etc', 'ipa') diff --git a/ipalib/plugable.py b/ipalib/plugable.py index 503534f..e9d4c80 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -713,6 +713,10 @@ def finalize(self): self.__doing('finalize') self.__do_if_not_done('load_plugins') + if self.env.confdir_env: + self.log.info("IPA_CONFDIR env var sets confdir to '%s'.", + self.env.confdir) + for plugin in self.__plugins: if not self.env.validate_api: if plugin.full_name not in DEFAULT_PLUGINS: diff --git a/ipatests/test_ipalib/test_plugable.py b/ipatests/test_ipalib/test_plugable.py index 1ee1102..ff22446 100644 --- a/ipatests/test_ipalib/test_plugable.py +++ b/ipatests/test_ipalib/test_plugable.py @@ -24,9 +24,13 @@ # FIXME: Pylint errors # pylint: disable=no-member +import os +import textwrap + +from ipalib import plugable, errors, create_api +from ipapython.admintool import ScriptError from ipatests.util import raises, read_only -from ipatests.util import ClassChecker, create_test_api -from ipalib import plugable, errors +from ipatests.util import ClassChecker, create_test_api, TempHome import pytest @@ -272,3 +276,35 @@ def test_load_plugins(self): assert o.isdone('load_plugins') is True e = raises(Exception, o.load_plugins) assert str(e) == 'API.load_plugins() already called' + + def test_ipaconf_env(self): + ipa_confdir = os.environ.get('IPA_CONFDIR', None) + try: + with TempHome() as home: + defaultconf = home.join('default.conf') + with open(defaultconf, 'w') as f: + f.write(textwrap.dedent(""" + [global] + basedn = dc=ipa,dc=test + realm = IPA.TEST + domain = ipa.test + """) + ) + os.environ['IPA_CONFDIR'] = home.path + api = create_api(mode='unit_test') + api.bootstrap() + api.finalize() + assert api.env.confdir == home.path + assert api.env.conf_default == defaultconf + assert api.env.realm == 'IPA.TEST' + assert api.env.domain == 'ipa.test' + + os.environ['IPA_CONFDIR'] = home.join('invalid') + api = create_api(mode='unit_test') + with pytest.raises(ScriptError): + api.bootstrap() + finally: + if ipa_confdir: + os.environ['IPA_CONFDIR'] = ipa_confdir + else: + os.environ.pop('IPA_CONFDIR') diff --git a/ipatests/util.py b/ipatests/util.py index bca8e9c..38edaf9 100644 --- a/ipatests/util.py +++ b/ipatests/util.py @@ -97,6 +97,12 @@ def join(self, *parts): def __del__(self): self.rmtree() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.rmtree() + class TempHome(TempDir): def __init__(self):
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code