Skip to content

Commit

Permalink
Create test scenarios with ipalab-config
Browse files Browse the repository at this point in the history
ipalab-config allows the creation of complex IPA environments using
rootless containers. For example, the tool provides the means to create
an environment where a trust can be set between IPA and Samba AD DC,
which is similar enough to Windows AD DC to be used in ansible-freeipa
testing.

To start a test scenario run 'infra/scenarios/start-scenario' passing
the scenario configuration as parameter. The configuration for the
scenario will be generated with 'ipalab-config', the containers will be
started, all the nodes will be deployed and the initial configuration
will be applied to the scenario. The configuration directory will be
moved to the repository root.

Only one scenario can be run at a single time.

When the tests are finished, 'infra/scenarios/stop-scenario' will
cleanup the environment, and a new scenario can be started. The
configuration directory created will not be removed, in this case, but
will be overwritten if a new scenario is created.

Signed-off-by: Rafael Guterres Jeffman <[email protected]>
  • Loading branch information
rjeffman committed Feb 9, 2025
1 parent 9bc3f0f commit 8028268
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ importer_result.json
/.tox/
/.venv/

# ansible-freeipa test environments
/**/ansible-freeipa-scenario/

# test output files
tests/logs/
TEST*.xml
8 changes: 4 additions & 4 deletions infra/azure/scripts/set_test_modules
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ then
[ ${#tests[@]} -gt 0 ] && enabled_tests=$(IFS=, ; echo "${tests[*]}")
[ -z "${enabled_tests}" ] && enabled_tests="None"

[ -n "${enabled_tests}" ] && IPA_ENABLED_TESTS="${enabled_tests},${IPA_ENABLED_TESTS}"
[ -n "${enabled_modules}" ] && IPA_ENABLED_MODULES="${enabled_modules},${IPA_ENABLED_MODULES}"
[ -n "${enabled_tests}" ] && IPA_ENABLED_TESTS="${enabled_tests},${IPA_ENABLED_TESTS:-}"
[ -n "${enabled_modules}" ] && IPA_ENABLED_MODULES="${enabled_modules},${IPA_ENABLED_MODULES:-}"

rm -f "${files_list}"
fi
Expand All @@ -61,7 +61,7 @@ fi
export IPA_ENABLED_MODULES
export IPA_ENABLED_TESTS

echo "IPA_ENABLED_MODULES = [${IPA_ENABLED_MODULES}]"
echo "IPA_ENABLED_TESTS = [${IPA_ENABLED_TESTS}]"
echo "IPA_ENABLED_MODULES = [${IPA_ENABLED_MODULES:-}]"
echo "IPA_ENABLED_TESTS = [${IPA_ENABLED_TESTS:-}]"

popd >/dev/null 2>&1 || die "Failed to change back to original directory."
60 changes: 60 additions & 0 deletions infra/scenarios/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
ansible-freeipa testing scenarios
=================================

The ansible-freeipa testing scenarios are a collection of scripts and configuration files to aid on the creation of environments composed of single or multiple IPA deployments, each one with one or more hosts, and external hosts like name servers or Samba Active Directory Domain Controllers.

The environment created is based on rootless containers (what itself may impose some limits and restrictions on testing) that are part of a `pod`. A custom bridge network is used for the `pod`.


Dependencies
------------

* ipalab-config version 0.10.3 or later
* podman-compose
* podman

All dependencies can be installed in a Python virtual environment.


Scenarios
---------

The following test scenarios are currently available:

**ipa-ad-trust.yml**

A scenario with one server, one client and one node not part of the IPA deployment running Samba AD DC. This scenario can be used to run AD related tests.


Restrictions
------------

When creating new scenarios, these rules apply:

* All scenarios `lab_name` must be `ansible-freeipa-scenario`
* All playbooks to be executed when starting a scenario must named starting with `config_`
* There's no guarantee on the order the configuration playbooks will be executed
* Non-IPA nodes are deployed before the IPA clusters


Usage Example
-------------

In this example a scenario with a server, a client and an AD Domain Controller (Samba) is created:

```
$ infra/scenarios/start-scenario infra/scenarios/ipa-ad-trust.yml
```

After the scenario is used, it can be shutdown with:

```
$ infra/scenarios/stop-scenario
```

To choose the distribution used for the IPA cluster, use the `-d` option:

```
$ infra/scenarios/start-scenario -d c9s infra/scenarios/ipa-ad-trust.yml
```

57 changes: 57 additions & 0 deletions infra/scenarios/ipa-ad-trust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# IPA trust to Samba AD DC.
#
# Steps to set trust on 'server':
# # kinit admin <<< SomeADMINpassword
# # ipa dnsforwardzone-add ad.ipa.test. --forwarder=192.168.13.250
# # ipa trust-add ad.ipa.test --type ad --range-type ipa-ad-trust --two-way true --admin=Administrator --password <<< Secret123
#
# Create samba user on 'addc':
# # samba-tool user create jdoe --given-name John --surname Doe
#
# Checking user on IPA server:
#
# # getent passwd [email protected]
# # kinit [email protected]
#
---
lab_name: ansible-freeipa-scenario
subnet: "192.168.13.0/24"
extra_data:
- playbooks/config_trust_users.yml
external:
hosts:
- name: addc
hostname: dc.ad.ipa.test
role: addc
ip_address: 192.168.13.250
options:
forwarder: 192.168.13.100
ipa_deployments:
- name: ipa
domain: linux.ipa.test
admin_password: SomeADMINpassword
dm_password: SomeDMpassword
vars:
# Trust variables are needed on both server and client
# to run test for both supported contexts
# trust test vars
winserver_domain: ad.ipa.test
winserver_admin_password: Secret123
winserver_ip: 192.168.13.250
# external users vars
test_ad_user: 'jdoe@DC'
test_alt_user: '[email protected]'
cluster:
servers:
- name: server
capabilities: ["DNS", "AD", "KRA"]
ip_address: 192.168.13.100
vars:
ipaserver_netbios_name: IPA
ipaserver_idstart: 60000
ipaserver_idmax: 62000
ipaserver_rid_base: 63000
ipaserver_secondary_rid_base: 70000
clients:
- name: cli01
dns: server
17 changes: 17 additions & 0 deletions infra/scenarios/playbooks/config_trust_users.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
- name: Add some users to Samba AD DC
hosts: addc
become: false
gather_facts: false

tasks:
- name: Add users to AD DC
ansible.builtin.shell: samba-tool user create {{ item.login }} --given-name {{ item.first }} --surname {{ item.last }}
args:
stdin: |
Secret123
Secret123
loop:
- {login: "jdoe", first: "John", last: "Doe"}
- {login: "lanne", first: "Lisa", last: "Anne"}
- {login: "zica", first: "Zoe", last: "Ica"}
2 changes: 2 additions & 0 deletions infra/scenarios/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ipalab-config>=0.10.3
podman-compose>=1.2.0
125 changes: 125 additions & 0 deletions infra/scenarios/run_test_scenario.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/bash -eu

BASEDIR="$(dirname $(readlink -f "$0"))"
TOPDIR="$(dirname "$(dirname "${BASEDIR}")")"
TESTDIR="${TOPDIR}/tests"

INVENTORY="${TOPDIR}/ansible-freeipa-scenario/inventory.yml "

usage() {
cat <<EOF
usage: run_test_scenario [-k] [-a] [-s SCENARIO] [-m MODULE] [PLAYBOOK...]
Run playbooks against an ansible-freeipa scenario.
Options:
-h Display this help screen.
-k Destroy the scenario after running tests.
-a Run all available test playbooks.
-s SCENARIO Select scenario (default: ipa-ad-trust)
-m MODULE Run all tests for module.
May be used multiple times.
Notes:
* When no PLAYBOOK or '-a' is provided, check changes in repository to select test to run.
EOF
}

die() {
[ $# -gt 0 ] && echo "FATAL: $*" >&2
exit 1
}

split() {
python3 -c "print('\n'.join(input().split(',')))" <&0
}

declare -a MODULES=()
SCENARIO="${TOPDIR}/infra/scenarios/ipa-ad-trust.yml"

while getopts ":haks:m:" option
do
case "${option}" in
h) usage && exit 1 ;;
a) RUN_ALL="Y" ; export SKIP_GIT_TEST="True" ;;
k) SHUTDOWN="YES" ;;
m) MODULES+="${OPTARG}" ;;
*) die "Invalid option: ${option}" ;;
esac
done
shift $((OPTIND - 1))

# Check test selection options
[ -n "${RUN_ALL:-}" ] && [ $# -gt 0 ] && die "Cannot use '-a' and set playbooks."
[ -n "${RUN_ALL:-}" ] && [ -n "${MODULES:-}" ] && die "Cannot use '-a' and select modules."

# Use provided tests
[ -n "${MODULES:-}" ] && IPA_ENABLED_MODULES="$(echo "${MODULES[@]}" | tr " " ",")"
[ -n "${1:-}" ] && IPA_ENABLED_TESTS="$(echo "$@" | tr " " "\n" | xargs -n 1 basename -s .yml | tr "\n" ",")"

# Ensure a scenario is running
if [ -z "$(podman pod ls --filter name=ansible-freeipa-scenario --format "{{ .Name }}")" ]
then
"${TOPDIR}/infra/scenarios/start-scenario" "${SCENARIO}" || die
else
echo "WARNING: Running tests against existing scenario."
fi

declare -a PLAYBOOKS=()

[ -z "${1:-}" ] && \
[ -z "${IPA_ENABLED_MODULES:-}" ] \
&& [ -z "${IPA_ENABLED_TESTS:-}" ] \
&& . infra/azure/scripts/set_test_modules

# Get list of disabled tests
declare -a IGNORE

# Get playbooks for all enabled modules
for module in $(split <<< "${IPA_DISABLED_MODULES[@]}")
do
grep -qe "^None" <<< "${module}" && continue
IGNORE+=($(find "${TESTDIR}/${module}" -name "test_*.yml" ))
done

# Get playbooks for all enabled tests
for module in $(split <<< "${IPA_DISABLED_TESTS[@]}")
do
grep -qe "^None" <<< "${module}" && continue
IGNORE+=("$(find "${TESTDIR}" -name "${module}.yml" )")
done

echo "${IGNORE[@]}" | tr " " "\n" > /tmp/ignored_tests

# remove duplicates
read -r -a IGNORE <<< "$(echo ${IGNORE[@]} | tr " " "\n" | sort | uniq | tr "\n" " ")"

if [ -n "${IPA_ENABLED_MODULES:-}" ] || [ -n "${IPA_ENABLED_TESTS:-}" ]
then
# Get list of enabled modules
for module in $(split <<< "${IPA_ENABLED_MODULES[@]}")
do
grep -qe "^None" <<< "${module}" && continue
PLAYBOOKS+=($(find "${TESTDIR}/${module}" -name "test_*.yml" ))
done

# Get playbooks for all enabled tests
for module in $(split <<< "${IPA_ENABLED_TESTS[@]}")
do
grep -qe "^None" <<< "${module}" && continue
PLAYBOOKS+=("$(find "${TESTDIR}" -name "${module}.yml" )")
done
# remove duplicates
read -r -a PLAYBOOKS <<< "$(echo ${PLAYBOOKS[@]} | tr " " "\n" | sort | uniq | tr "\n" " ")"

else
echo "INFO: Running all tests."
PLAYBOOKS=($(find "${TESTDIR}" -name "test_*.yml" | tr "\n" " "))
fi

test_playbooks="$(grep -Fxvf /tmp/ignored_tests <(echo "${PLAYBOOKS[@]}" | tr " " "\n"))"
ansible-playbook -i ${INVENTORY} $test_playbooks

[ "${SHUTDOWN:-"NO"}" == "YES" ] && infra/scenarios/stop-scenario
89 changes: 89 additions & 0 deletions infra/scenarios/start-scenario
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/bin/bash -eu

die() {
echo "FATAL: $*" >&2
exit 1
}

check_dependencies() {
for dep in "$@"
do
command -v "$dep" >/dev/null || die "Required dependency missing: ${dep}"
done
}

usage() {
cat << EOF
usage: start-scenario [-h] [-D] CONFIG
Start an ansible-freeipa testing scenario described by CONFIG.
Options:
-h Display this help screen
-D Create configuration but don't start the environment
-d Select the default distro. One of: fedora-latest (default),
fedora-rawhide, c10s, c9s, c8s.
EOF
}


while getopts ":hDd:" option
do
case "${option}" in
h) usage && exit 0 ;;
D) DEPLOY="N" ;;
d) distro="-d ${OPTARG}" ;;
*) die "Invalid option: ${option}" ;;
esac
done

shift $((OPTIND - 1))
[ $# -eq 1 ] || die "A single scenario description must be provided."
SCENARIO="$(readlink -f "$1")"
shift 1

check_dependencies "ipalab-config" "podman-compose" "podman" "ansible-playbook"

TEST_SCENARIO="ansible-freeipa-scenario"

BASEDIR="$(readlink -f "$(dirname "$0")")"
TOPDIR="$(readlink -f "${BASEDIR}/../..")"
LABDIR="${TOPDIR}/${TEST_SCENARIO}"

# Check if a testing scenario already exists
pod="$(podman pod ps --filter "name=${TEST_SCENARIO}" --format "{{ .Name }}")"
[ -z "${pod}" ] || die "Testing scenario is running."

# Generate configuration
cd "${BASEDIR}"
rm -rf "${LABDIR}" 2>/dev/null | die "Could not remove old scenario. Check existing files permissions."
echo "Creating scenario configuration"
# shellcheck disable=SC2086
ipalab-config ${distro:-} -o "${LABDIR}" "$(readlink -f "$SCENARIO")"

[ "${DEPLOY:-"Y"}" == "Y" ] || exit 0

cd "${LABDIR}"

# Start compose
echo Starting compose
podman-compose up -d > /tmp/ansible-freeipa-scenario.log 2>&1

# Deploy external nodes
echo Deploying external nodes
ansible-playbook -i inventory.yml playbooks/deploy_*.yml

# Deploy IPA cluster
echo Deploying IPA cluster
ansible-playbook -i inventory.yml playbooks/install-cluster.yml

# Initial scenario configuration
echo Deploying scenario initial configuration
ansible-playbook -i inventory.yml playbooks/config_*.yml

# Print container names
echo -e "\n\nScenario containers:"
podman pod ps --filter "name=pod_${TEST_SCENARIO}" --format "{{ .ContainerNames }}" \
| sed "s/^/\t/;s/,/\n\t/g"
Loading

0 comments on commit 8028268

Please sign in to comment.