summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am12
-rw-r--r--contrib/ci/README.md62
-rwxr-xr-xcontrib/ci/clean25
-rw-r--r--contrib/ci/configure.sh50
-rw-r--r--contrib/ci/deps.sh119
-rw-r--r--contrib/ci/distro.sh86
-rwxr-xr-xcontrib/ci/make-check-wrap52
-rw-r--r--contrib/ci/misc.sh116
-rwxr-xr-xcontrib/ci/rpm-spec-builddeps35
-rwxr-xr-xcontrib/ci/run371
-rwxr-xr-xcontrib/ci/valgrind-condense128
12 files changed, 1056 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index f935a3592..82432e181 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,3 +111,4 @@ sss_ssh_authorizedkeys
sss_ssh_knownhostsproxy
sssd_ssh
test-authtok
+/ci-build-*
diff --git a/Makefile.am b/Makefile.am
index 1b183d023..869ebb182 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -318,6 +318,11 @@ dist_noinst_SCRIPTS = \
contrib/rhel/update_debug_levels.py \
contrib/fedora/bashrc_sssd \
contrib/fedora/make_srpm.sh \
+ contrib/ci/clean \
+ contrib/ci/make-check-wrap \
+ contrib/ci/rpm-spec-builddeps \
+ contrib/ci/run \
+ contrib/ci/valgrind-condense \
src/tests/pyhbac-test.py \
src/tests/pysss_murmur-test.py
@@ -328,7 +333,12 @@ dist_noinst_DATA = \
src/config/testconfigs/sssd-badversion.conf \
src/config/testconfigs/sssd-invalid.conf \
src/config/testconfigs/sssd-invalid-badbool.conf \
- src/config/etc/sssd.api.d/crash_test_dummy
+ src/config/etc/sssd.api.d/crash_test_dummy \
+ contrib/ci/README.md \
+ contrib/ci/configure.sh \
+ contrib/ci/deps.sh \
+ contrib/ci/distro.sh \
+ contrib/ci/misc.sh
###############################
# Global compilation settings #
diff --git a/contrib/ci/README.md b/contrib/ci/README.md
new file mode 100644
index 000000000..6c87200db
--- /dev/null
+++ b/contrib/ci/README.md
@@ -0,0 +1,62 @@
+Continuous integration
+======================
+
+The executables and modules in this directory implement continuous integration
+(CI) tests, which can be run to verify SSSD code quality and validity.
+
+Supported host distros are Fedora 20 and later, RHEL 6.5 and later, and Debian
+Testing.
+
+The tests are executed by running `contrib/ci/run` from the source tree root.
+It accepts options to choose from three test sets: "essential", "moderate" and
+"rigorous" (-e/-m/-r), with the essential set selected by default.
+
+Essential tests include building everything and running the built-in test
+suite under Valgrind, completing in under 5 minutes. Valgrind failures are
+ignored for now.
+
+Moderate tests include essential tests, plus a distcheck target build and mock
+package builds for Fedora and RHEL on Red Hat distros. They complete in about
+15 minutes.
+
+Rigorous tests include moderate tests, plus a pass with Clang static analyzer
+over the whole build and test execution with code coverage collection and
+verification, completing in 30 minutes. Static analyzer failures are ignored
+for now.
+
+Use `contrib/ci/clean` to remove test results from the source tree.
+
+
+Setup
+-----
+
+CI requires `lsb_release` command to be available in order to determine host
+distro version. On Red Hat distros it is contained in the `redhat-lsb-core`
+package and on Debian in `lsb-release`.
+
+The rest of the required packages CI will attempt to install itself, using
+the distribution's package manager invoked through sudo.
+
+A sudo rule can be employed to selectively avoid password prompts on Red Hat
+distros:
+
+ <USER> ALL=(ALL:ALL) NOPASSWD: /usr/bin/yum --assumeyes install -- *
+
+and Debian-based distros:
+
+ <USER> ALL=(ALL:ALL) NOPASSWD: /usr/bin/apt-get --yes install -- *
+
+Where `<USER>` is the user invoking CI.
+
+On Red Hat distros a repository carrying dependencies missing from some
+distros needs to be added to yum configuration. See instructions on the
+[Copr project page](http://copr-fe.cloud.fedoraproject.org/coprs/lslebodn/sssd-deps/).
+That repository is also automatically used by CI during mock builds.
+
+Package installation can be disabled with the -n/--no-deps option, e.g. for
+manual dependency management, or for shaving off a few seconds of execution
+time, when dependency changes are not expected.
+
+On Red Hat distros, where mock builds are ran, it is better to have the
+invoking user added to the `mock` group. Otherwise mock builds will be
+executed through sudo.
diff --git a/contrib/ci/clean b/contrib/ci/clean
new file mode 100755
index 000000000..ee18c1070
--- /dev/null
+++ b/contrib/ci/clean
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Clean source tree after a run of integration tests.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+export PATH=`dirname "\`readlink -f \"\$0\"\`"`:$PATH
+
+. misc.sh
+
+rm_rf_ro ci-*
diff --git a/contrib/ci/configure.sh b/contrib/ci/configure.sh
new file mode 100644
index 000000000..b92b09441
--- /dev/null
+++ b/contrib/ci/configure.sh
@@ -0,0 +1,50 @@
+#
+# Configure argument management.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_CONFIGURE_SH+set} ]; then
+declare -r _CONFIGURE_SH=
+
+. distro.sh
+
+# List of "configure" arguments.
+declare -a CONFIGURE_ARG_LIST=(
+ "--disable-dependency-tracking"
+ "--disable-rpath"
+ "--disable-static"
+ "--enable-ldb-version-check"
+ "--with-syslog=journald"
+)
+
+
+if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-6.*- ]]; then
+ CONFIGURE_ARG_LIST+=(
+ "--disable-cifs-idmap-plugin"
+ "--with-syslog=syslog"
+ )
+fi
+
+if [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ CONFIGURE_ARG_LIST+=(
+ # TODO Remove once libini_config >= 1.1.0 becomes available
+ "--without-samba"
+ )
+fi
+
+declare -r -a CONFIGURE_ARG_LIST
+
+fi # _CONFIGURE_SH
diff --git a/contrib/ci/deps.sh b/contrib/ci/deps.sh
new file mode 100644
index 000000000..ba59f7848
--- /dev/null
+++ b/contrib/ci/deps.sh
@@ -0,0 +1,119 @@
+#
+# Dependency management.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_DEPS_SH+set} ]; then
+declare -r _DEPS_SH=
+
+. distro.sh
+
+# Dependency list
+declare -a DEPS_LIST=(
+ lcov
+ valgrind
+ xqilla
+)
+
+# True, if all test dependencies are satisfied by the package list
+declare DEPS_TESTS_SATISFIED=true
+
+if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ declare _DEPS_LIST_SPEC
+ DEPS_LIST+=(
+ clang-analyzer
+ mock
+ rpm-build
+ )
+ _DEPS_LIST_SPEC=`
+ sed -e 's/@PACKAGE_VERSION@/0/g' \
+ -e 's/@PACKAGE_NAME@/package-name/g' \
+ -e 's/@PRERELEASE_VERSION@//g' contrib/sssd.spec.in |
+ rpm-spec-builddeps /dev/stdin`
+ readarray -t -O "${#DEPS_LIST[@]}" DEPS_LIST <<<"$_DEPS_LIST_SPEC"
+ if [[ "$DISTRO_BRANCH" == *-redhatenterprise*-6.*- ]]; then
+ DEPS_TESTS_SATISFIED=false
+ fi
+fi
+
+if [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ DEPS_LIST+=(
+ autoconf
+ automake
+ autopoint
+ check
+ cifs-utils
+ clang
+ dh-apparmor
+ dnsutils
+ docbook-xml
+ docbook-xsl
+ gettext
+ krb5-config
+ libaugeas-dev
+ libc-ares-dev
+ libcmocka-dev
+ libcollection-dev
+ libdbus-1-dev
+ libdhash-dev
+ libglib2.0-dev
+ libini-config-dev
+ libkeyutils-dev
+ libkrb5-dev
+ libldap2-dev
+ libldb-dev
+ libltdl-dev
+ libnl-3-dev
+ libnl-route-3-dev
+ libnspr4-dev
+ libnss3-dev
+ libpam0g-dev
+ libpcre3-dev
+ libpopt-dev
+ libsasl2-dev
+ libselinux1-dev
+ libsemanage1-dev
+ libsmbclient-dev
+ libsystemd-journal-dev
+ libtalloc-dev
+ libtdb-dev
+ libtevent-dev
+ libtool
+ libxml2-utils
+ python-dev
+ samba-dev
+ systemd
+ xml-core
+ xsltproc
+ )
+fi
+
+declare -a -r DEPS_LIST
+declare -r DEPS_TESTS_SATISFIED
+
+# Install dependencies.
+function deps_install()
+{
+ distro_pkg_install "${DEPS_LIST[@]}"
+}
+
+# Remove dependencies.
+function deps_remove()
+{
+ distro_pkg_remove "${DEPS_LIST[@]}"
+}
+
+fi # _DEPS_SH
diff --git a/contrib/ci/distro.sh b/contrib/ci/distro.sh
new file mode 100644
index 000000000..5416bfff3
--- /dev/null
+++ b/contrib/ci/distro.sh
@@ -0,0 +1,86 @@
+#
+# Distribution version discovery
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_DISTRO_SH+set} ]; then
+declare -r _DISTRO_SH=
+
+# Distribution family (lowercase)
+declare DISTRO_FAMILY=
+# Distribution ID (lowercase)
+declare DISTRO_ID=
+# Distribution release (lowercase)
+declare DISTRO_RELEASE=
+
+if [ -e /etc/redhat-release ]; then
+ DISTRO_FAMILY=redhat
+elif [ -e /etc/debian_version ]; then
+ DISTRO_FAMILY=debian
+else
+ DISTRO_FAMILY=unknown
+fi
+declare -r DISTRO_FAMILY
+
+DISTRO_ID=`lsb_release --id | sed -e 's/^[^:]*:\s*\(.*\)$/\L\1\E/'`
+declare -r DISTRO_ID
+DISTRO_RELEASE=`lsb_release --release | sed -e 's/^[^:]*:\s*\(.*\)$/\L\1\E/'`
+declare -r DISTRO_RELEASE
+
+# Distribution branch (lowercase)
+declare -r DISTRO_BRANCH="-$DISTRO_FAMILY-$DISTRO_ID-$DISTRO_RELEASE-"
+
+
+# Install packages.
+# Args: [pkg_name...]
+function distro_pkg_install()
+{
+ declare prompt=$'Need root permissions to install packages.\n'
+ prompt+="Enter sudo password for $USER: "
+ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" yum --assumeyes install -- "$@" |&
+ # Pass input to output, fail if a missing package is reported
+ # TODO Remove and switch to DNF once
+ # https://bugzilla.redhat.com/show_bug.cgi?id=1128139 is fixed
+ awk 'BEGIN {s=0}
+ /^No package .* available.$/ {s=1}
+ {print}
+ END {exit s}'
+ elif [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" apt-get --yes install -- "$@"
+ else
+ echo "Cannot install packages on $DISTRO_BRANCH" >&2
+ exit 1
+ fi
+}
+
+# Remove packages.
+# Args: [pkg_name...]
+function distro_pkg_remove()
+{
+ declare prompt=$'Need root permissions to remove packages.\n'
+ prompt+="Enter sudo password for $USER: "
+ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" yum --assumeyes remove -- "$@"
+ elif [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" apt-get --yes remove -- "$@"
+ else
+ echo "Cannot remove packages on $DISTRO_BRANCH" >&2
+ exit 1
+ fi
+}
+
+fi # _DISTRO_SH
diff --git a/contrib/ci/make-check-wrap b/contrib/ci/make-check-wrap
new file mode 100755
index 000000000..4ad323e5f
--- /dev/null
+++ b/contrib/ci/make-check-wrap
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Build a make check target, prepending test commands with specified command
+# and arguments.
+# Args: [make_arg...] [-- wrap_cmd wrap_arg...]
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+
+declare -a argv=("make")
+declare arg
+
+while [ $# != 0 ]; do
+ arg="$1"
+ shift
+ if [ "$arg" == "--" ]; then
+ break
+ fi
+ argv+=("$arg")
+done
+
+if [ $# != 0 ]; then
+ # If Makefile supports LOG_COMPILER
+ if grep -q -w LOG_COMPILER Makefile; then
+ printf -v arg 'LOG_COMPILER=%q' $1
+ argv+=("$arg")
+ shift
+ if [ $# != 0 ]; then
+ printf -v arg ' %q' "$@"
+ argv+=("LOG_FLAGS=$arg")
+ fi
+ else
+ printf -v arg ' %q' "$@"
+ argv+=("AUX_TESTS_ENVIRONMENT=$arg")
+ fi
+fi
+
+"${argv[@]}"
diff --git a/contrib/ci/misc.sh b/contrib/ci/misc.sh
new file mode 100644
index 000000000..9af848c76
--- /dev/null
+++ b/contrib/ci/misc.sh
@@ -0,0 +1,116 @@
+#
+# Miscellaneous routines.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_MISC_SH+set} ]; then
+declare -r _MISC_SH=
+
+# Remove files and directories recursively, forcing write permissions on
+# directories.
+# Args: path...
+function rm_rf_ro()
+{
+ chmod -Rf u+w -- "$@" || true
+ rm -Rf -- "$@"
+}
+
+# Run "scan-build" with output to a single specified directory, instead of
+# a subdirectory.
+# Args: dir [scan_build_arg...]
+function scan_build_single()
+{
+ declare -r dir="$1"; shift
+ declare entry
+ declare subdir
+ declare status
+
+ set +o errexit
+ scan-build -o "$dir" "$@"
+ status="$?"
+ set -o errexit
+
+ for entry in "$dir/"*; do
+ if [ -n "${subdir+set}" ] || ! [ -d "$entry" ]; then
+ echo 'Unexpected entries in scan-build output directory' >&2
+ exit 1
+ fi
+ subdir="$entry"
+ done
+
+ mv "$subdir/"* "$dir"
+ rmdir "$subdir"
+ return "$status"
+}
+
+# Check if a scan-build result directory has any non-empty .plist files.
+# Args: dir
+function scan_check()
+{
+ declare -r dir="$1"
+ declare f
+ for f in "$dir"/*.plist; do
+ if [ "`xqilla -i \"\$f\" /dev/stdin \
+ <<<'count(/plist/dict/array[count(*) > 0])'`" != 0 ]; then
+ return 1
+ fi
+ done
+ return 0
+}
+
+# Extract line and function coverage percentage from a "genhtml" or "lcov
+# --summary" output.
+# Input: "genhtml" or "lcov --summary" output
+# Output: lines funcs
+function lcov_summary()
+{
+ sed -ne 's/^ *\(lines\|functions\)\.*: \([0-9]\+\).*$/ \2/p' |
+ tr -d '\n'
+ echo
+}
+
+# Check if a "genhtml" or "lcov --summary" output has a minimum coverage
+# percentage of lines and functions.
+# Input: "genhtml" or "lcov --summary" output
+# Args: min_lines min_funcs
+function lcov_check()
+{
+ declare -r min_lines="$1"; shift
+ declare -r min_funcs="$1"; shift
+ declare lines
+ declare funcs
+
+ read -r lines funcs < <(lcov_summary)
+ ((lines >= min_lines && funcs >= min_funcs)) && return 0 || return 1
+}
+
+# Check if the current user belongs to a group.
+# Args: group_name
+function memberof()
+{
+ declare -r group_name="$1"
+ declare group_id
+ declare id
+ group_id=`getent group "$group_name" | cut -d: -f3` || return 1
+ for id in "${GROUPS[@]}"; do
+ if [ "$id" == "$group_id" ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+fi # _MISC_SH
diff --git a/contrib/ci/rpm-spec-builddeps b/contrib/ci/rpm-spec-builddeps
new file mode 100755
index 000000000..b73ed2083
--- /dev/null
+++ b/contrib/ci/rpm-spec-builddeps
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#
+# Extract build dependencies from an RPM .spec file.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import re
+import rpm
+
+def usage(file):
+ file.write(("Usage: %s SPEC\n" +
+ "Extract build dependencies from an RPM .spec file.\n") %
+ re.match(".*?([^/]+)$", sys.argv[0]).group(1))
+
+if len(sys.argv) != 2:
+ usage(sys.stderr)
+ sys.exit(1)
+
+spec = rpm.spec(sys.argv[1])
+for d in rpm.ds(spec.sourceHeader, 'requires'):
+ print d.DNEVR()[2:]
diff --git a/contrib/ci/run b/contrib/ci/run
new file mode 100755
index 000000000..b8a296aea
--- /dev/null
+++ b/contrib/ci/run
@@ -0,0 +1,371 @@
+#!/bin/bash
+#
+# Run continuous integration tests.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+export PATH=`dirname "\`readlink -f \"\$0\"\`"`:$PATH
+export LC_ALL=C
+
+. deps.sh
+. distro.sh
+. configure.sh
+. misc.sh
+
+declare -r DEBUG_CFLAGS="-g3 -O2"
+declare -r COVERAGE_CFLAGS="-g3 -O0 --coverage"
+declare -r ARCH=`uname -m`
+declare -r CPU_NUM=`getconf _NPROCESSORS_ONLN`
+declare -r TITLE_WIDTH=24
+declare -r RESULT_WIDTH=18
+
+# Minimum percentage of code lines covered by tests
+declare -r COVERAGE_MIN_LINES=15
+# Minimum percentage of code functions covered by tests
+declare -r COVERAGE_MIN_FUNCS=0
+
+declare BASE_PFX=""
+declare DEPS=true
+declare BASE_DIR=`pwd`
+declare MODERATE=false
+declare RIGOROUS=false
+
+# Output program usage information.
+function usage()
+{
+ cat <<EOF
+Usage: `basename "$0"` [OPTION...]
+Run continuous integration tests.
+
+Options:
+ -h, --help Output this help message and exit.
+ -p, --prefix=STRING Use STRING as the prefix to prepend to file and
+ directory paths in output.
+ -n, --no-deps Don't attempt to install dependencies.
+ -e, --essential Run the essential subset of tests.
+ -m, --moderate Run the moderate subset of tests.
+ -r, --rigorous,
+ -f, --full Run the rigorous (full) set of tests.
+
+Default options: --essential
+EOF
+}
+
+# Output a file display path: a path relocated from base directory (BASE_DIR)
+# to base prefix (BASE_PFX).
+# Args: path
+function disppath()
+{
+ declare -r path=`readlink -f "$1"`
+ printf "%s" "$BASE_PFX${path:${#BASE_DIR}+1}"
+}
+
+# Run a stage.
+# Args: id cmd [arg...]
+function stage()
+{
+ declare -r id="$1"; shift
+ declare -r log="ci-$id.log"
+ declare status
+ declare start
+ declare end
+ declare duration
+
+ printf "%-${TITLE_WIDTH}s" "$id:"
+
+ {
+ printf "Start: "
+ start=`date +%s`
+ date --date="@$start"
+ set +o errexit
+ (
+ set -o errexit -o xtrace
+ "$@"
+ )
+ status=$?
+ set -o errexit
+ printf "End: "
+ end=`date +%s`
+ date --date="@$end"
+ } &> "$log"
+
+ duration=$((end - start))
+
+ if [ "$status" == 0 ]; then
+ printf 'success '
+ else
+ printf 'failure '
+ fi
+ printf "%02u:%02u:%02u " \
+ $((duration / (60 * 60))) \
+ $((duration / 60 % 60)) \
+ $((duration % 60))
+ disppath "$log"
+ printf "\n"
+
+ return "$status"
+}
+
+# Execute mock as is, or, if the user is not in the "mock" group, under sudo,
+# which has password prompt/input on the console, instead of stderr/stdin.
+# Args: [mock_arg...]
+function mock_privileged()
+{
+ if memberof mock; then
+ mock "$@"
+ else
+ declare prompt=$'Not a "mock" group member.\n'
+ prompt+="To run mock enter sudo password for $USER: "
+ sudo -p "$prompt" mock "$@"
+ fi
+}
+
+# Execute mock_privileged with extra chroot configuration added.
+# Args: chroot [mock_arg...]
+# Input: extra configuration
+function mock_privileged_conf()
+{
+ declare -r chroot="$1"; shift
+ declare conf_dir
+
+ conf_dir=`mktemp --tmpdir --directory mock-config.XXXXXXXX`
+ trap 'trap - RETURN; rm -R "$conf_dir";' RETURN
+ cp -r /etc/mock/* "$conf_dir"/
+ cat >> "${conf_dir}/${chroot}.cfg"
+ mock_privileged --configdir="$conf_dir" --root="$chroot" "$@"
+}
+
+# Execute mock_privileged with dependency package source configuration added.
+# Args: chroot [mock_arg...]
+function mock_privileged_deps()
+{
+ declare -r chroot="$1"; shift
+ declare repo
+
+ if [[ "$chroot" == fedora-* ]]; then
+ repo='fedora-$releasever-$basearch'
+ elif [[ "$chroot" =~ epel-([0-9]+) ]]; then
+ repo="epel-${BASH_REMATCH[1]}-\$basearch"
+ else
+ echo "Unknown chroot config: $chroot" >&2
+ exit 1
+ fi
+
+ mock_privileged_conf "$chroot" "$@" <<<"
+config_opts['yum.conf'] += '''
+[sssd-deps]
+name=Extra SSSD dependencies
+baseurl=http://copr-be.cloud.fedoraproject.org/results/lslebodn/sssd-deps/$repo/
+skip_if_unavailable=true
+gpgcheck=0
+enabled=1
+'''
+"
+}
+
+# Run debug build checks.
+function build_debug()
+{
+ export CFLAGS="$DEBUG_CFLAGS"
+ declare test_dir
+ declare test_dir_distcheck
+ declare distcheck_configure_args
+ declare status
+
+ test_dir=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
+ stage configure "$BASE_DIR/configure" \
+ "${CONFIGURE_ARG_LIST[@]}" \
+ --with-test-dir="$test_dir"
+
+ # Not building "tests" due to https://fedorahosted.org/sssd/ticket/2350
+ stage make-tests make-check-wrap -j $CPU_NUM check -- true
+
+ # Ignored until issues found by Valgrind are fixed
+ status=0
+ CK_FORK=no \
+ stage make-check-valgrind \
+ make-check-wrap -j $CPU_NUM check -- \
+ libtool --mode=execute \
+ valgrind-condense 99 '!(*.py)' -- \
+ --vgdb=no \
+ --trace-children=yes \
+ --trace-children-skip='*/bin/*,*/sbin/*' \
+ --leak-check=full ||
+ status=$?
+ mv "$test_dir" ci-test-dir
+
+ if "$MODERATE"; then
+ test_dir_distcheck=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
+ # Single thread due to https://fedorahosted.org/sssd/ticket/2354
+ status=0
+ printf -v distcheck_configure_args " %q" \
+ "${CONFIGURE_ARG_LIST[@]}" \
+ "--with-test-dir=$test_dir_distcheck"
+ stage make-distcheck make distcheck \
+ AUX_DISTCHECK_CONFIGURE_FLAGS=" \
+ $distcheck_configure_args" ||
+ status=$?
+ mv "$test_dir_distcheck" ci-test-dir-distcheck
+ ((status == 0))
+
+ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ stage make-srpm env -u CFLAGS -- make srpm
+ stage mock-epel6 mock_privileged_deps "epel-6-$ARCH" \
+ --resultdir ci-mock-result-epel6 \
+ rpmbuild/SRPMS/*.src.rpm
+ stage mock-fedora20 mock_privileged_deps "fedora-20-$ARCH" \
+ --resultdir ci-mock-result-fedora20 \
+ rpmbuild/SRPMS/*.src.rpm
+ fi
+ fi
+
+ unset CFLAGS
+}
+
+# Run coverage build checks.
+function build_coverage()
+{
+ declare -r scan_report_dir="ci-report-scan"
+ declare -r coverage_report_dir="ci-report-coverage"
+ declare test_dir
+
+ export CFLAGS="$COVERAGE_CFLAGS"
+
+ test_dir=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
+ stage configure scan-build "$BASE_DIR/configure" \
+ "${CONFIGURE_ARG_LIST[@]}" \
+ --with-test-dir="$test_dir"
+
+ # Build everything, including tests
+ # Not building "tests" due to https://fedorahosted.org/sssd/ticket/2350
+ stage scan-make-tests scan_build_single \
+ "$scan_report_dir" \
+ -plist-html \
+ --html-title="sssd - scan-build report" \
+ make-check-wrap -j $CPU_NUM check -- true
+ printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
+ "scan report:" \
+ "`disppath \"\$scan_report_dir/index.html\"`"
+ # Ignored until issues found by the scanner are fixed
+ stage scan-check scan_check "$scan_report_dir" ||
+ true
+
+ stage lcov-pre lcov --capture --initial --directory . \
+ --base-directory "$BASE_DIR" \
+ --output-file ci-base.info
+ # Run tests
+ stage make-check scan-build make -j $CPU_NUM check || true
+ mv "$test_dir" ci-test-dir
+
+ stage lcov-post lcov --capture --directory . \
+ --base-directory "$BASE_DIR" \
+ --output-file ci-check.info
+ stage lcov-merge lcov --add-tracefile ci-base.info \
+ --add-tracefile ci-check.info \
+ --output-file ci-dirty.info
+ stage lcov-clean lcov --remove ci-dirty.info \
+ "/usr/*" "src/tests/*" \
+ --output-file ci.info
+ stage genhtml eval 'genhtml --output-directory \
+ "$coverage_report_dir" \
+ --title "sssd" --show-details \
+ --legend --prefix "$BASE_DIR" \
+ ci.info |& tee ci-genhtml.out'
+ printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
+ "coverage report:" \
+ "`disppath \"\$coverage_report_dir/index.html\"`"
+
+ # If dependencies for all tests are satisfied
+ # and so all the tests should have been built and ran.
+ if "$DEPS_TESTS_SATISFIED"; then
+ stage lcov-check eval 'lcov_check "$COVERAGE_MIN_LINES" \
+ "$COVERAGE_MIN_FUNCS" \
+ < ci-genhtml.out'
+ fi
+
+ unset CFLAGS
+}
+
+# Run a build inside a sub-directory.
+# Args: id cmd [arg...]
+function run_build()
+{
+ declare -r id="$1"; shift
+ declare -r dir="ci-build-$id"
+
+ mkdir "$dir"
+ printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
+ "${id^^} BUILD:" "`disppath \"\$dir\"`"
+
+ cd "$dir"
+ "$@"
+ cd ..
+}
+
+#
+# Main routine
+#
+declare args_expr
+args_expr=`getopt --name \`basename "\$0"\` \
+ --options hp:nemrf \
+ --longoptions help,prefix:,no-deps \
+ --longoptions essential,moderate,rigorous,full \
+ -- "$@"`
+eval set -- "$args_expr"
+
+while true; do
+ case "$1" in
+ -h|--help)
+ usage; exit 0;;
+ -p|--prefix)
+ BASE_PFX="$2"; shift 2;;
+ -n|--no-deps)
+ DEPS=false; shift;;
+ -e|--essential)
+ MODERATE=false; RIGOROUS=false; shift;;
+ -m|--moderate)
+ MODERATE=true; RIGOROUS=false; shift;;
+ -r|--rigorous|-f|--full)
+ MODERATE=true; RIGOROUS=true; shift;;
+ --)
+ shift; break;;
+ *)
+ echo "Unknown option: $1" >&2
+ exit 1;;
+ esac
+done
+
+if [ $# != 0 ]; then
+ echo "Positional arguments are not accepted." >&2
+ usage >&2
+ exit 1
+fi
+
+trap 'echo FAILURE' EXIT
+rm_rf_ro ci-*
+export V=1
+if "$DEPS"; then
+ stage install-deps deps_install
+fi
+stage autoreconf autoreconf --install --force
+run_build debug build_debug
+if "$RIGOROUS"; then
+ run_build coverage build_coverage
+fi
+unset V
+trap - EXIT
+echo SUCCESS
diff --git a/contrib/ci/valgrind-condense b/contrib/ci/valgrind-condense
new file mode 100755
index 000000000..b838039e2
--- /dev/null
+++ b/contrib/ci/valgrind-condense
@@ -0,0 +1,128 @@
+#!/bin/bash
+#
+# Run Valgrind, condensing logged reports into an exit code.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+shopt -s extglob
+
+function usage()
+{
+ cat <<EOF
+Usage: `basename "$0"` ERROR_EXITCODE [PATH_PATTERN...] [-- VALGRIND_ARG...]
+Run Valgrind, condensing logged reports into an exit code.
+
+Arguments:
+ ERROR_EXITCODE An exit code to return if at least one error is found in
+ Valgrind log files.
+ PATH_PATTERN An extended glob pattern matching (original) paths to
+ programs to execute under Valgrind. Execution is skipped
+ and success is returned for non-matching programs. Without
+ patterns, all programs match.
+ VALGRIND_ARG An argument to pass to Valgrind after the arguments
+ specified by `basename "$0"`.
+
+The first non-option VALGRIND_ARG will be considered the path to the program
+to execute under Valgrind and will be used in naming Valgrind log files as
+such:
+
+ PROGRAM_NAME.PID.valgrind.log
+
+where PROGRAM_NAME is the filename portion of the program path and PID is the
+executed process ID. If the last directory of the program path is ".libs" and
+the filename begins with "lt-", both are removed to match the name of libtool
+frontend script. All files matching PROGRAM_NAME.*.valgrind.log are removed
+before invoking Valgrind.
+
+If an error is found in Valgrind log files, ERROR_EXITCODE is returned,
+otherwise Valgrind exit code is returned.
+EOF
+}
+
+
+if [[ $# == 0 ]]; then
+ echo "Invalid number of arguments." >&2
+ usage >&2
+ exit 1
+fi
+
+declare error_exitcode="$1"; shift
+declare -a path_pattern_list=()
+declare arg
+declare got_dash_dash
+declare program_path
+declare path_pattern
+declare match
+declare program_name
+declare status=0
+
+# Extract path patterns
+while [[ $# != 0 ]]; do
+ arg="$1"
+ shift
+ if [[ "$arg" == "--" ]]; then
+ break
+ else
+ path_pattern_list+=("$arg")
+ fi
+done
+
+# Find program path argument
+got_dash_dash=false
+for arg in "$@"; do
+ if [[ "$arg" == "--" ]]; then
+ got_dash_dash=true
+ elif "$got_dash_dash" || [[ "$arg" != -* ]]; then
+ program_path="$arg"
+ break
+ fi
+done
+
+if [[ -z "${program_path+set}" ]]; then
+ echo "Program path not specified." >&2
+ usage >&2
+ exit 1
+fi
+
+# Match against path patterns, if any
+if [[ ${#path_pattern_list[@]} != 0 ]]; then
+ match=false
+ for path_pattern in "${path_pattern_list[@]}"; do
+ if [[ "$program_path" == $path_pattern ]]; then
+ match=true
+ fi
+ done
+ if ! $match; then
+ exit 0
+ fi
+fi
+
+# Generate original path from libtool path
+program_path=`sed -e 's/^\(.*\/\)\?\.libs\/lt-\([^\/]\+\)$/\1\2/' \
+ <<<"$program_path"`
+
+program_name=`basename "$program_path"`
+
+rm -f "$program_name".*.valgrind.log
+valgrind --log-file="$program_name.%p.valgrind.log" "$@" || status=$?
+
+if grep -q '^==[0-9]\+== *ERROR SUMMARY: *[1-9]' \
+ "$program_name".*.valgrind.log; then
+ exit "$error_exitcode"
+else
+ exit "$status"
+fi