Kea 2.7.6
|
By default, Kea is built without unit-tests as they're used mostly by developers and prospective contributors. Kea's unit-tests are using gtest framework from Google. Google's approach has changed over the years. For some time, they were very keen on not installing gtest as a normal software would be, but rather provide gtest as sources. This was further complicated with the fact that some Linux distributions packaged gtest and tried to mimic its installation. Kea tries its best to accommodate all typical situations and provides two switches to point to gtest. You can use --with-gtest
or --with-gtest-source
. Both attempt to locate gtest on their own. However, if neither of them can find it, you can specify the path explicitly. For example, on ubuntu with googletest package installed, you can do the following for Kea to find it:
Depending on how you compiled or installed gtest
(e.g. from sources or using some package management system) one of those two switches will find gtest
. After that you make and run the unit-tests with:
As usual, using -jX
option will speed up compilation. This parameter is even more useful for unit-tests as there are over 6000 unit-tests and their compilation is significantly slower than just the production Kea sources.
Kea should work with reasonably recent gtest versions. We recently tried with 1.7.0, 1.8.0, 1.8.1 and 1.10.0.
The following environment variable can affect the unit tests:
GTEST_OUTPUT=${PWD}/test-results/
will create a directory containing an XML file for each directory being tested. Leaving the slash out will create a single XML file and will put all the test results in it.DEBUG=true
. unset DEBUG
to remove this behavior.GCC and LLVM support some sanitizers which perform additional tests at runtime, for instance the ThreadSanitizer (aka TSan) detects data race in executed C++ code (unfortunately on macOS it intercepts signals and fails to send them to waiting select system calls so some tests always fail when it is used, experiments are run with different versions of Tsan).
The simplest way to enable a sanitizer is to add it to the CXXFLAGS environment variable in .configure by e.g. -fsanitize=thread.
When enabling lcov (code coverage), some gtest functions are detected as not being thread safe. It is recommended to disable lcov when enabling thread sanitizer.
With the use of databases requiring separate authorisation, there are certain database-specific pre-requisites for successfully running the unit tests. These are listed in the following sections.
Unit tests validating database backends require that the keatest database is created. This database should be empty. The unit tests also require that the keatest user is created and that this user is configured to access the database with a password of keatest. Unit tests use these credentials to create database schema, run test cases and drop the schema. Thus, the keatest user must have sufficiently high privileges to create and drop tables, as well as insert and modify the data within those tables.
The database backends which support read only access to the host reservations databases (currently MySQL and PostgreSQL) include unit tests verifying that a database user with read-only privileges can be used to retrieve host reservations. Those tests require another user, keatest_readonly, with SQL SELECT privilege to the keatest database (i.e. without INSERT, UPDATE etc.), is also created. keatest_readonly should also have the password keatest.
The following sections provide step-by-step guidelines how to setup the databases for running unit tests.
The steps to create the database and users are:
% mysql -u root -p Enter password: : mysql>
mysql> CREATE DATABASE keatest; mysql>
mysql> CREATE USER 'keatest'@'localhost' IDENTIFIED BY 'keatest'; mysql> CREATE USER 'keatest_readonly'@'localhost' IDENTIFIED BY 'keatest'; mysql> CREATE USER 'keatest_secure'@'localhost' IDENTIFIED BY 'keatest'; mysql> ALTER USER 'keatest_secure'@'localhost' REQUIRE X509; mysql>
mysql> GRANT ALL ON keatest.* TO 'keatest'@'localhost'; mysql> GRANT SELECT ON keatest.* TO 'keatest_readonly'@'localhost'; mysql> GRANT ALL ON keatest.* TO 'keatest_secure'@'localhost'; mysql>
mysql> GRANT ALL ON keatest.* TO 'keatest_secure'@'localhost' REQUIRE X509; mysql>
mysql> SET GLOBAL LOG_BIN_TRUST_FUNCTION_CREATORS = 1; mysql>
mysql> quit Bye %
The unit tests are run automatically when "make check" is executed (providing that Kea has been built with the --with-mysql
switch (see the installation section in the Kea Administrator Reference Manual).
Usually MySQL is compiled with SSL/TLS support using OpenSSL. This is easy to verify using the:
mysql> SHOW GLOBAL VARIABLES LIKE 'have_ssl';
The variable is documented to have three possible values:
The value of this MySQL global variable is reflected by the KEA_MYSQL_HAVE_SSL environment variable.
The keatest_secure user requires X509 so a client certificate. Of course in production a stricter requirement should be used, in particular when a client certificate should be bound to a particular user.
MySQL unit tests reuse the asiolink library setup. This .my.cnf configuration file works with MariaDB 10.6.4:
[mysqld] ssl_cert=<kea-sources>/src/lib/asiolink/testutils/ca/kea-server.crt ssl_key=<kea-sources>/src/lib/asiolink/testutils/ca/kea-server.key ssl_ca=<kea-sources>/src/lib/asiolink/testutils/ca/kea-ca.crt [client-mariadb] ssl_cert=<kea-sources>/src/lib/asiolink/testutils/ca/kea-client.crt ssl_key=<kea-sources>/src/lib/asiolink/testutils/ca/kea-client.key ssl_ca=<kea-sources>/src/lib/asiolink/testutils/ca/kea-ca.crt ssl-verify-server-cert
The last statement requires mutual authentication named two way in the MariaDB documentation. For MySQL versions greater than 5.7.11 this statement should be replaced by:
[client] ... ssl-mode=VERIFY_IDENTITY
On Debian some MySQL packages use GnuTLS instead OpenSSL to provide the SSL/TLS support: this is known to not work with this proposed setup.
PostgreSQL set up differs from system to system. Please consult your operating system-specific PostgreSQL documentation. The remainder of that section uses Ubuntu 13.10 x64 (with PostgreSQL 9.0+) as an example.
On Ubuntu, PostgreSQL is installed (with sudo apt-get install postgresql
) under user postgres. To create new databases or add new users, initial commands must be issued under this username:
$ sudo -u postgres psql postgres [sudo] password for thomson: psql (9.1.12) Type "help" for help. postgres=# CREATE USER keatest WITH PASSWORD 'keatest'; CREATE ROLE postgres=# CREATE DATABASE keatest; CREATE DATABASE postgres=# GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest; GRANT postgres=# \c keatest You are now connected to database "keatest" as user "postgres". keatest=# GRANT ALL PRIVILEGES ON SCHEMA public TO keatest; GRANT postgres=# \q
PostgreSQL versions earlier than 9.0 don't provide an SQL statement for granting privileges on all tables in a database. In newer PostgreSQL versions, it is possible to grant specific privileges on all tables within a schema. However, this only affects tables which exist when the privileges are granted. To ensure that the user has specific privileges to tables dynamically created by the unit tests, the default schema privileges must be altered. In Postgres 15.0+, you need to explicitly grant privileges to access the public
schema.
The following example demonstrates how to create the user keatest_readonly, which has SELECT privilege to the tables within the keatest database, in Postgres 9.0+. For earlier versions of Postgres, it is recommended to simply grant full privileges to keatest_readonly, using the same steps as for the keatest user.
$ psql -U postgres Password for user postgres: psql (9.1.12) Type "help" for help. postgres=# CREATE USER keatest_readonly WITH PASSWORD 'keatest'; CREATE ROLE postgres=# \q $ psql -U keatest Password for user keatest: psql (9.1.12) Type "help" for help. keatest=> ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES to keatest_readonly; ALTER DEFAULT PRIVILEGES keatest=> \q
Note that the keatest user (rather than postgres) is used to grant privileges to the keatest_readonly user. This ensures that the SELECT privilege is granted only on the tables that the keatest user can access within the public schema.
It seems this no longer works on recent versions of PostgreSQL: if you get a permission problem on SELECT on the schema_version table for eatest_readonly, please try with the schema loaded:
$ psql -h localhost -U keatest -d keatest Password for user keatest: psql (11.3 (Debian 11.3-1)) SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) Type "help" for help. keatest=> GRANT SELECT ON ALL TABLES IN SCHEMA public TO keatest_readonly; GRANT keatest=> \q
Now we should be able to log into the newly created database using both user names:
$ psql -d keatest -U keatest Password for user keatest: psql (9.1.12) Type "help" for help. keatest=> \q $ psql -d keatest -U keatest_readonly Password for user keatest_readonly: psql (9.1.12) Type "help" for help. keatest=>
If instead of seeing keatest=> prompt, your login is refused with an error code about failed peer or Ident authentication failed for user "keatest"
, it means that PostgreSQL is configured to check unix username and reject login attempts if PostgreSQL names are different. To alter that, the PostgreSQL pg_hba.conf configuration file must be changed. It usually resides at /var/lib/postgresql/data/pg_hba.conf
or at /etc/postgresql/${version}/main/pg_hba.conf
, but you can find out for sure by running sudo -u postgres psql -t -c 'SHOW hba_file'
. Make sure that all the authentication methods are changed to "md5" like this:
local all all md5 host all all 127.0.0.1/32 md5 host all all ::1/128 md5
Another possible problem is that you get no password prompt. This is most probably because you have no pg_hba.conf
config file and everybody is by default trusted. As it has a very bad effect on the security you should have been warned this is a highly unsafe configuration. The solution is the same, i.e., require password or md5 authentication method.
If you lose the postgres user access you can first add:
local all postgres trust
to trust only the local postgres user. Note the postgres user can be pgsql on some systems.
Please consult your PostgreSQL user manual before applying those changes as those changes may expose your other databases that you run on the same system. In general case, it is a poor idea to run anything of value on a system that runs tests. Use caution!
The unit tests are run automatically when "make check" is executed (providing that Kea has been build with the --with-pgsql
switch (see the installation section in the Kea Administrator Reference Manual).
The GSS-TSIG hook library uses the GSS-API with Kerberos. While there are no doubts that the hook can be safely used with a valid Kerberos configuration in production, unit tests reported problems on some systems.
GSS-TSIG hook unit tests use a setup inherited from bind9 with old crypto settings which are not allowed by default Kerberos system configuration. A simple workaround is to set the KRB5_CONFIG environment variable to a random value that doesn't match a file (e.g. KRB5_CONFIG=).
Shell tests are shellcheck
ed. But there are other writing practices that are good to follow in order to keep, not only shell tests, but shell scripts in general, POSIX-compliant. See below:
sh
shell can differ on various operating systems. On most systems it is GNU sh. Notable exceptions are Alpine which links it to ash, FreeBSD which has the primordial non-GNU sh, Ubuntu which links it to dash. These four shells should all be tested against, when adding shell scripts or making changes to them.printf
instead of echo
. There are times when a newline is not desired such as when you want to print on a single line from multiple points in your script or when you want to get the character count from an expression: printf
also has the benefit of separating the format from the actual variables which has many use cases. One such use case is coloring output with ANSI escape sequence codes, see the test_finish
function in src/lib/testutils/dhcp_test_lib.sh.in
, which is not possible with POSIX echo.set -e
should be enabled at all times to immediately fail when a command returns a non-zero exit code. There are times when you expect a non-zero exit code in your tests. This is what the run_command
function in src/lib/testutils/dhcp_test_lib.sh.in
is for. It momentarily disables the -e
flag to capture the output and exit code and enables it again afterwards. The variables used are ${EXIT_CODE}
and ${OUTPUT}
. /dev/stderr is not captured. run_command
also doesn't work with pipes and redirections. When these mechanisms are needed, you can always wrap your complex expression in a function and then call run_command wrapping_function
. Alternatively, if you only care about checking for zero exit code, you can use if
conditions. grep
returns a non-zero exit code if it doesn't find anything. In that case, you can add || true
at the end to signal the fact that you allow finding nothing like so: set -u
should be enabled at all times to immediately signal an undefined variable. If you're a stickler for the legacy behavior of defaulting to an empty space then you can reference all your variables with: *
wildcard character e.g. ls *.txt
. Sometimes, you want to glob intentionally. In that case, you can omit quoting, but it is preferable to take the wildcard characters outside the variable so that you are able to quote to prevent other globbing and word splitting e.g.: $
..in
autoconf template files. They initially contain template variables like @prefix@
which are then substituted with the configured values. All of these should be double quoted, not single-quoted since they themselves can contain shell variables that need to be expanded.$(...)
notation instead of legacy backticks. One important advantage is that the $(...)
notation allows for nested executions. test
and [
, ==
is just a convenience alias for =
. Use =
because it's more widely supported. If using, [[
, then indeed ==
has extra features like glob matching. But don't use [[
, it's not part of the POSIX standard."${@}"
as positional parameters to all the function and scripts invocations. if it gets too unmanageable or you need custom positional arguments then break your script into multiple scripts or handle all possible parameters and don't accept any ad-hoc parameters. eval
. It doesn't preserve original quoting. Have faith that there are always good alternatives.