ci(podman): add state tests in simple container

This commit is contained in:
Andrii Nikitin 2023-04-25 11:48:29 +02:00
parent 5e2e2e3e72
commit aea6ecf86e
9 changed files with 354 additions and 0 deletions

20
.github/workflows/test.yaml vendored Normal file
View file

@ -0,0 +1,20 @@
name: CI
on: [push, pull_request, workflow_dispatch]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
img:
- opensuse.leap
- debian.11
steps:
- uses: actions/checkout@v2
- name: Test
run: |
cd t && \
make test_container
env:
T_IMAGE: ${{ matrix.img }}

12
t/01-smoke-server.sh Executable file
View file

@ -0,0 +1,12 @@
#!lib/test-in-container-systemd.sh mariadb
set -ex
salt-call --local state.apply 'mysql'
salt-call --local state.apply 'mysql.remove_test_database'
mysql -e 'select user(), current_user(), version()'
service mysql status
echo success

29
t/02-smoke-client.sh Executable file
View file

@ -0,0 +1,29 @@
#!lib/test-in-container-systemd.sh mariadb-client
set -ex
#########################
# workaround for https://github.com/saltstack-formulas/mysql-formula/issues/267
( grep -qi debian /etc/*release && mkdir -p /etc/mysql ) || \
( grep -qi suse /etc/*release && mkdir -p /etc/my.cnf.d ) || \
:
#########################
salt-call --local state.apply 'mysql.client'
mariadb -V || mysql -V
# check mysqld service is not installed
rc=0
service mysql status || rc=$?
test $rc -gt 0
rc=0
service mysql status || rc=$?
test $rc -gt 0
rc=0
service mariadb status || rc=$?
test $rc -gt 0
echo success

62
t/10-password.sh Executable file
View file

@ -0,0 +1,62 @@
#!lib/test-in-container-systemd.sh mariadb
set -ex
# hack the pillar
mkdir -p /srv/pillar
echo "
mysql:
server:
mysqld:
max_allowed_packet: 1111040
database:
- testdb
user:
myuser1:
password: 'mypass123'
host: localhost
databases:
- database: testdb
grants: ['all']
" > /srv/pillar/testdata.sls
echo '
{{ saltenv }}:
"*":
- testdata
' >> /srv/pillar/top.sls
salt-call --local pillar.get mysql:user:myuser1:password
salt-call --local pillar.item mysql:user:myuser1:password | grep mypass123
pass=$(echo $(salt-call --local pillar.get mysql:user:myuser1:password) | tail -n 1 | grep -Eo '[^ ]+$')
test "$pass" == mypass123
salt-call --local state.apply 'mysql'
salt-call --local state.apply 'mysql.remove_test_database'
set -a
shopt -s expand_aliases
alias sql="mariadb -h 127.0.0.1 -umyuser1 -p$pass -e"
(
sql 'select user(), current_user(), version()'
packet="$(sql 'show variables like "max_allowed_packet"' -Nb)"
test 1111040 == ${packet//[!0-9]/}
echo test access denied to mysql database
rc=0
sql 'select user, host from mysql.user' || rc=$?
test "$rc" -gt 0
echo test wrong password is denied
rc=0
sql 'select user(), current_user(), version()' -p"wrongpassword" || rc=$?
test "$rc" -gt 0
)
service mariadb status || service mysql status
echo success

8
t/Makefile Normal file
View file

@ -0,0 +1,8 @@
test_container:
( for f in *.sh; do ./$$f && continue; echo FAIL $$f; exit 1 ; done )
test_container_fast:
( for f in *.sh; do T_CACHE_PACKAGES=1 ./$$f && continue; echo FAIL $$f; exit 1 ; done )

77
t/README.md Normal file
View file

@ -0,0 +1,77 @@
Scripts to test SLS changes in podman containers
-------------------
The goal is to cover following workflow:
* Change files related to an individual state.
* Spawn a container with a local standalone salt node and apply the states.
* Check basic bash commands to verify outcome.
The test is set of bash commands.
The script relies on shebang to prepare an image and spawn a container with the sls files.
It is also ready to test salt states using set of commands `salt-call --local`.
###### Example: Run test for mysql states:
```bash
cd t
./01-smoke-server.sh
```
#### Challenge 1: By default, a container is destroyed when the test finishes.
This is to simplify re-run of tests and do not flood machine with leftover containers after tests.
To make sure container stays around after faiure - set environment variable *T_PAUSE_ON_FAILURE* to 1
###### Example: Connect to the container after test failure
```bash
> # terminal 1
> echo fail >> 01-smoke-server.sh
> T_PAUSE_ON_FAILURE=1 ./01-smoke-server.sh
...
bash: line 18: fail: command not found
Test failed, press any key to finish
```
The terminal will wait for any input to finish the test and clean up the container.
Now use another terminal window to check the running podman container and get into it for eventual troubleshooting:
```bash
> # terminal 2
> podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a37d23503fa localhost/mysql.formula.t38c8b1d778efa61c676f1aa99b2aded5.image:latest 4 minutes ago Up 4 minutes ago mysql.formula.t38c8b1d778efa61c676f1aa99b2aded5.01-smoke-server.sh
> # use copy container id to start bash in it (hint: or bash completion should work for container name as well)
> podman exec -it mysql.formuls.t38c8b1d778efa61c676f1aa99b2aded5.01-smoke-server.sh bash
2a37d23503fa:/opt/project # ls
bin encrypted_pillar_recipients ENCRYPTION.md FORMULAS.yaml gpgkeys pillar README.md salt t test
2a37d23503fa:/opt/project # # now we are inside the container and can troubleshoot outcome of salt commands
2a37d23503fa:/opt/project # rcmysql status
* mariadb.service - MariaDB database server
Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disabled)
Active: active (running) since Fri 2023-03-17 12:45:24 UTC; 10min ago
```
#### Challenge 2: Vary OS in container.
Create new file Dockerfile.%osname% similar to existing Docker files in t/lib.
Use environment variable T_IMAGE=%osname% to let scripts use corresponding Dockerfile.
By default tests are run with t/Dockerfile.opensuse.leap
#### Challenge 3: Cache packages inside test image.
(Currently works only with default docker image, i.e. T_IMAGE is empty).
Downloading and installing packages may be time consuming, so sometimes it may be advantageous to have an option to pre-install required packages inside image, in which the test will be run. (So the test will concentrate on verifying other aspects of salt states, without spending time on installing packages).
At the same time the CI needs to verify salt states related to installation, so it must run the test without such caching.
Such caching is implemented as optional parameters to shebang command in the test scripts. These parameters are ignored unless global variable *T_CACHE_PACKAGES* is set to 1.
```bash
> # check parameter in shebang:
> head -n 1 01-smoke-server.sh
#!lib/test-in-container-systemd.sh mariadb
> # run the test in a container with preinstalled mariadb package as specified above:
> T_CACHE_PACKAGES=1 ./01-smoke-server.sh
> # run the test in a container without preinstalled mariadb package (will take longer, but will verify package installation as part of test)
> ./01-smoke-server.sh
```

View file

@ -0,0 +1,19 @@
FROM debian:11
ENV container podman
ENV LANG en_US.UTF-8
# these are needed to run test
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install systemd salt-minion curl sudo
##DUMMY
RUN mkdir -p /srv/salt/ && \
sed -i 's^\#*\s*file_client: .*$^file_client: local\nsystemd.scope: False\nenable_fqdns_grains: False^' /etc/salt/minion
ADD mysql /srv/salt/mysql
WORKDIR /opt/project
ENTRYPOINT ["/bin/systemd"]

View file

@ -0,0 +1,21 @@
FROM registry.opensuse.org/opensuse/leap
ENV container podman
ENV LANG en_US.UTF-8
RUN test ! -f /var/log/zypper.log || mv /var/log/zypper.log /var/log/zypper.log.preinstalled
# these are needed to run test
RUN zypper -vvvn install systemd salt-minion sudo
##DUMMY
RUN mkdir -p /srv/salt/ && \
sed -i 's^\#*\s*file_client: .*$^file_client: local\nsystemd.scope: False\nenable_fqdns_grains: False^' /etc/salt/minion && \
sed -i '/pam_systemd.so/d' /etc/pam.d/common-session-pc # delete pam_systemd , otherwise sudo will hang
ADD mysql /srv/salt/mysql
WORKDIR /opt/project
ENTRYPOINT ["/usr/lib/systemd/systemd"]

View file

@ -0,0 +1,106 @@
#!/bin/bash
#
# Copyright (C) 2023 SUSE LLC
#
# 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 2 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/>.
last=${@:$#} # last parameter
other=${*%${!#}} # all parameters except the last
testcase=$last
set -euo pipefail
PODMAN=podman
image=${T_IMAGE-opensuse.leap}
(
PODMAN_info="$($PODMAN info >/dev/null 2>&1)" || $PODMAN info
[ -n "$testcase" ] || (echo No testcase provided; exit 1)
[ -f "$testcase" ] || (echo Cannot find file "$testcase"; exit 1 )
) >&2
thisdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
basename=$(basename "$testcase")
basename=${basename,,}
basename=${basename//:/_}
othermd5=
test -z "$other" || othermd5=($(echo "$other" | md5sum -))
ident=mysql.formula.t$othermd5
containername="$ident.${basename,,}"
# we use variable T_CACHE_PACKAGES to speedup testing and make sure that
(
if test -z "$other" || \
test "${T_IMAGE-}" != "" || \
test "${T_CACHE_PACKAGES:-}" != 1
then
cat $thisdir/Dockerfile.${image}
else
ar=""
in="zypper -vvvn in "
for pkg in $other vim; do
if [[ $pkg == *:* ]]; then
n=${pkg//*:}
ar="$ar zypper -n ar http://download.opensuse.org/repositories/$pkg/\\\\\$releasever/ $n;"
in="$in $n"
else
in="$in $pkg"
fi
done
test -z "$ar" || ar="$ar zypper --gpg-auto-import-keys ref; "
sed "s,\#\#DUMMY,RUN $ar $in,g" $thisdir/Dockerfile.${T_IMAGE}
fi
) | cat | $PODMAN build -t $ident.image -f - $thisdir/../..
map_port=""
[ -z "${EXPOSE_PORT:-}" ] || map_port="-p $EXPOSE_PORT:80"
$PODMAN run --privileged --rm $map_port --name "$containername" -d -v"$thisdir/../..":/opt/project --add-host localhost:127.0.0.1 -- $ident.image
in_cleanup=0
ret=111
function cleanup {
[ "$in_cleanup" != 1 ] || return
in_cleanup=1
if [ "$ret" != 0 ] && [ -n "${T_PAUSE_ON_FAILURE-}" ]; then
read -rsn1 -p"Test failed, press any key to finish";echo
fi
[ "$ret" == 0 ] || echo FAIL $basename
$PODMAN stop -t 0 "$containername" >&/dev/null || :
}
trap cleanup INT TERM EXIT
counter=1
# wait container start
until [ $counter -gt 10 ]; do
sleep 0.5
$PODMAN exec "$containername" pwd >& /dev/null && break
((counter++))
done
$PODMAN exec "$containername" pwd >& /dev/null || (echo Cannot start container; exit 1 ) >&2
echo "$*"
# [ -z $initscript ] || echo "bash -xe /opt/project/t/$initscript" | $PODMAN exec -i "$containername" bash -x
set +e
$PODMAN exec -e TESTCASE="$testcase" -i "$containername" bash -xe < "$testcase"
ret=$?
( exit $ret )