mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-07-30 19:34:45 +00:00
Merge pull request #1276 from t-woerner/run_containers_ubuntu_24_04_test
Run tests with podman and ubuntu 20.04
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ importer_result.json
|
|||||||
/.venv/
|
/.venv/
|
||||||
|
|
||||||
tests/logs/
|
tests/logs/
|
||||||
|
TEST*.xml
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ environment variable. For example:
|
|||||||
IPA_SSH_PASSWORD=<ipaserver_ssh_password> IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest
|
IPA_SSH_PASSWORD=<ipaserver_ssh_password> IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want, or need to to set the Python interpreter to use, you must set `IPA_PYTHON_PATH`
|
||||||
|
environment variable. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
IPA_PYTHON_PATH=/usr/bin/python3.14 IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest
|
||||||
|
```
|
||||||
|
|
||||||
To run a single test use the full path with the following format:
|
To run a single test use the full path with the following format:
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ stages:
|
|||||||
- template: templates/fast_tests.yml
|
- template: templates/fast_tests.yml
|
||||||
parameters:
|
parameters:
|
||||||
build_number: $(Build.BuildNumber)
|
build_number: $(Build.BuildNumber)
|
||||||
scenario: fedora-latest
|
distro: fedora-latest
|
||||||
ansible_version: "-core >=2.14,<2.15"
|
ansible_version: "-core >=2.15,<2.16"
|
||||||
|
|
||||||
# Galaxy on Fedora
|
# Galaxy on Fedora
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@ stages:
|
|||||||
- template: templates/fast_tests.yml
|
- template: templates/fast_tests.yml
|
||||||
parameters:
|
parameters:
|
||||||
build_number: $(Build.BuildNumber)
|
build_number: $(Build.BuildNumber)
|
||||||
scenario: fedora-latest
|
distro: fedora-latest
|
||||||
ansible_version: "-core >=2.14,<2.15"
|
ansible_version: "-core >=2.15,<2.16"
|
||||||
|
|
||||||
# CentOS 9 Stream
|
# CentOS 9 Stream
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ stages:
|
|||||||
- template: templates/fast_tests.yml
|
- template: templates/fast_tests.yml
|
||||||
parameters:
|
parameters:
|
||||||
build_number: $(Build.BuildNumber)
|
build_number: $(Build.BuildNumber)
|
||||||
scenario: c9s
|
distro: c9s
|
||||||
ansible_version: "-core >=2.14,<2.15"
|
ansible_version: "-core >=2.15,<2.16"
|
||||||
|
|
||||||
# CentOS 8 Stream
|
# CentOS 8 Stream
|
||||||
|
|
||||||
@@ -48,19 +48,23 @@ stages:
|
|||||||
- template: templates/fast_tests.yml
|
- template: templates/fast_tests.yml
|
||||||
parameters:
|
parameters:
|
||||||
build_number: $(Build.BuildNumber)
|
build_number: $(Build.BuildNumber)
|
||||||
scenario: c8s
|
distro: c8s
|
||||||
ansible_version: "-core >=2.14,<2.15"
|
ansible_version: "-core >=2.15,<2.16"
|
||||||
|
target_python: "/usr/libexec/platform-python"
|
||||||
|
|
||||||
|
# CentOS 7 cannot be used with current systemd
|
||||||
|
#
|
||||||
# CentOS 7
|
# CentOS 7
|
||||||
|
#
|
||||||
- stage: CentOS_7
|
# - stage: CentOS_7
|
||||||
dependsOn: []
|
# dependsOn: []
|
||||||
jobs:
|
# jobs:
|
||||||
- template: templates/fast_tests.yml
|
# - template: templates/fast_tests.yml
|
||||||
parameters:
|
# parameters:
|
||||||
build_number: $(Build.BuildNumber)
|
# build_number: $(Build.BuildNumber)
|
||||||
scenario: centos-7
|
# distro: centos-7
|
||||||
ansible_version: "-core >=2.14,<2.15"
|
# ansible_version: "-core >=2.15,<2.16"
|
||||||
|
# target_python: "/usr/bin/python2"
|
||||||
|
|
||||||
# Rawhide
|
# Rawhide
|
||||||
|
|
||||||
@@ -70,5 +74,5 @@ stages:
|
|||||||
- template: templates/fast_tests.yml
|
- template: templates/fast_tests.yml
|
||||||
parameters:
|
parameters:
|
||||||
build_number: $(Build.BuildNumber)
|
build_number: $(Build.BuildNumber)
|
||||||
scenario: fedora-rawhide
|
distro: fedora-rawhide
|
||||||
ansible_version: "-core >=2.14,<2.15"
|
ansible_version: "-core >=2.15,<2.16"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
parameters:
|
parameters:
|
||||||
- name: scenario
|
- name: distro
|
||||||
type: string
|
type: string
|
||||||
default: fedora-latest
|
default: fedora-latest
|
||||||
- name: build_number
|
- name: build_number
|
||||||
@@ -8,6 +8,9 @@ parameters:
|
|||||||
- name: ansible_version
|
- name: ansible_version
|
||||||
type: string
|
type: string
|
||||||
default: ""
|
default: ""
|
||||||
|
- name: target_python
|
||||||
|
type: string
|
||||||
|
default: "/usr/bin/python3"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- template: playbook_fast.yml
|
- template: playbook_fast.yml
|
||||||
@@ -15,13 +18,14 @@ jobs:
|
|||||||
group_number: 1
|
group_number: 1
|
||||||
number_of_groups: 1
|
number_of_groups: 1
|
||||||
build_number: ${{ parameters.build_number }}
|
build_number: ${{ parameters.build_number }}
|
||||||
scenario: ${{ parameters.scenario }}
|
distro: ${{ parameters.distro }}
|
||||||
ansible_version: ${{ parameters.ansible_version }}
|
ansible_version: ${{ parameters.ansible_version }}
|
||||||
python_version: '< 3.12'
|
python_version: '< 3.12'
|
||||||
|
target_python: ${{ parameters.target_python }}
|
||||||
|
|
||||||
# - template: pytest_tests.yml
|
# - template: pytest_tests.yml
|
||||||
# parameters:
|
# parameters:
|
||||||
# build_number: ${{ parameters.build_number }}
|
# build_number: ${{ parameters.build_number }}
|
||||||
# scenario: ${{ parameters.scenario }}
|
# distro: ${{ parameters.distro }}
|
||||||
# ansible_version: ${{ parameters.ansible_version }}
|
# ansible_version: ${{ parameters.ansible_version }}
|
||||||
# python_version: '< 3.12'
|
# python_version: '< 3.12'
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ parameters:
|
|||||||
- name: number_of_groups
|
- name: number_of_groups
|
||||||
type: number
|
type: number
|
||||||
default: 1
|
default: 1
|
||||||
- name: scenario
|
- name: distro
|
||||||
type: string
|
type: string
|
||||||
default: fedora-latest
|
default: fedora-latest
|
||||||
- name: ansible_version
|
- name: ansible_version
|
||||||
@@ -17,28 +17,28 @@ parameters:
|
|||||||
default: 3.x
|
default: 3.x
|
||||||
- name: build_number
|
- name: build_number
|
||||||
type: string
|
type: string
|
||||||
|
- name: target_python
|
||||||
|
type: string
|
||||||
|
default: "/usr/bin/python3"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Test_Group${{ parameters.group_number }}
|
- job: Test_Group${{ parameters.group_number }}
|
||||||
displayName: Run playbook tests ${{ parameters.scenario }} (${{ parameters.group_number }}/${{ parameters.number_of_groups }})
|
displayName: Run playbook tests ${{ parameters.distro }} (${{ parameters.group_number }}/${{ parameters.number_of_groups }})
|
||||||
timeoutInMinutes: 360
|
timeoutInMinutes: 360
|
||||||
variables:
|
variables:
|
||||||
- template: variables.yaml
|
- template: variables.yaml
|
||||||
- template: variables_${{ parameters.scenario }}.yaml
|
- template: variables_${{ parameters.distro }}.yaml
|
||||||
steps:
|
steps:
|
||||||
- task: UsePythonVersion@0
|
- task: UsePythonVersion@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '${{ parameters.python_version }}'
|
versionSpec: '${{ parameters.python_version }}'
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
pip install \
|
pip install "ansible${{ parameters.ansible_version }}"
|
||||||
"molecule-plugins[docker]" \
|
|
||||||
"requests<2.29" \
|
|
||||||
"ansible${{ parameters.ansible_version }}"
|
|
||||||
retryCountOnTaskFailure: 5
|
retryCountOnTaskFailure: 5
|
||||||
displayName: Install molecule and Ansible
|
displayName: Install Ansible
|
||||||
|
|
||||||
- script: ansible-galaxy collection install community.docker ansible.posix
|
- script: ansible-galaxy collection install containers.podman
|
||||||
retryCountOnTaskFailure: 5
|
retryCountOnTaskFailure: 5
|
||||||
displayName: Install Ansible collections
|
displayName: Install Ansible collections
|
||||||
|
|
||||||
@@ -47,43 +47,35 @@ jobs:
|
|||||||
displayName: Install dependencies
|
displayName: Install dependencies
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
rm -rf ~/ansible
|
. utils/set_test_modules
|
||||||
mkdir -p ~/.ansible/roles ~/.ansible/library ~/.ansible/module_utils
|
python3 utils/check_test_configuration.py ${{ parameters.distro }}
|
||||||
cp -a roles/* ~/.ansible/roles
|
displayName: Check test configuration
|
||||||
cp -a plugins/modules/* ~/.ansible/library
|
|
||||||
cp -a plugins/module_utils/* ~/.ansible/module_utils
|
- script: |
|
||||||
molecule create -s ${{ parameters.scenario }}
|
utils/setup_test_container.sh \
|
||||||
retryCountOnTaskFailure: 5
|
-e podman \
|
||||||
displayName: Setup test container
|
-a \
|
||||||
env:
|
-m 4 \
|
||||||
ANSIBLE_LIBRARY: ./molecule
|
-n "ipaserver.test.local" \
|
||||||
|
-p ${{ parameters.target_python }} \
|
||||||
|
-i ${{ parameters.distro }}-server \
|
||||||
|
${{ parameters.distro }}-test
|
||||||
|
displayName: Setup target container
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
. utils/set_test_modules
|
. utils/set_test_modules
|
||||||
python utils/check_test_configuration.py ${{ parameters.scenario }}
|
pytest -m "playbook" --verbose --color=yes --suppress-no-test-exit-code --junit-xml=TEST-results-pr-check.xml
|
||||||
displayName: Check scenario test configuration
|
|
||||||
|
|
||||||
- script: |
|
|
||||||
. utils/set_test_modules
|
|
||||||
if ! pytest \
|
|
||||||
-m "playbook" \
|
|
||||||
--verbose \
|
|
||||||
--color=yes \
|
|
||||||
--suppress-no-test-exit-code \
|
|
||||||
--splits=${{ parameters.number_of_groups }} \
|
|
||||||
--group=${{ parameters.group_number }} \
|
|
||||||
--randomly-seed=$(date "+%Y%m%d") \
|
|
||||||
--junit-xml=TEST-results-group-${{ parameters.group_number }}.xml
|
|
||||||
then
|
|
||||||
[ $? -eq 5 ] && true || false
|
|
||||||
fi
|
|
||||||
displayName: Run playbook tests
|
displayName: Run playbook tests
|
||||||
env:
|
env:
|
||||||
IPA_SERVER_HOST: ${{ parameters.scenario }}
|
ANSIBLE_ROLES_PATH: "${PWD}/roles"
|
||||||
RUN_TESTS_IN_DOCKER: true
|
ANSIBLE_LIBRARY: "${PWD}/plugins"
|
||||||
|
ANSIBLE_MODULE_UTILS: "${PWD}/plugins/module_utils"
|
||||||
|
IPA_SERVER_HOST: ${{ parameters.distro }}-test
|
||||||
|
RUN_TESTS_IN_DOCKER: podman
|
||||||
IPA_DISABLED_MODULES: ${{ variables.ipa_disabled_modules }}
|
IPA_DISABLED_MODULES: ${{ variables.ipa_disabled_modules }}
|
||||||
IPA_DISABLED_TESTS: ${{ variables.ipa_disabled_tests }}
|
IPA_DISABLED_TESTS: ${{ variables.ipa_disabled_tests }}
|
||||||
IPA_VERBOSITY: "-vvv"
|
IPA_VERBOSITY: "-vvv"
|
||||||
|
IPA_PYTHON_PATH: ${{ parameters.target_python }}
|
||||||
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
127
tests/utils.py
127
tests/utils.py
@@ -43,6 +43,10 @@ def get_ssh_password():
|
|||||||
return os.getenv("IPA_SSH_PASSWORD")
|
return os.getenv("IPA_SSH_PASSWORD")
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_interpreter():
|
||||||
|
return os.getenv("IPA_PYTHON_PATH")
|
||||||
|
|
||||||
|
|
||||||
def get_server_host():
|
def get_server_host():
|
||||||
return os.getenv("IPA_SERVER_HOST")
|
return os.getenv("IPA_SERVER_HOST")
|
||||||
|
|
||||||
@@ -97,6 +101,12 @@ def get_inventory_content():
|
|||||||
if sshpass:
|
if sshpass:
|
||||||
ipa_server_host += " ansible_ssh_pass=%s" % sshpass
|
ipa_server_host += " ansible_ssh_pass=%s" % sshpass
|
||||||
|
|
||||||
|
python_interpreter = get_python_interpreter()
|
||||||
|
if python_interpreter:
|
||||||
|
ipa_server_host += (
|
||||||
|
" ansible_python_interpreter=%s" % python_interpreter
|
||||||
|
)
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
"[ipaserver]",
|
"[ipaserver]",
|
||||||
ipa_server_host,
|
ipa_server_host,
|
||||||
@@ -138,30 +148,6 @@ def write_logs(result, test_name):
|
|||||||
log_file.write(result.stderr.decode("utf-8"))
|
log_file.write(result.stderr.decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
def _run_playbook(playbook):
|
|
||||||
"""
|
|
||||||
Create a inventory using a temporary file and run ansible using it.
|
|
||||||
|
|
||||||
The logs of the run will be placed in `tests/logs/`.
|
|
||||||
"""
|
|
||||||
with tempfile.NamedTemporaryFile() as inventory_file:
|
|
||||||
inventory_file.write(get_inventory_content())
|
|
||||||
inventory_file.flush()
|
|
||||||
cmd_options = ["-i", inventory_file.name]
|
|
||||||
verbose = os.environ.get("IPA_VERBOSITY", None)
|
|
||||||
if verbose is not None:
|
|
||||||
cmd_options.append(verbose)
|
|
||||||
cmd = ["ansible-playbook"] + cmd_options + [playbook]
|
|
||||||
# pylint: disable=subprocess-run-check
|
|
||||||
process = subprocess.run(
|
|
||||||
cmd, cwd=SCRIPT_DIR, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
test_name = get_test_name_from_playbook_path(playbook)
|
|
||||||
write_logs(process, test_name)
|
|
||||||
|
|
||||||
return process
|
|
||||||
|
|
||||||
|
|
||||||
def _truncate(lines, charcount, minlines=0):
|
def _truncate(lines, charcount, minlines=0):
|
||||||
output = ""
|
output = ""
|
||||||
line_count = 1
|
line_count = 1
|
||||||
@@ -178,12 +164,11 @@ def _truncate(lines, charcount, minlines=0):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def run_playbook(playbook, allow_failures=False):
|
def _run_playbook(playbook):
|
||||||
"""
|
"""
|
||||||
Run an Ansible playbook and assert the return code.
|
Create a inventory using a temporary file and run ansible using it.
|
||||||
|
|
||||||
Call ansible (using _run_playbook function) and assert the result of
|
The logs of the run will be placed in `tests/logs/`.
|
||||||
the execution.
|
|
||||||
|
|
||||||
In case of failure the tail of the error message will be displayed
|
In case of failure the tail of the error message will be displayed
|
||||||
as an assertion message.
|
as an assertion message.
|
||||||
@@ -191,43 +176,65 @@ def run_playbook(playbook, allow_failures=False):
|
|||||||
The full log of the execution will be available in the directory
|
The full log of the execution will be available in the directory
|
||||||
`tests/logs/`.
|
`tests/logs/`.
|
||||||
"""
|
"""
|
||||||
result = _run_playbook(playbook)
|
with tempfile.NamedTemporaryFile() as inventory_file:
|
||||||
|
inventory_file.write(get_inventory_content())
|
||||||
|
inventory_file.flush()
|
||||||
|
cmd_options = ["-i", inventory_file.name]
|
||||||
|
verbose = os.environ.get("IPA_VERBOSITY", None)
|
||||||
|
if verbose is not None:
|
||||||
|
cmd_options.append(verbose)
|
||||||
|
cmd = ["ansible-playbook"] + cmd_options + [playbook]
|
||||||
|
process = subprocess.run(
|
||||||
|
cmd, cwd=SCRIPT_DIR, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, check=False
|
||||||
|
)
|
||||||
|
test_name = get_test_name_from_playbook_path(playbook)
|
||||||
|
write_logs(process, test_name)
|
||||||
|
|
||||||
if allow_failures:
|
msg = ""
|
||||||
return result
|
if process.returncode != 0:
|
||||||
|
status_code_msg = "ansible-playbook return code: {0}".format(
|
||||||
|
process.returncode
|
||||||
|
)
|
||||||
|
|
||||||
status_code_msg = "ansible-playbook return code: {0}".format(
|
_stdout = process.stdout.decode("utf8")
|
||||||
result.returncode
|
_stderr = process.stderr.decode("utf8")
|
||||||
)
|
# Truncate stdout and stderr in the way that it hopefully
|
||||||
_stdout = result.stdout.decode("utf8")
|
# shows all important information. At least 15 lines of stdout
|
||||||
_stderr = result.stderr.decode("utf8")
|
# (Ansible tasks) and remaining from stderr to fill up to
|
||||||
# Truncate stdout and stderr in the way that it hopefully
|
# maxlen size.
|
||||||
# shows all important information. At least 15 lines of stdout
|
maxlen = 2000
|
||||||
# (Ansible tasks) and remaining from stderr to fill up to
|
factor = maxlen / (len(_stdout) + len(_stderr))
|
||||||
# maxlen size.
|
stdout = _truncate(_stdout.splitlines(),
|
||||||
maxlen = 2000
|
int(factor * len(_stdout)),
|
||||||
factor = maxlen / (len(_stdout) + len(_stderr))
|
minlines=15)
|
||||||
stdout = _truncate(_stdout.splitlines(),
|
stderr = _truncate(_stderr.splitlines(), maxlen - len(stdout))
|
||||||
int(factor * len(_stdout)),
|
|
||||||
minlines=15)
|
|
||||||
stderr = _truncate(_stderr.splitlines(), maxlen - len(stdout))
|
|
||||||
|
|
||||||
assert_msg = "\n".join(
|
msg = "\n".join(
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"-" * 30 + " Captured stdout " + "-" * 30,
|
"-" * 30 + " Captured stdout " + "-" * 30,
|
||||||
stdout,
|
stdout,
|
||||||
"-" * 30 + " Captured stderr " + "-" * 30,
|
"-" * 30 + " Captured stderr " + "-" * 30,
|
||||||
stderr,
|
stderr
|
||||||
"-" * 30 + " Playbook Return Code " + "-" * 30,
|
]
|
||||||
status_code_msg,
|
)
|
||||||
]
|
msg += "-" * 30 + " Playbook Return Code " + "-" * 30 + "\n"
|
||||||
)
|
msg += status_code_msg
|
||||||
|
|
||||||
# Need to get the last bytes of msg otherwise Azure
|
return process, msg
|
||||||
# will cut it out.
|
|
||||||
assert result.returncode == 0, assert_msg[-2500:]
|
|
||||||
|
|
||||||
|
|
||||||
|
def run_playbook(playbook, allow_failures=False):
|
||||||
|
"""
|
||||||
|
Run an Ansible playbook and assert the return code.
|
||||||
|
|
||||||
|
Call ansible (using _run_playbook function) and assert the result of
|
||||||
|
the execution.
|
||||||
|
"""
|
||||||
|
result, assert_msg = _run_playbook(playbook)
|
||||||
|
if not allow_failures:
|
||||||
|
assert result.returncode == 0, assert_msg
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,19 @@
|
|||||||
#!/bin/bash -eu
|
#!/bin/bash -eu
|
||||||
|
|
||||||
trap interrupt_exception SIGINT
|
SCRIPTDIR="$(readlink -f "$(dirname "$0")")"
|
||||||
|
TOPDIR="$(readlink -f "${SCRIPTDIR}/..")"
|
||||||
|
|
||||||
RST="\033[0m"
|
# shellcheck source=utils/shfun
|
||||||
RED="\033[31m"
|
. "${SCRIPTDIR}/shfun"
|
||||||
# BRIGHTRED="\033[31;1m"
|
# shellcheck source=utils/shcontainer
|
||||||
# GREEN="\033[32m"
|
. "${SCRIPTDIR}/shcontainer"
|
||||||
BRIGHTGREEN="\033[32;1m"
|
# shellcheck source=utils/shansible
|
||||||
# BROWN="\033[33m"
|
. "${SCRIPTDIR}/shansible"
|
||||||
YELLOW="\033[33;1m"
|
|
||||||
# NAVY="\033[34m"
|
|
||||||
BLUE="\033[34;1m"
|
|
||||||
# MAGENTA="\033[35m"
|
|
||||||
# BRIGHTMAGENTA="\033[35;1m"
|
|
||||||
# DARKCYAN="\033[36m"
|
|
||||||
# CYAN="\033[36;1m"
|
|
||||||
# BLACK="\033[30m"
|
|
||||||
# DARKGRAY="\033[30;1m"
|
|
||||||
# GRAY="\033[37m"
|
|
||||||
WHITE="\033[37;1m"
|
|
||||||
|
|
||||||
TOPDIR="$(readlink -f "$(dirname "$0")/..")"
|
|
||||||
|
|
||||||
interrupt_exception() {
|
|
||||||
trap - SIGINT
|
|
||||||
log warn "User interrupted test execution."
|
|
||||||
cleanup
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
local prog="${0##*/}"
|
local prog="${0##*/}"
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
usage: ${prog} [-h] [-l] [-e] [-K] [-c CONTAINER] [-s TESTS_SUITE] [-x] [-A SEED.GRP] [-i IMAGE] [-m MEMORY] [-v...] [TEST...]
|
usage: ${prog} [-h] [-l] [-e] [-K] [-A|-a ANSIBLE] [-p INTERPRETER] [-c CONTAINER] [-s TESTS_SUITE] [-x] [-S SEED.GRP] [-i IMAGE] [-m MEMORY] [-v...] [TEST...]
|
||||||
${prog} runs playbook(s) TEST using an ansible-freeipa testing image.
|
${prog} runs playbook(s) TEST using an ansible-freeipa testing image.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
@@ -47,172 +28,72 @@ positional arguments:
|
|||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h display this message and exit
|
-h display this message and exit
|
||||||
|
-a ANSIBLE Ansible version to use, e.g. "ansible-core==2.16.0"
|
||||||
|
(default: latest ansible-core for the python version)
|
||||||
|
-A Do not install Ansible, use host's provided one.
|
||||||
-c CONTAINER use container CONTAINER to run tests
|
-c CONTAINER use container CONTAINER to run tests
|
||||||
-K keep container, even if tests succeed
|
-K keep container, even if tests succeed
|
||||||
-l list available images
|
-l list available images
|
||||||
-e force recreation of the virtual environment
|
-e force recreation of the virtual environment
|
||||||
-i IMAGE select image to run the tests (default: fedora-latest)
|
-i IMAGE select image to run the tests (default: fedora-latest)
|
||||||
-m container memory, in GiB (default: 3)
|
-m MEMORY container memory, in GiB (default: 3)
|
||||||
|
-p INTERPRETER Python interpreter to use on target container
|
||||||
-s TEST_SUITE run all playbooks for test suite, which is a directory
|
-s TEST_SUITE run all playbooks for test suite, which is a directory
|
||||||
under ${WHITE}tests${RST}
|
under ${WHITE}tests${RST}
|
||||||
-A SEED.GROUP Replicate Azure's test group and seed (seed is YYYYMMDD)
|
-S SEED.GROUP Replicate Azure's test group and seed (seed is YYYYMMDD)
|
||||||
-v Increase Ansible verbosity (can be used multiple times)
|
-v Increase Ansible verbosity (can be used multiple times)
|
||||||
-x Stop on first error.
|
-x Stop on first error.
|
||||||
EOF
|
EOF
|
||||||
)"
|
)"
|
||||||
}
|
}
|
||||||
|
|
||||||
log() {
|
|
||||||
local level="${1^^}" message="${*:2}"
|
|
||||||
case "${level}" in
|
|
||||||
ERROR) COLOR="${RED}" ;;
|
|
||||||
WARN) COLOR="${YELLOW}" ;;
|
|
||||||
DEBUG) COLOR="${BLUE}" ;;
|
|
||||||
INFO) COLOR="${WHITE}" ;;
|
|
||||||
SUCCESS) COLOR="${BRIGHTGREEN}" ;;
|
|
||||||
*) COLOR="${RST}" ;;
|
|
||||||
esac
|
|
||||||
echo -en "${COLOR}"
|
|
||||||
[ "${level}" == "ERROR" ] && echo -en "${level}:"
|
|
||||||
echo -e "${message}${RST}"
|
|
||||||
}
|
|
||||||
|
|
||||||
quiet() {
|
|
||||||
"$@" >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
in_python_virtualenv() {
|
|
||||||
local script
|
|
||||||
read -r -d "" script <<EOS
|
|
||||||
import sys;
|
|
||||||
base = getattr(sys, "base_prefix", ) or getattr(sys, "real_prefix", ) or sys.prefix
|
|
||||||
print('yes' if sys.prefix != base else 'no')
|
|
||||||
EOS
|
|
||||||
test "$(python -c "${script}")" == "yes"
|
|
||||||
}
|
|
||||||
|
|
||||||
run_inline_playbook() {
|
|
||||||
local playbook
|
|
||||||
local err
|
|
||||||
quiet mkdir -p "${test_env}/playbooks"
|
|
||||||
playbook=$(mktemp "${test_env}/playbooks/ansible-freeipa-test-playbook_ipa.XXXXXXXX")
|
|
||||||
cat - >"${playbook}"
|
|
||||||
ansible-playbook -i "${inventory}" "${playbook}"
|
|
||||||
err=$?
|
|
||||||
rm "${playbook}"
|
|
||||||
return ${err}
|
|
||||||
}
|
|
||||||
|
|
||||||
die() {
|
|
||||||
usg="N"
|
|
||||||
if [ "${1}" == "-u" ]
|
|
||||||
then
|
|
||||||
usg="Y"
|
|
||||||
shift 1
|
|
||||||
fi
|
|
||||||
log error "${*}"
|
|
||||||
STOP_CONTAINER="N"
|
|
||||||
cleanup
|
|
||||||
[ "${usg}" == "Y" ] && usage
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
make_inventory() {
|
|
||||||
local scenario=$1 engine=${2:-podman}
|
|
||||||
inventory="${test_env}/inventory"
|
|
||||||
log info "Inventory file: ${inventory}"
|
|
||||||
cat << EOF > "${inventory}"
|
|
||||||
[ipaserver]
|
|
||||||
${scenario} ansible_connection=${engine}
|
|
||||||
[ipaserver:vars]
|
|
||||||
ipaserver_domain = test.local
|
|
||||||
ipaserver_realm = TEST.LOCAL
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_container() {
|
|
||||||
local scenario=${1} engine=${2:-podman}
|
|
||||||
echo "Stopping container..."
|
|
||||||
quiet "${engine}" stop "${scenario}"
|
|
||||||
echo "Removing container..."
|
|
||||||
quiet "${engine}" rm "${scenario}"
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
if [ $# -gt 0 ]
|
|
||||||
then
|
|
||||||
if [ "${STOP_CONTAINER}" != "N" ]
|
|
||||||
then
|
|
||||||
stop_container "${1}" "${2}"
|
|
||||||
rm "${inventory}"
|
|
||||||
else
|
|
||||||
log info "Keeping container: $(podman ps --format "{{.Names}} - {{.ID}}" --filter "name=${1}")"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ "${STOP_VIRTUALENV}" == "Y" ]
|
|
||||||
then
|
|
||||||
echo "Deactivating virtual environment"
|
|
||||||
deactivate
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
list_images() {
|
|
||||||
local quay_api="https://quay.io/api/v1/repository/ansible-freeipa/upstream-tests/tag"
|
|
||||||
echo -e "${WHITE}Available images:"
|
|
||||||
curl --silent -L "${quay_api}" | jq '.tags[]|.name' | tr -d '"'| sort | uniq | sed "s/.*/ &/"
|
|
||||||
echo -e "${RST}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
|
|
||||||
ANSIBLE_VERSION=${ANSIBLE_VERSION:-'ansible-core'}
|
|
||||||
verbose=""
|
verbose=""
|
||||||
FORCE_ENV="N"
|
engine="${engine:-"podman"}"
|
||||||
CONTINUE_ON_ERROR=""
|
CONTINUE_ON_ERROR=""
|
||||||
STOP_CONTAINER="Y"
|
STOP_CONTAINER="Y"
|
||||||
STOP_VIRTUALENV="N"
|
STOP_VIRTUALENV="N"
|
||||||
declare -a ENABLED_MODULES
|
declare -a ENABLED_MODULES
|
||||||
declare -a ENABLED_TESTS
|
declare -a ENABLED_TESTS
|
||||||
ENABLED_MODULES=()
|
read -r -a ENABLED_MODULES <<< "${IPA_ENABLED_MODULES:-""}"
|
||||||
ENABLED_TESTS=()
|
read -r -a ENABLED_TESTS <<< "${IPA_ENABLED_MODULES:-""}"
|
||||||
test_env="${TESTENV_DIR:-${VIRTUAL_ENV:-/tmp/ansible-freeipa-tests}}"
|
|
||||||
engine="podman"
|
|
||||||
IMAGE_REPO="quay.io/ansible-freeipa/upstream-tests"
|
|
||||||
IMAGE_TAG="fedora-latest"
|
IMAGE_TAG="fedora-latest"
|
||||||
scenario=""
|
scenario="freeipa-tests"
|
||||||
MEMORY=3
|
MEMORY=3
|
||||||
hostname="ipaserver.test.local"
|
IPA_HOSTNAME="ipaserver.test.local"
|
||||||
SEED=""
|
SEED="$(date "+%Y%m%d")"
|
||||||
GROUP=0
|
GROUP=1
|
||||||
SPLITS=0
|
SPLITS=0
|
||||||
ANSIBLE_COLLECTIONS=${ANSIBLE_COLLECTIONS:-"containers.podman"}
|
ANSIBLE_COLLECTIONS=${ANSIBLE_COLLECTIONS:-"${engine_collection}"}
|
||||||
|
SKIP_ANSIBLE=""
|
||||||
|
ansible_interpreter="/usr/bin/python3"
|
||||||
EXTRA_OPTIONS=""
|
EXTRA_OPTIONS=""
|
||||||
|
unset ANSIBLE_VERSION
|
||||||
|
|
||||||
# Process command options
|
# Process command options
|
||||||
|
|
||||||
while getopts ":hA:c:ei:Klms:vx" option
|
while getopts ":ha:Ac:ei:Klm:p:s:S:vx" option
|
||||||
do
|
do
|
||||||
case "$option" in
|
case "$option" in
|
||||||
h) help && exit 0 ;;
|
h) help && exit 0 ;;
|
||||||
A)
|
A)
|
||||||
[ ${#ENABLED_MODULES[@]} -eq 0 ] || die -u "Can't use '-A' with '-s'"
|
[ -n "${ANSIBLE_VERSION:-""}" ] && die "Can't use -A with '-a'"
|
||||||
SEED="$(cut -d. -f1 <<< "${OPTARG}" | tr -d "-")"
|
SKIP_ANSIBLE="YES"
|
||||||
GROUP="$(cut -d. -f2 <<< "${OPTARG}")"
|
;;
|
||||||
if [ -z "${SEED}" ] || [ -z "${GROUP}" ]
|
a)
|
||||||
then
|
[ "${SKIP_ANSIBLE:-"no"}" == "YES" ] && die "Can't use -A with '-a'"
|
||||||
die -u "Seed for '-A' must have the format YYYYMMDD.N"
|
ANSIBLE_VERSION="${OPTARG}"
|
||||||
fi
|
;;
|
||||||
SPLITS=3
|
|
||||||
;;
|
|
||||||
c) scenario="${OPTARG}" ;;
|
c) scenario="${OPTARG}" ;;
|
||||||
e) FORCE_ENV="Y" ;;
|
e) FORCE_ENV="Y" ;;
|
||||||
i) IMAGE_TAG="${OPTARG}" ;;
|
i) IMAGE_TAG="${OPTARG}" ;;
|
||||||
K) STOP_CONTAINER="N" ;;
|
K) STOP_CONTAINER="N" ;;
|
||||||
l) list_images && exit 0 || exit 1;;
|
l) "${SCRIPTDIR}"/setup_test_container.sh -l && exit 0 || exit 1 ;;
|
||||||
m) MEMORY="${OPTARG}" ;;
|
m) MEMORY="${OPTARG}" ;;
|
||||||
|
p) ansible_interpreter="${OPTARG}" ;;
|
||||||
s)
|
s)
|
||||||
[ ${SPLITS} -ne 0 ] && die -u "Can't use '-A' with '-s'"
|
[ ${SPLITS} -ne 0 ] && die -u "Can't use '-S' with '-s'"
|
||||||
if [ -d "${TOPDIR}/tests/${OPTARG}" ]
|
if [ -d "${TOPDIR}/tests/${OPTARG}" ]
|
||||||
then
|
then
|
||||||
ENABLED_MODULES+=("${OPTARG}")
|
ENABLED_MODULES+=("${OPTARG}")
|
||||||
@@ -220,6 +101,16 @@ do
|
|||||||
log error "Invalid suite: ${OPTARG}"
|
log error "Invalid suite: ${OPTARG}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
S)
|
||||||
|
[ ${#ENABLED_MODULES[@]} -eq 0 ] || die -u "Can't use '-A' with '-s'"
|
||||||
|
SEED="$(cut -d. -f1 <<< "${OPTARG}" | tr -d "-")"
|
||||||
|
GROUP="$(cut -d. -f2 <<< "${OPTARG}")"
|
||||||
|
if [ -z "${SEED}" ] || [ -z "${GROUP}" ]
|
||||||
|
then
|
||||||
|
die -u "Seed for '-A' must have the format YYYYMMDD.N"
|
||||||
|
fi
|
||||||
|
SPLITS=3
|
||||||
|
;;
|
||||||
v) verbose=${verbose:--}${option} ;;
|
v) verbose=${verbose:--}${option} ;;
|
||||||
x) EXTRA_OPTIONS="$EXTRA_OPTIONS --exitfirst" ;;
|
x) EXTRA_OPTIONS="$EXTRA_OPTIONS --exitfirst" ;;
|
||||||
*) die -u "Invalid option: ${OPTARG}" ;;
|
*) die -u "Invalid option: ${OPTARG}" ;;
|
||||||
@@ -240,155 +131,28 @@ done
|
|||||||
|
|
||||||
[ ${SPLITS} -eq 0 ] && [ ${#ENABLED_MODULES[@]} -eq 0 ] && [ ${#ENABLED_TESTS[@]} -eq 0 ] && die -u "No test defined."
|
[ ${SPLITS} -eq 0 ] && [ ${#ENABLED_MODULES[@]} -eq 0 ] && [ ${#ENABLED_TESTS[@]} -eq 0 ] && die -u "No test defined."
|
||||||
|
|
||||||
|
export STOP_CONTAINER FORCE_ENV STOP_VIRTUALENV ansible_interpreter
|
||||||
|
|
||||||
|
# Ensure $python is set
|
||||||
|
[ -z "${python}" ] && python="python3"
|
||||||
|
|
||||||
|
log info "Controller Python executable: ${python}"
|
||||||
|
${python} --version
|
||||||
|
|
||||||
# Prepare virtual environment
|
# Prepare virtual environment
|
||||||
VENV=$(in_python_virtualenv && echo Y || echo N)
|
start_virtual_environment
|
||||||
|
log info "Installing dependencies from 'requirements-tests.txt'"
|
||||||
|
pip install --upgrade -r "${TOPDIR}/requirements-tests.txt"
|
||||||
|
|
||||||
if [ "${FORCE_ENV}" == "Y" ]
|
[ -z "${SKIP_ANSIBLE}" ] && install_ansible "${ANSIBLE_VERSION:-"ansible-core"}"
|
||||||
then
|
|
||||||
[ "${VENV}" == "Y" ] && deactivate
|
|
||||||
VENV="N"
|
|
||||||
rm -rf "$test_env"
|
|
||||||
log info "Virtual environment will be (re)created."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$VENV" == "N" ]
|
|
||||||
then
|
|
||||||
log info "Preparing virtual environment: ${test_env}"
|
|
||||||
if [ ! -d "${test_env}" ]
|
|
||||||
then
|
|
||||||
log info "Creating virtual environment: ${test_env}..."
|
|
||||||
if ! python3 -m venv "${test_env}"
|
|
||||||
then
|
|
||||||
die "Cannot create virtual environment."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ -f "${test_env}/bin/activate" ]
|
|
||||||
then
|
|
||||||
log info "Starting virtual environment: ${test_env}"
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
. "${test_env}/bin/activate" || die "Cannot activate environment."
|
|
||||||
STOP_VIRTUALENV="Y"
|
|
||||||
else
|
|
||||||
die "Cannot activate environment."
|
|
||||||
fi
|
|
||||||
log info "Installing required tools."
|
|
||||||
log none "Upgrading: pip setuptools wheel"
|
|
||||||
pip install --quiet --upgrade pip setuptools wheel
|
|
||||||
log info "Installing dependencies from 'requirements-tests.txt'"
|
|
||||||
pip install --quiet -r "${TOPDIR}/requirements-tests.txt"
|
|
||||||
log info "Installing Ansible: ${ANSIBLE_VERSION}"
|
|
||||||
pip install --quiet "${ANSIBLE_VERSION}"
|
|
||||||
log debug "Ansible version: $(ansible --version | sed -n "1p")${RST}"
|
|
||||||
else
|
|
||||||
log info "Using current virtual environment."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${ANSIBLE_COLLECTIONS}" ]
|
|
||||||
then
|
|
||||||
log warn "Installed collections will not be removed after execution."
|
|
||||||
log none "Installing: Ansible Collection ${ANSIBLE_COLLECTIONS}"
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
quiet ansible-galaxy collection install ${ANSIBLE_COLLECTIONS} || die "Failed to install Ansible collections."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ansible configuration
|
# Ansible configuration
|
||||||
export ANSIBLE_ROLES_PATH="${TOPDIR}/roles"
|
export ANSIBLE_ROLES_PATH="${TOPDIR}/roles"
|
||||||
export ANSIBLE_LIBRARY="${TOPDIR}/plugins:${TOPDIR}/molecule"
|
export ANSIBLE_LIBRARY="${TOPDIR}/plugins"
|
||||||
export ANSIBLE_MODULE_UTILS="${TOPDIR}/plugins/module_utils"
|
export ANSIBLE_MODULE_UTILS="${TOPDIR}/plugins/module_utils"
|
||||||
|
|
||||||
# Prepare container
|
|
||||||
container_id=""
|
|
||||||
container_status=("-f" "status=created" "-f" "status=running")
|
|
||||||
[ -n "${scenario}" ] && container_id="$(${engine} ps --all -q -f "name=${scenario}" "${container_status[@]}")"
|
|
||||||
if [ -z "${container_id}" ]
|
|
||||||
then
|
|
||||||
# Retrieve image and start container.
|
|
||||||
log info "Pulling FreeIPA image '${IMAGE_REPO}:${IMAGE_TAG}'..."
|
|
||||||
img_id=$(${engine} pull -q "${IMAGE_REPO}:${IMAGE_TAG}")
|
|
||||||
log info "Creating container..."
|
|
||||||
CONFIG="--hostname ${hostname} --memory ${MEMORY}g --memory-swap -1 --dns none --add-host ipaserver.test.local:127.0.0.1"
|
|
||||||
[ -n "${scenario}" ] && CONFIG="${CONFIG} --name ${scenario}"
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
container_id=$(${engine} create ${CONFIG} "${img_id}" || die "Cannot create container")
|
|
||||||
echo "CONTAINER: ${container_id}"
|
|
||||||
fi
|
|
||||||
scenario="${scenario:-$(${engine} ps -q --format "{{.Names}}" --filter "id=${container_id}" "${container_status[@]}")}"
|
|
||||||
log debug "Using container: ${scenario}"
|
|
||||||
|
|
||||||
# Start container
|
# Start container
|
||||||
make_inventory "${scenario}"
|
"${SCRIPTDIR}/setup_test_container.sh" -e "${engine}" -m "${MEMORY}" -p "${ansible_interpreter}" -i "${IMAGE_TAG}" -n "${IPA_HOSTNAME}" -a "${scenario}" || die "Failed to setup test container"
|
||||||
log info "Starting container for ${scenario}..."
|
|
||||||
quiet ${engine} start "${scenario}"
|
|
||||||
|
|
||||||
# create /etc/resolve.conf
|
|
||||||
run_inline_playbook <<EOF || die "Failed to create /etc/resolv.conf"
|
|
||||||
---
|
|
||||||
- name: Create /etc/resolv.conf
|
|
||||||
hosts: ipaserver
|
|
||||||
gather_facts: no
|
|
||||||
become: yes
|
|
||||||
tasks:
|
|
||||||
- name: Create /etc/resolv.conf
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /etc/resolv.conf
|
|
||||||
mode: 0644
|
|
||||||
content: |
|
|
||||||
search test.local
|
|
||||||
nameserver 127.0.0.1
|
|
||||||
...
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# wait for FreeIPA services to be available
|
|
||||||
run_inline_playbook <<EOF || die "Failed to verify IPA or KDC services."
|
|
||||||
---
|
|
||||||
- name: Wait for IPA services to be available
|
|
||||||
hosts: ipaserver
|
|
||||||
gather_facts: no
|
|
||||||
tasks:
|
|
||||||
- name: Wait for IPA to be started.
|
|
||||||
ansible.builtin.systemd:
|
|
||||||
name: ipa
|
|
||||||
state: started
|
|
||||||
- name: Wait for Kerberos KDC to be started.
|
|
||||||
ansible.builtin.systemd:
|
|
||||||
name: krb5kdc
|
|
||||||
state: started
|
|
||||||
register: result
|
|
||||||
until: not result.failed
|
|
||||||
retries: 30
|
|
||||||
delay: 5
|
|
||||||
- name: Check if TGT is available for admin.
|
|
||||||
ansible.builtin.shell:
|
|
||||||
cmd: echo SomeADMINpassword | kinit -c ansible_freeipa_cache admin
|
|
||||||
register: result
|
|
||||||
until: not result.failed
|
|
||||||
retries: 30
|
|
||||||
delay: 5
|
|
||||||
- name: Cleanup TGT.
|
|
||||||
ansible.builtin.shell:
|
|
||||||
cmd: kdestroy -c ansible_freeipa_cache -A
|
|
||||||
...
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# check image software versions.
|
|
||||||
run_inline_playbook <<EOF || die "Failed to verify software installation."
|
|
||||||
---
|
|
||||||
- name: Software environment.
|
|
||||||
hosts: ipaserver
|
|
||||||
become: yes
|
|
||||||
gather_facts: no
|
|
||||||
tasks:
|
|
||||||
- name: Retrieve versions.
|
|
||||||
shell:
|
|
||||||
cmd: |
|
|
||||||
rpm -q freeipa-server freeipa-client ipa-server ipa-client 389-ds-base pki-ca krb5-server
|
|
||||||
cat /etc/redhat-release
|
|
||||||
uname -a
|
|
||||||
register: result
|
|
||||||
- name: Testing environment.
|
|
||||||
debug:
|
|
||||||
var: result.stdout_lines
|
|
||||||
EOF
|
|
||||||
|
|
||||||
|
|
||||||
# run tests
|
# run tests
|
||||||
@@ -396,6 +160,9 @@ RESULT=0
|
|||||||
|
|
||||||
export RUN_TESTS_IN_DOCKER=${engine}
|
export RUN_TESTS_IN_DOCKER=${engine}
|
||||||
export IPA_SERVER_HOST="${scenario}"
|
export IPA_SERVER_HOST="${scenario}"
|
||||||
|
# Ensure proper ansible_python_interpreter is used by pytest.
|
||||||
|
export IPA_PYTHON_PATH="${ansible_interpreter}"
|
||||||
|
|
||||||
if [ ${SPLITS} -ne 0 ]
|
if [ ${SPLITS} -ne 0 ]
|
||||||
then
|
then
|
||||||
EXTRA_OPTIONS="${EXTRA_OPTIONS} --splits=${SPLITS} --group=${GROUP} --randomly-seed=${SEED}"
|
EXTRA_OPTIONS="${EXTRA_OPTIONS} --splits=${SPLITS} --group=${GROUP} --randomly-seed=${SEED}"
|
||||||
@@ -417,11 +184,11 @@ IPA_VERBOSITY="${verbose}"
|
|||||||
[ -n "${IPA_VERBOSITY}" ] && export IPA_VERBOSITY
|
[ -n "${IPA_VERBOSITY}" ] && export IPA_VERBOSITY
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
if ! pytest -m "playbook" --verbose --color=yes ${EXTRA_OPTIONS}
|
if ! pytest -m "playbook" --verbose --color=yes --suppress-no-test-exit-code --junit-xml=TEST-results-group-${GROUP:-1}.xml ${EXTRA_OPTIONS}
|
||||||
then
|
then
|
||||||
RESULT=2
|
RESULT=2
|
||||||
log error "Container not stopped for verification: ${scenario}"
|
log error "Container not stopped for verification: ${scenario}"
|
||||||
log info "Container: $(podman ps -f "id=${container_id}" --format "{{.Names}} - {{.ID}}")"
|
log info "Container: $(${engine} ps -f "name=${scenario}" --format "{{.Names}} - {{.ID}}")"
|
||||||
fi
|
fi
|
||||||
[ -z "${CONTINUE_ON_ERROR}" ] && [ $RESULT -ne 0 ] && die "Stopping on test failure."
|
[ -z "${CONTINUE_ON_ERROR}" ] && [ $RESULT -ne 0 ] && die "Stopping on test failure."
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ die() {
|
|||||||
|
|
||||||
TOPDIR="$(dirname "${BASH_SOURCE[0]}")/.."
|
TOPDIR="$(dirname "${BASH_SOURCE[0]}")/.."
|
||||||
|
|
||||||
|
[ -n "$(command -v python3)" ] && python="$(command -v python3)" || python="$(command -v python2)"
|
||||||
|
|
||||||
pushd "${TOPDIR}" >/dev/null 2>&1 || die "Failed to change directory."
|
pushd "${TOPDIR}" >/dev/null 2>&1 || die "Failed to change directory."
|
||||||
|
|
||||||
files_list=$(mktemp)
|
files_list=$(mktemp)
|
||||||
@@ -25,7 +27,7 @@ git diff "${remote}/master" --name-only > "${files_list}"
|
|||||||
git remote remove ${remote}
|
git remote remove ${remote}
|
||||||
|
|
||||||
# Get all modules that should have tests executed
|
# Get all modules that should have tests executed
|
||||||
enabled_modules="$(python utils/get_test_modules.py $(cat "${files_list}"))"
|
enabled_modules="$(${python} utils/get_test_modules.py $(cat "${files_list}"))"
|
||||||
[ -z "${enabled_modules}" ] && enabled_modules="None"
|
[ -z "${enabled_modules}" ] && enabled_modules="None"
|
||||||
|
|
||||||
# Get individual tests that should be executed
|
# Get individual tests that should be executed
|
||||||
|
|||||||
119
utils/setup_test_container.sh
Executable file
119
utils/setup_test_container.sh
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/bin/bash -eu
|
||||||
|
|
||||||
|
SCRIPTDIR="$(readlink -f "$(dirname "$0")")"
|
||||||
|
|
||||||
|
# shellcheck source=utils/shcontainer
|
||||||
|
. "${SCRIPTDIR}/shcontainer"
|
||||||
|
# shellcheck source=utils/shansible
|
||||||
|
. "${SCRIPTDIR}/shansible"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
local prog="${0##*/}"
|
||||||
|
cat <<EOF
|
||||||
|
usage: ${prog} [-h] [-l] [-a] [-e ENGINE] [-i IMAGE] [-m MEMORY] [-n HOSTNAME] NAME
|
||||||
|
${prog} starts a container to test ansible-freeipa.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
usage
|
||||||
|
echo -e "$(cat <<EOF
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
NAME set the container name
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-h display this message and exit
|
||||||
|
-l list available images
|
||||||
|
-a Test Ansible connection.
|
||||||
|
-e ENGINE set the container engine to use
|
||||||
|
(default: ${WHITE}podman${RST}, if available)
|
||||||
|
-i IMAGE select image to run the tests (default: fedora-latest)
|
||||||
|
-m MEMORY set container memory, in GiB (default: 3)
|
||||||
|
-n HOSTNAME set the hostname in the container
|
||||||
|
(default: ipaserver.test.local)
|
||||||
|
-p INTERPRETER Python interpreter to use on target container
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
}
|
||||||
|
|
||||||
|
list_images() {
|
||||||
|
local quay_api="https://quay.io/api/v1/repository/ansible-freeipa/upstream-tests/tag"
|
||||||
|
echo -e "${WHITE}Available images:"
|
||||||
|
curl --silent -L "${quay_api}" | jq '.tags[]|.name' | tr -d '"'| sort | uniq | sed "s/.*/ &/"
|
||||||
|
echo -e "${RST}"
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_TAG="fedora-latest"
|
||||||
|
MEMORY="${MEMORY:-3}"
|
||||||
|
IPA_HOSTNAME="${IPA_HOSTNAME:-"ipaserver.test.local"}"
|
||||||
|
test_env="${test_env:-"/tmp"}"
|
||||||
|
ansible_interpreter="/usr/bin/python3"
|
||||||
|
engine="podman"
|
||||||
|
ansible_test=""
|
||||||
|
|
||||||
|
while getopts ":hae:i:lm:n:p:" option
|
||||||
|
do
|
||||||
|
case "$option" in
|
||||||
|
h) help && exit 0 ;;
|
||||||
|
a) ansible_test="yes" ;;
|
||||||
|
e) engine="${OPTARG}" ;;
|
||||||
|
i) IMAGE_TAG="${OPTARG}" ;;
|
||||||
|
l) list_images && exit 0 || exit 1;;
|
||||||
|
m) MEMORY="${OPTARG}" ;;
|
||||||
|
n) IPA_HOSTNAME="${OPTARG}" ;;
|
||||||
|
p) ansible_interpreter="${OPTARG}" ;;
|
||||||
|
*) die -u "Invalid option: ${OPTARG}" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
export IPA_HOSTNAME MEMORY IMAGE_TAG scenario
|
||||||
|
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
[ $# == 1 ] || die -u "You must provide the name for a single container."
|
||||||
|
scenario="${1}"
|
||||||
|
shift
|
||||||
|
|
||||||
|
prepare_container "${scenario}" "${IMAGE_TAG}"
|
||||||
|
start_container "${scenario}"
|
||||||
|
|
||||||
|
# wait for FreeIPA services to be available (usually ~45 seconds)
|
||||||
|
log info "Wait for container to be initialized."
|
||||||
|
wait=15
|
||||||
|
while podman exec "${scenario}" systemctl list-jobs | grep -qvi "no jobs running"
|
||||||
|
do
|
||||||
|
log none "Waiting ${wait}s... "
|
||||||
|
sleep "${wait}"
|
||||||
|
log none "Retrying".
|
||||||
|
done
|
||||||
|
|
||||||
|
# run tests
|
||||||
|
|
||||||
|
# ensure we can get a TGT for admin
|
||||||
|
log info "Testing kinit with admin."
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
"${engine}" exec "${scenario}" /bin/sh -c 'for i in $(seq 5); do echo "SomeADMINpassword" | kinit -c ansible_freeipa_cache admin && kdestroy -c ansible_freeipa_cache -A && break; echo "Failed to get TGT. Retrying in 10s..."; sleep 10; done' || die "Failed to grant admin TGT."
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
log info "Creating inventory."
|
||||||
|
make_inventory "${scenario}" "${engine}" "${ansible_interpreter:-"/usr/bin/python3"}"
|
||||||
|
if [ -z "${inventory:-''}" ]
|
||||||
|
then
|
||||||
|
log error "Could not create inventory file."
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
log info "Inventory path: [${inventory}]"
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
log debug "$(cat "${inventory}")"
|
||||||
|
if [ "${ansible_test}" == "yes" ]
|
||||||
|
then
|
||||||
|
log info "Testing Ansible connection."
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
run_if_exists ansible_ping "${inventory}"
|
||||||
|
log info "Querying installed software"
|
||||||
|
run_if_exists query_container_installed_software
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
88
utils/shansible
Normal file
88
utils/shansible
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash -eu
|
||||||
|
# This file is meant to be source'd by other scripts
|
||||||
|
|
||||||
|
SCRIPTDIR="$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")"
|
||||||
|
|
||||||
|
# shellcheck source=utils/shfun
|
||||||
|
. "${SCRIPTDIR}/shfun"
|
||||||
|
|
||||||
|
install_ansible() {
|
||||||
|
ANSIBLE_VERSION="${1:-${ANSIBLE_VERSION:-"ansible-core"}}"
|
||||||
|
[ $# -gt 0 ] && shift
|
||||||
|
log info "Installing Ansible: ${ANSIBLE_VERSION}"
|
||||||
|
pip install --quiet "${ANSIBLE_VERSION}"
|
||||||
|
log debug "Ansible version: $(ansible --version | sed -n "1p")${RST}"
|
||||||
|
|
||||||
|
if [ -n "${ANSIBLE_COLLECTIONS}" ]
|
||||||
|
then
|
||||||
|
log warn "Installed collections will not be removed after execution."
|
||||||
|
log none "Installing: Ansible Collection ${ANSIBLE_COLLECTIONS}"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
quiet ansible-galaxy collection install ${ANSIBLE_COLLECTIONS} || die "Failed to install Ansible collections."
|
||||||
|
fi
|
||||||
|
export ANSIBLE_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
run_inline_playbook() {
|
||||||
|
local playbookdir playbook err
|
||||||
|
playbookdir=${1:-"playbooks"}
|
||||||
|
quiet mkdir -p "${playbookdir}"
|
||||||
|
playbook=$(mktemp "${playbookdir}/ansible-freeipa-test-playbook_ipa.XXXXXXXX" 2>/dev/null)
|
||||||
|
# In some configurations, it may not be possible to use another
|
||||||
|
# directory, so we store the playbook in the current one.
|
||||||
|
# [ -z "${playbook}" ] && playbook=$(mktemp "ansible-freeipa-test-playbook_ipa.XXXXXXXX")
|
||||||
|
|
||||||
|
inventory="${inventory:-${test_env:-"."}/inventory}"
|
||||||
|
quiet mkdir -p "${playbookdir}"
|
||||||
|
cat - >"${playbook}"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
run_if_exists ansible-playbook ${ansible_options:-} -i "${inventory}" "${playbook}"
|
||||||
|
err=$?
|
||||||
|
rm -f "${playbook}"
|
||||||
|
return ${err}
|
||||||
|
}
|
||||||
|
|
||||||
|
make_inventory() {
|
||||||
|
local scenario pod_engine ansible_interpreter
|
||||||
|
scenario=$1
|
||||||
|
pod_engine="${engine:-${2:-podman}}"
|
||||||
|
ansible_interpreter="${3:-${ansible_interpreter:-"/usr/bin/python3"}}"
|
||||||
|
export inventory="${test_env:-"."}/inventory"
|
||||||
|
log info "Inventory file: ${inventory}"
|
||||||
|
cat << EOF > "${inventory}"
|
||||||
|
[ipaserver]
|
||||||
|
${scenario} ansible_connection=${pod_engine} ansible_python_interpreter=${ansible_interpreter}
|
||||||
|
[ipaserver:vars]
|
||||||
|
ipaserver_domain = test.local
|
||||||
|
ipaserver_realm = TEST.LOCAL
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
query_container_installed_software() {
|
||||||
|
# check image software versions.
|
||||||
|
run_inline_playbook "${test_env:-"/tmp"}/playbooks" <<EOF || die "Failed to verify software installation."
|
||||||
|
---
|
||||||
|
- name: Software environment.
|
||||||
|
hosts: ipaserver
|
||||||
|
become: yes
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- name: Retrieve versions.
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
cat /etc/redhat-release
|
||||||
|
${python:-"python3"} --version
|
||||||
|
rpm -q freeipa-server freeipa-client ipa-server ipa-client 389-ds-base pki-ca krb5-server
|
||||||
|
uname -a
|
||||||
|
register: result
|
||||||
|
- name: Testing environment.
|
||||||
|
ansible.builtin.debug:
|
||||||
|
var: result.stdout_lines
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
ansible_ping() {
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
ansible ${ansible_options:-} -m ping -i "${1:-${inventory}}" all || die "Could not connect to container."
|
||||||
|
}
|
||||||
|
|
||||||
|
export ANSIBLE_VERSION=${ANSIBLE_VERSION:-'ansible-core'}
|
||||||
61
utils/shcontainer
Normal file
61
utils/shcontainer
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/bash -eu
|
||||||
|
# This file is meant to be source'd by other scripts
|
||||||
|
|
||||||
|
SCRIPTDIR="$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")"
|
||||||
|
|
||||||
|
IMAGE_REPO="quay.io/ansible-freeipa/upstream-tests"
|
||||||
|
|
||||||
|
# shellcheck source=utils/shfun
|
||||||
|
. "${SCRIPTDIR}/shfun"
|
||||||
|
|
||||||
|
stop_container() {
|
||||||
|
local scenario=${1}
|
||||||
|
log none "Stopping container..."
|
||||||
|
quiet "${engine}" stop "${scenario}"
|
||||||
|
log none "Removing container..."
|
||||||
|
quiet "${engine}" rm "${scenario}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare container
|
||||||
|
prepare_container() {
|
||||||
|
local container_id container_status hostname scenario
|
||||||
|
local IMAGE_TAG img_id CONFIG
|
||||||
|
container_id=""
|
||||||
|
container_status=("-f" "status=created" "-f" "status=running")
|
||||||
|
hostname="${IPA_HOSTNAME:-"ipaserver.test.local"}"
|
||||||
|
scenario="${1:-${scenario:-"freeipa-tests"}}"
|
||||||
|
IMAGE_TAG="${2:-${IMAGE_TAG:-fedora-latest}}"
|
||||||
|
[ -n "${scenario}" ] && container_id="$(${engine} ps --all -q -f "name=${scenario}" "${container_status[@]}")"
|
||||||
|
if [ -z "${container_id}" ]
|
||||||
|
then
|
||||||
|
# Retrieve image and start container.
|
||||||
|
log info "Pulling FreeIPA image '${IMAGE_REPO}:${IMAGE_TAG}'..."
|
||||||
|
img_id=$(${engine} pull -q "${IMAGE_REPO}:${IMAGE_TAG}")
|
||||||
|
log debug "Hostname: ${hostname}"
|
||||||
|
log info "Creating container..."
|
||||||
|
CONFIG="--systemd true --hostname ${hostname} --memory ${MEMORY}g --memory-swap -1 --no-hosts"
|
||||||
|
[ -n "${scenario}" ] && CONFIG="${CONFIG} --name ${scenario}"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
container_id=$(${engine} create ${CONFIG} "${img_id}" || die "Cannot create container")
|
||||||
|
log none "CONTAINER: ${container_id}"
|
||||||
|
fi
|
||||||
|
export scenario="${scenario:-$(${engine} ps -q --format "{{.Names}}" --filter "id=${container_id}" "${container_status[@]}")}"
|
||||||
|
log debug "Prepared container: ${scenario}"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_container() {
|
||||||
|
local scenario="${1:-${scenario}}"
|
||||||
|
log info "Starting container for ${scenario}..."
|
||||||
|
"${engine}" start "${scenario}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$(command -v podman)" ]
|
||||||
|
then
|
||||||
|
engine="docker"
|
||||||
|
engine_collection="community.docker"
|
||||||
|
else
|
||||||
|
engine="podman"
|
||||||
|
engine_collection="containers.podman"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export engine engine_collection
|
||||||
Reference in New Issue
Block a user