URL: https://github.com/freeipa/freeipa/pull/397
Author: tiran
 Title: #397: Improve wheel building and provide ipaserver wheel for local 
testing
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/397/head:pr397
git checkout pr397
From 6419040e0bcf726232f30c4020fbea9bb9e10376 Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Tue, 17 Jan 2017 08:49:54 +0100
Subject: [PATCH 1/3] Conditionally import pyhbac

The pyhbac module is part of SSSD. It's not available as stand-alone
PyPI package. It would take a lot of effort to package it because the
code is deeply tight into SSSD.

Let's follow the example of other SSSD Python packages and make the
import of pyhbac conditionally. It's only necessary for caacl and
hbactest plugins.

I renamed convert_to_ipa_rule() to _convert_to_ipa_rule() because it
does not check for presence of pyhbac package itself. The check is
performed earlier in execute(). The prefix indicates that it is an
internal function and developers have to think twice before using it
in another place.

This makes it much easier to install ipaserver with instrumented build
of Python with a different ABI or in isolated virtual envs to profile
and debug the server.

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 ipaserver/plugins/caacl.py    | 86 -----------------------------------------
 ipaserver/plugins/cert.py     | 90 ++++++++++++++++++++++++++++++++++++++++++-
 ipaserver/plugins/hbactest.py | 19 +++++++--
 3 files changed, 105 insertions(+), 90 deletions(-)

diff --git a/ipaserver/plugins/caacl.py b/ipaserver/plugins/caacl.py
index ff1178a..43a397d 100644
--- a/ipaserver/plugins/caacl.py
+++ b/ipaserver/plugins/caacl.py
@@ -2,12 +2,10 @@
 # Copyright (C) 2015  FreeIPA Contributors see COPYING for license
 #
 
-import pyhbac
 import six
 
 from ipalib import api, errors, output
 from ipalib import Bool, Str, StrEnum
-from ipalib.constants import IPA_CA_CN
 from ipalib.plugable import Registry
 from .baseldap import (
     LDAPObject, LDAPSearch, LDAPCreate, LDAPDelete, LDAPQuery,
@@ -80,90 +78,6 @@
 register = Registry()
 
 
-def _acl_make_request(principal_type, principal, ca_id, profile_id):
-    """Construct HBAC request for the given principal, CA and profile"""
-
-    req = pyhbac.HbacRequest()
-    req.targethost.name = ca_id
-    req.service.name = profile_id
-    if principal_type == 'user':
-        req.user.name = principal.username
-    elif principal_type == 'host':
-        req.user.name = principal.hostname
-    elif principal_type == 'service':
-        req.user.name = unicode(principal)
-    groups = []
-    if principal_type == 'user':
-        user_obj = api.Command.user_show(principal.username)['result']
-        groups = user_obj.get('memberof_group', [])
-        groups += user_obj.get('memberofindirect_group', [])
-    elif principal_type == 'host':
-        host_obj = api.Command.host_show(principal.hostname)['result']
-        groups = host_obj.get('memberof_hostgroup', [])
-        groups += host_obj.get('memberofindirect_hostgroup', [])
-    req.user.groups = sorted(set(groups))
-    return req
-
-
-def _acl_make_rule(principal_type, obj):
-    """Turn CA ACL object into HBAC rule.
-
-    ``principal_type``
-        String in {'user', 'host', 'service'}
-    """
-    rule = pyhbac.HbacRule(obj['cn'][0])
-    rule.enabled = obj['ipaenabledflag'][0]
-    rule.srchosts.category = {pyhbac.HBAC_CATEGORY_ALL}
-
-    # add CA(s)
-    if 'ipacacategory' in obj and obj['ipacacategory'][0].lower() == 'all':
-        rule.targethosts.category = {pyhbac.HBAC_CATEGORY_ALL}
-    else:
-        # For compatibility with pre-lightweight-CAs CA ACLs,
-        # no CA members implies the host authority (only)
-        rule.targethosts.names = obj.get('ipamemberca_ca', [IPA_CA_CN])
-
-    # add profiles
-    if ('ipacertprofilecategory' in obj
-            and obj['ipacertprofilecategory'][0].lower() == 'all'):
-        rule.services.category = {pyhbac.HBAC_CATEGORY_ALL}
-    else:
-        attr = 'ipamembercertprofile_certprofile'
-        rule.services.names = obj.get(attr, [])
-
-    # add principals and principal's groups
-    category_attr = '{}category'.format(principal_type)
-    if category_attr in obj and obj[category_attr][0].lower() == 'all':
-        rule.users.category = {pyhbac.HBAC_CATEGORY_ALL}
-    else:
-        if principal_type == 'user':
-            rule.users.names = obj.get('memberuser_user', [])
-            rule.users.groups = obj.get('memberuser_group', [])
-        elif principal_type == 'host':
-            rule.users.names = obj.get('memberhost_host', [])
-            rule.users.groups = obj.get('memberhost_hostgroup', [])
-        elif principal_type == 'service':
-            rule.users.names = [
-                unicode(principal)
-                for principal in obj.get('memberservice_service', [])
-            ]
-
-    return rule
-
-
-def acl_evaluate(principal, ca_id, profile_id):
-    if principal.is_user:
-        principal_type = 'user'
-    elif principal.is_host:
-        principal_type = 'host'
-    else:
-        principal_type = 'service'
-    req = _acl_make_request(principal_type, principal, ca_id, profile_id)
-    acls = api.Command.caacl_find(no_members=False)['result']
-    rules = [_acl_make_rule(principal_type, obj) for obj in acls]
-    return req.evaluate(rules) == pyhbac.HBAC_EVAL_ALLOW
-
-
 @register()
 class caacl(LDAPObject):
     """
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 1a6d045..039a613 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -43,7 +43,6 @@
 from .virtual import VirtualCommand
 from .baseldap import pkey_to_value
 from .certprofile import validate_profile_id
-from .caacl import acl_evaluate
 from ipalib.text import _
 from ipalib.request import context
 from ipalib import output
@@ -52,6 +51,11 @@
 from ipapython.ipa_log_manager import root_logger
 from ipaserver.plugins.service import normalize_principal, validate_realm
 
+try:
+    import pyhbac
+except ImportError:
+    raise errors.SkipPluginModule(reason=_('pyhbac is not installed.'))
+
 if six.PY3:
     unicode = str
 
@@ -158,6 +162,90 @@
 PKIDATE_FORMAT = '%Y-%m-%d'
 
 
+def _acl_make_request(principal_type, principal, ca_id, profile_id):
+    """Construct HBAC request for the given principal, CA and profile"""
+
+    req = pyhbac.HbacRequest()
+    req.targethost.name = ca_id
+    req.service.name = profile_id
+    if principal_type == 'user':
+        req.user.name = principal.username
+    elif principal_type == 'host':
+        req.user.name = principal.hostname
+    elif principal_type == 'service':
+        req.user.name = unicode(principal)
+    groups = []
+    if principal_type == 'user':
+        user_obj = api.Command.user_show(principal.username)['result']
+        groups = user_obj.get('memberof_group', [])
+        groups += user_obj.get('memberofindirect_group', [])
+    elif principal_type == 'host':
+        host_obj = api.Command.host_show(principal.hostname)['result']
+        groups = host_obj.get('memberof_hostgroup', [])
+        groups += host_obj.get('memberofindirect_hostgroup', [])
+    req.user.groups = sorted(set(groups))
+    return req
+
+
+def _acl_make_rule(principal_type, obj):
+    """Turn CA ACL object into HBAC rule.
+
+    ``principal_type``
+        String in {'user', 'host', 'service'}
+    """
+    rule = pyhbac.HbacRule(obj['cn'][0])
+    rule.enabled = obj['ipaenabledflag'][0]
+    rule.srchosts.category = {pyhbac.HBAC_CATEGORY_ALL}
+
+    # add CA(s)
+    if 'ipacacategory' in obj and obj['ipacacategory'][0].lower() == 'all':
+        rule.targethosts.category = {pyhbac.HBAC_CATEGORY_ALL}
+    else:
+        # For compatibility with pre-lightweight-CAs CA ACLs,
+        # no CA members implies the host authority (only)
+        rule.targethosts.names = obj.get('ipamemberca_ca', [IPA_CA_CN])
+
+    # add profiles
+    if ('ipacertprofilecategory' in obj
+            and obj['ipacertprofilecategory'][0].lower() == 'all'):
+        rule.services.category = {pyhbac.HBAC_CATEGORY_ALL}
+    else:
+        attr = 'ipamembercertprofile_certprofile'
+        rule.services.names = obj.get(attr, [])
+
+    # add principals and principal's groups
+    category_attr = '{}category'.format(principal_type)
+    if category_attr in obj and obj[category_attr][0].lower() == 'all':
+        rule.users.category = {pyhbac.HBAC_CATEGORY_ALL}
+    else:
+        if principal_type == 'user':
+            rule.users.names = obj.get('memberuser_user', [])
+            rule.users.groups = obj.get('memberuser_group', [])
+        elif principal_type == 'host':
+            rule.users.names = obj.get('memberhost_host', [])
+            rule.users.groups = obj.get('memberhost_hostgroup', [])
+        elif principal_type == 'service':
+            rule.users.names = [
+                unicode(principal)
+                for principal in obj.get('memberservice_service', [])
+            ]
+
+    return rule
+
+
+def acl_evaluate(principal, ca_id, profile_id):
+    if principal.is_user:
+        principal_type = 'user'
+    elif principal.is_host:
+        principal_type = 'host'
+    else:
+        principal_type = 'service'
+    req = _acl_make_request(principal_type, principal, ca_id, profile_id)
+    acls = api.Command.caacl_find(no_members=False)['result']
+    rules = [_acl_make_rule(principal_type, obj) for obj in acls]
+    return req.evaluate(rules) == pyhbac.HBAC_EVAL_ALLOW
+
+
 def normalize_pkidate(value):
     return datetime.datetime.strptime(value, PKIDATE_FORMAT)
 
diff --git a/ipaserver/plugins/hbactest.py b/ipaserver/plugins/hbactest.py
index 626e894..e156568 100644
--- a/ipaserver/plugins/hbactest.py
+++ b/ipaserver/plugins/hbactest.py
@@ -29,9 +29,14 @@
     except ImportError:
         _dcerpc_bindings_installed = False
 
-import pyhbac
 import six
 
+try:
+    import pyhbac
+except ImportError:
+    pyhbac = None
+
+
 if six.PY3:
     unicode = str
 
@@ -210,7 +215,7 @@
 
 register = Registry()
 
-def convert_to_ipa_rule(rule):
+def _convert_to_ipa_rule(rule):
     # convert a dict with a rule to an pyhbac rule
     ipa_rule = pyhbac.HbacRule(rule['cn'][0])
     ipa_rule.enabled = rule['ipaenabledflag'][0]
@@ -309,6 +314,14 @@ def canonicalize(self, host):
         return host
 
     def execute(self, *args, **options):
+        if pyhbac is None:
+            raise errors.ValidationError(
+                name=_('missing pyhbac'),
+                error=_(
+                    'pyhbac is not available on the server.'
+                )
+            )
+
         # First receive all needed information:
         # 1. HBAC rules (whether enabled or disabled)
         # 2. Required options are (user, target host, service)
@@ -356,7 +369,7 @@ def execute(self, *args, **options):
         # --disabled will import all disabled rules
         # --rules will implicitly add the rules from a rule list
         for rule in hbacset:
-            ipa_rule = convert_to_ipa_rule(rule)
+            ipa_rule = _convert_to_ipa_rule(rule)
             if ipa_rule.name in testrules:
                 ipa_rule.enabled = True
                 rules.append(ipa_rule)

From 741b06600c556b76989a1ea374cb91729f19534e Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Tue, 17 Jan 2017 08:57:33 +0100
Subject: [PATCH 2/3] Add extra_requires for additional dependencies

ipaserver did not have extra_requires to state additional dependencies.

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 ipaserver/setup.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/ipaserver/setup.py b/ipaserver/setup.py
index 42b0c1b..227327b 100755
--- a/ipaserver/setup.py
+++ b/ipaserver/setup.py
@@ -60,12 +60,6 @@
             "pyasn1",
             "pyldap",
             "six",
-            # not available on PyPI
-            # "python-libipa_hbac",
-            # "python-sss",
-            # "python-sss-murmur",
-            # "python-SSSDConfig",
-            # "samba-python",
         ],
         entry_points={
             'custodia.authorizers': [
@@ -75,4 +69,12 @@
                 'IPASecStore = ipaserver.secrets.store:IPASecStore',
             ],
         },
+        extras_require={
+            # These packages are currently not available on PyPI.
+            "caacl": ["pyhbac"],
+            "dcerpc": ["samba", "pysss", "pysss_nss_idmap"],
+            "hbactest": ["pyhbac"],
+            "install": ["SSSDConfig"],
+            "trust": ["pysss_murmur", "pysss_nss_idmap"],
+        }
     )

From 8764e8da440ff7c4aa8f972e5f1a1b460e6a42ef Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Tue, 17 Jan 2017 12:16:25 +0100
Subject: [PATCH 3/3] Add an option to build ipaserver wheels

To create a wheel bundle with ipaserver and its dependencies:

    make wheel_bundle IPA_SERVER_WHEELS=1

To include additional dependencies:

    make wheel_bundle IPA_EXTRA_WHEELS=ipatests[webui]

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 Makefile.am                   | 22 ++++++++++++++++++----
 configure.ac                  |  6 ++++++
 freeipa.spec.in               |  4 +---
 ipaserver/plugins/hbactest.py | 10 +---------
 ipaserver/setup.py            |  1 -
 ipasetup.py.in                |  3 ++-
 6 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index af22315..3164717 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -227,6 +227,17 @@ endif  # WITH_JSLINT
 WHEELDISTDIR = $(top_builddir)/dist/wheels
 WHEELBUNDLEDIR = $(top_builddir)/dist/bundle
 
+@MK_IFEQ@ ($(IPA_SERVER_WHEELS),1)
+    IPA_WHEEL_PACKAGES @MK_ASSIGN@ $(IPACLIENT_SUBDIRS) ipaplatform ipaserver
+    IPA_OMIT_INSTALL @MK_ASSIGN@ 0
+@MK_ELSE@
+    IPA_WHEEL_PACKAGES @MK_ASSIGN@ $(IPACLIENT_SUBDIRS)
+    IPA_OMIT_INSTALL @MK_ASSIGN@ 1
+@MK_ENDIF@
+
+# additional wheels for bundle, e.g. IPA_EXTRA_WHEELS="ipatests[webui] pylint"
+IPA_EXTRA_WHEELS=
+
 $(WHEELDISTDIR):
 	mkdir -p $(WHEELDISTDIR)
 
@@ -234,19 +245,22 @@ $(WHEELBUNDLEDIR):
 	mkdir -p $(WHEELBUNDLEDIR)
 
 bdist_wheel: $(WHEELDISTDIR)
-	for dir in $(IPACLIENT_SUBDIRS); do \
+	rm -f $(foreach item,$(IPA_WHEEL_PACKAGES) ipatests,$(WHEELDISTDIR)/$(item)-*.whl)
+	export IPA_OMIT_INSTALL=$(IPA_OMIT_INSTALL); \
+	for dir in $(IPA_WHEEL_PACKAGES) ipatests; do \
 	    $(MAKE) $(AM_MAKEFLAGS) -C $${dir} $@ || exit 1; \
 	done
 
 wheel_bundle: $(WHEELBUNDLEDIR) bdist_wheel .wheelconstraints
-	rm -f $(foreach item,$(IPACLIENT_SUBDIRS),$(WHEELBUNDLEDIR)/$(item)-*.whl)
-	$(PYTHON) -m pip wheel \
+	rm -f $(foreach item,$(IPA_WHEEL_PACKAGES) ipatests,$(WHEELBUNDLEDIR)/$(item)-*.whl)
+	@# dbus-python sometimes fails when MAKEFLAGS is set to -j2 or higher
+	MAKEFLAGS= $(PYTHON) -m pip wheel \
 	    --disable-pip-version-check \
 	    --constraint .wheelconstraints \
 	    --find-links $(WHEELDISTDIR) \
 	    --find-links $(WHEELBUNDLEDIR) \
 	    --wheel-dir $(WHEELBUNDLEDIR) \
-	    $(IPACLIENT_SUBDIRS)
+	    $(IPA_WHEEL_PACKAGES) $(IPA_EXTRA_WHEELS)
 
 wheel_placeholder: $(WHEELDISTDIR)
 	for dir in $(IPA_PLACEHOLDERS); do \
diff --git a/configure.ac b/configure.ac
index f5c5270..4e3454f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -373,6 +373,12 @@ AC_SUBST([GIT_VERSION], [IPA_GIT_VERSION])
 # used by Makefile.am for files depending on templates
 AC_SUBST([CONFIG_STATUS])
 
+# workaround for syntax clash between make and automake
+AC_SUBST([MK_IFEQ], [ifeq])
+AC_SUBST([MK_ELSE], [else])
+AC_SUBST([MK_ENDIF], [endif])
+AC_SUBST([MK_ASSIGN], [=])
+
 dnl ---------------------------------------------------------------------------
 dnl Finish
 dnl ---------------------------------------------------------------------------
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 829c3f0..db6fbe1 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -31,9 +31,6 @@
     %global linter_options --disable-pylint --without-jslint
 %endif
 
-# Python wheel support and PyPI packages
-%global with_wheels 0
-
 %global alt_name ipa
 %if 0%{?rhel}
 # Require 4.6.0-4 which brings RC4 for FIPS + trust fixes to priv. separation
@@ -147,6 +144,7 @@ BuildRequires:  python-cffi
 # Build dependencies for wheel packaging and PyPI upload
 #
 %if 0%{with_wheels}
+BuildRequires:  dbus-devel
 BuildRequires:  python2-twine
 BuildRequires:  python2-wheel
 %if 0%{?with_python3}
diff --git a/ipaserver/plugins/hbactest.py b/ipaserver/plugins/hbactest.py
index e156568..9c39b9a 100644
--- a/ipaserver/plugins/hbactest.py
+++ b/ipaserver/plugins/hbactest.py
@@ -34,7 +34,7 @@
 try:
     import pyhbac
 except ImportError:
-    pyhbac = None
+    raise errors.SkipPluginModule(reason=_('pyhbac is not installed.'))
 
 
 if six.PY3:
@@ -314,14 +314,6 @@ def canonicalize(self, host):
         return host
 
     def execute(self, *args, **options):
-        if pyhbac is None:
-            raise errors.ValidationError(
-                name=_('missing pyhbac'),
-                error=_(
-                    'pyhbac is not available on the server.'
-                )
-            )
-
         # First receive all needed information:
         # 1. HBAC rules (whether enabled or disabled)
         # 2. Required options are (user, target host, service)
diff --git a/ipaserver/setup.py b/ipaserver/setup.py
index 227327b..097508f 100755
--- a/ipaserver/setup.py
+++ b/ipaserver/setup.py
@@ -71,7 +71,6 @@
         },
         extras_require={
             # These packages are currently not available on PyPI.
-            "caacl": ["pyhbac"],
             "dcerpc": ["samba", "pysss", "pysss_nss_idmap"],
             "hbactest": ["pyhbac"],
             "install": ["SSSDConfig"],
diff --git a/ipasetup.py.in b/ipasetup.py.in
index 7f9b2c9..382707c 100644
--- a/ipasetup.py.in
+++ b/ipasetup.py.in
@@ -29,7 +29,8 @@ class build_py(setuptools_build_py):
 
     def finalize_options(self):
         setuptools_build_py.finalize_options(self)
-        if 'bdist_wheel' in self.distribution.commands:
+        omit = os.environ.get('IPA_OMIT_INSTALL', '0')
+        if omit == '1':
             distname = self.distribution.metadata.name
             self.skip_package = '{}.install'.format(distname)
             log.warn("bdist_wheel: Ignore package: %s",
-- 
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

Reply via email to