Skip to content

Commit

Permalink
ci(podman): add state tests in simple container
Browse files Browse the repository at this point in the history
  • Loading branch information
andrii-suse committed Apr 25, 2023
1 parent 5e2e2e3 commit 092ed6d
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions t/01-smoke-server.sh
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions t/02-smoke-client.sh
Original file line number Diff line number Diff line change
@@ -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 changes: 62 additions & 0 deletions t/10-password.sh
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions t/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 77 additions & 0 deletions t/README.md
Original file line number Diff line number Diff line change
@@ -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
```

19 changes: 19 additions & 0 deletions t/lib/Dockerfile.debian.11
Original file line number Diff line number Diff line change
@@ -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"]
21 changes: 21 additions & 0 deletions t/lib/Dockerfile.opensuse.leap
Original file line number Diff line number Diff line change
@@ -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"]
106 changes: 106 additions & 0 deletions t/lib/test-in-container-systemd.sh
Original file line number Diff line number Diff line change
@@ -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 )

0 comments on commit 092ed6d

Please sign in to comment.