From 4b1bc0f28e9ed00f8a4c6998f7af69d7b6a05560 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 24 Nov 2025 13:28:30 +0100 Subject: [PATCH 1/7] Container PyTest suite for mysql-container Migration matrix is here: - run_container_creation_tests -> test_container_configuration.py - run_configuration_tests -> test_container_configuration.py - run_general_tests -> test_container_general.py - run_change_password_test -> test_container_password.py - run_change_password_new_user_test -> test_container_password.py - run_replication_test -> Not Yet - run_s2i_test -> test_container_basics.py - run_ssl_test -> test_container_ssl.py - run_datadir_actions_test -> test_container_general.py Signed-off-by: Petr "Stone" Hracek --- test/conftest.py | 45 ++++ test/constants.py | 5 - test/run-pytest | 17 ++ test/test_container_basics.py | 113 +++++++++ test/test_container_configuration.py | 250 ++++++++++++++++++++ test/test_container_general.py | 207 ++++++++++++++++ test/test_container_password.py | 127 ++++++++++ test/test_container_ssl.py | 82 +++++++ test/test_ocp_mysql_latest_imagestreams.py | 4 +- test/test_ocp_mysql_local_template.py | 22 +- test/test_ocp_mysql_shared_helm_template.py | 32 +-- 11 files changed, 864 insertions(+), 40 deletions(-) create mode 100644 test/conftest.py delete mode 100644 test/constants.py create mode 100755 test/run-pytest create mode 100644 test/test_container_basics.py create mode 100644 test/test_container_configuration.py create mode 100644 test/test_container_general.py create mode 100644 test/test_container_password.py create mode 100644 test/test_container_ssl.py diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 00000000..d7cb40b7 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,45 @@ +import os +import sys + +from pathlib import Path +from collections import namedtuple + +from container_ci_suite.utils import check_variables + +if not check_variables(): + sys.exit(1) + +TAGS = { + "rhel8": "-el8", + "rhel9": "-el9", + "rhel10": "-el10", +} +TEST_DIR = Path(__file__).parent.absolute() +Vars = namedtuple( + "Vars", + [ + "OS", + "VERSION", + "IMAGE_NAME", + "TEST_DIR", + "TAG", + "TEST_APP", + "VERY_LONG_DB_NAME", + "VERY_LONG_USER_NAME", + ], +) +VERSION = os.getenv("VERSION") +OS = os.getenv("TARGET").lower() +TEST_APP = TEST_DIR / "test-app" +VERY_LONG_DB_NAME = "very_long_database_name_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +VERY_LONG_USER_NAME = "very_long_user_name_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +VARS = Vars( + OS=OS, + VERSION=VERSION, + IMAGE_NAME=os.getenv("IMAGE_NAME"), + TEST_DIR=Path(__file__).parent.absolute(), + TAG=TAGS.get(OS), + TEST_APP=TEST_APP, + VERY_LONG_DB_NAME=VERY_LONG_DB_NAME, + VERY_LONG_USER_NAME=VERY_LONG_USER_NAME, +) diff --git a/test/constants.py b/test/constants.py deleted file mode 100644 index 26eb71ae..00000000 --- a/test/constants.py +++ /dev/null @@ -1,5 +0,0 @@ -TAGS = { - "rhel8": "-el8", - "rhel9": "-el9", - "rhel10": "-el10", -} diff --git a/test/run-pytest b/test/run-pytest new file mode 100755 index 00000000..cc76ee7b --- /dev/null +++ b/test/run-pytest @@ -0,0 +1,17 @@ +#!/bin/bash +# +# IMAGE_NAME specifies a name of the candidate image used for testing. +# The image has to be available before this script is executed. +# VERSION specifies the major version of the MariaDB in format of X.Y +# OS specifies RHEL version (e.g. OS=rhel10) +# + +THISDIR=$(dirname ${BASH_SOURCE[0]}) + +git show -s + +PYTHON_VERSION="3.12" +if [[ ! -f "/usr/bin/python$PYTHON_VERSION" ]]; then + PYTHON_VERSION="3.13" +fi +cd "${THISDIR}" && "python${PYTHON_VERSION}" -m pytest -s -rA --showlocals -vv test_container_*.py diff --git a/test/test_container_basics.py b/test/test_container_basics.py new file mode 100644 index 00000000..ae034fb6 --- /dev/null +++ b/test/test_container_basics.py @@ -0,0 +1,113 @@ +import shutil +import tempfile + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper +from pathlib import Path + +from conftest import VARS + + +def build_s2i_app(app_path: Path) -> ContainerTestLib: + container_lib = ContainerTestLib(VARS.IMAGE_NAME) + app_name = app_path.name + s2i_app = container_lib.build_as_df( + app_path=app_path, + s2i_args="--pull-policy=never", + src_image=VARS.IMAGE_NAME, + dst_image=f"{VARS.IMAGE_NAME}-{app_name}", + ) + return s2i_app + + +class TestMySqlBasicsContainer: + """ + Test MySQL container configuration. + """ + + def setup_method(self): + self.s2i_db = build_s2i_app(app_path=VARS.TEST_DIR / "test-app") + self.s2i_db.set_new_db_type(db_type="mysql") + + def teardown_method(self): + self.s2i_db.cleanup() + + def test_s2i_usage(self): + """ + Test container creation fails with invalid combinations of arguments. + """ + cid_config_build = "s2i_config_build" + self.s2i_db.assert_container_creation_fails( + cid_file_name=cid_config_build, + command="", + container_args=[ + "-e MYSQL_USER=root", + "-e MYSQL_PASSWORD=pass", + "-e MYSQL_DATABASE=db", + "-e MYSQL_ROOT_PASSWORD=pass", + ], + ) + assert self.s2i_db.create_container( + cid_file_name=cid_config_build, + container_args=[ + "-e MYSQL_USER=config_test_user", + "-e MYSQL_PASSWORD=config_test_user", + "-e MYSQL_DATABASE=db", + "-e MYSQL_OPERATIONS_USER=operations_user", + "-e MYSQL_OPERATIONS_PASSWORD=operations_user", + ], + ) + cip = self.s2i_db.get_cip(cid_file_name=cid_config_build) + assert cip + assert self.s2i_db.test_db_connection( + container_ip=cip, username="operations_user", password="operations_user" + ) + cid = self.s2i_db.get_cid(cid_file_name=cid_config_build) + db_configuration = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="cat /etc/my.cnf /etc/my.cnf.d/*", + ) + assert db_configuration + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + + def test_s2i_usage_with_mount(self): + """ + Test container creation fails with invalid combinations of arguments. + """ + data_dir = tempfile.mkdtemp(prefix="/tmp/mysql-test_data") + shutil.copytree(VARS.TEST_DIR / "test-app", f"{data_dir}/test-app") + assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"chown -R 27:27 {data_dir}/test-app", + ] + ) + cid_s2i_test_mount = "s2i_test_mount" + self.s2i_db.create_container( + cid_file_name=cid_s2i_test_mount, + container_args=[ + "-e MYSQL_USER=config_test_user", + "-e MYSQL_PASSWORD=config_test_user", + "-e MYSQL_DATABASE=db", + "-e MYSQL_OPERATIONS_USER=operations_user", + "-e MYSQL_OPERATIONS_PASSWORD=operations_pass", + f"-v {data_dir}/test-app:/opt/app-root/src/:z", + ], + ) + cip_test_mount = self.s2i_db.get_cip(cid_file_name=cid_s2i_test_mount) + assert cip_test_mount + assert self.s2i_db.test_db_connection( + container_ip=cip_test_mount, + username="operations_user", + password="operations_pass", + max_attempts=10, + ) + cid = self.s2i_db.get_cid(cid_file_name=cid_s2i_test_mount) + assert cid + db_configuration = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="cat /etc/my.cnf /etc/my.cnf.d/*", + ) + assert db_configuration + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + shutil.rmtree(data_dir) diff --git a/test/test_container_configuration.py b/test/test_container_configuration.py new file mode 100644 index 00000000..db2eaddc --- /dev/null +++ b/test/test_container_configuration.py @@ -0,0 +1,250 @@ +import re + +import pytest + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper + +from conftest import VARS + + +class TestMySqlConfigurationContainer: + """ + Test MySQL container configuration. + """ + + def setup_method(self): + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME) + + def teardown_method(self): + self.db.cleanup() + + def test_container_creation_fails(self): + """ + Test container creation fails with no arguments. + """ + cid_config_test = "container_creation_fails" + assert self.db.assert_container_creation_fails( + cid_file_name=cid_config_test, container_args=[], command="" + ) + + @pytest.mark.parametrize( + "container_args", + [ + ["-e MYSQL_USER=user", "-e MYSQL_DATABASE=db"], + ["-e MYSQL_PASSWORD=pass", "-e MYSQL_DATABASE=db"], + ], + ) + def test_try_image_invalid_combinations(self, container_args): + """ + Test container creation fails with invalid combinations of arguments. + """ + cid_file_name = "try_image_invalid_combinations" + assert self.db.assert_container_creation_fails( + cid_file_name=cid_file_name, container_args=container_args, command="" + ) + + @pytest.mark.parametrize( + "container_args", + [ + ["-e", "MYSQL_USER=user", "-e", "MYSQL_PASSWORD=pass"], + [ + "-e MYSQL_USER=$invalid", + "-e MYSQL_PASSWORD=pass", + "-e MYSQL_DATABASE=db", + "-e MYSQL_ROOT_PASSWORD=root_pass", + ], + [ + f"-e MYSQL_USER={VARS.VERY_LONG_USER_NAME}", + "-e MYSQL_PASSWORD=pass", + "-e MYSQL_DATABASE=db", + "-e MYSQL_ROOT_PASSWORD=root_pass", + ], + [ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=", + "-e MYSQL_DATABASE=db", + "-e MYSQL_ROOT_PASSWORD=root_pass", + ], + [ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=pass", + "-e MYSQL_DATABASE=$invalid", + "-e MYSQL_ROOT_PASSWORD=root_pass", + ], + [ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=pass", + f"-e MYSQL_DATABASE={VARS.VERY_LONG_DB_NAME}", + "-e MYSQL_ROOT_PASSWORD=root_pass", + ], + [ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=pass", + "-e MYSQL_DATABASE=db", + "-e MYSQL_ROOT_PASSWORD=", + ], + [ + "-e MYSQL_USER=root", + "-e MYSQL_PASSWORD=pass", + "-e MYSQL_DATABASE=db", + "-e MYSQL_ROOT_PASSWORD=pass", + ], + ], + ) + def test_invalid_configuration_tests(self, container_args): + """ + Test invalid configuration combinations for MySQL container. + """ + cid_config_test = "invalid_configuration_tests" + assert self.db.assert_container_creation_fails( + cid_file_name=cid_config_test, container_args=container_args, command="" + ) + + +class TestMySqlConfigurationTests: + """ + Test MySQL container configuration tests. + """ + + def setup_method(self): + self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.db.set_new_db_type(db_type="mysql") + + def teardown_method(self): + self.db.cleanup() + + def test_configuration_auto_calculated_settings(self): + """ + Test MySQL container configuration auto-calculated settings. + """ + cid_config_test = "auto-config_test" + assert self.db.create_container( + cid_file_name=cid_config_test, + container_args=[ + "--env MYSQL_COLLATION=latin2_czech_cs", + "--env MYSQL_CHARSET=latin2", + "--env MYSQL_USER=config_test_user", + "--env MYSQL_PASSWORD=config_test", + "--env MYSQL_DATABASE=db", + ], + docker_args="--memory=512m", + ) + cip = self.db.get_cip(cid_file_name=cid_config_test) + assert cip + return_value = self.db.test_db_connection( + container_ip=cip, + username="config_test_user", + password="config_test", + max_attempts=10, + ) + assert return_value + cid = self.db.get_cid(cid_file_name=cid_config_test) + db_configuration = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="cat /etc/my.cnf /etc/my.cnf.d/*", + ) + assert db_configuration + assert re.search( + r"key_buffer_size\s*=\s*51M", + db_configuration, + ) + assert re.search( + r"read_buffer_size\s*=\s*25M", + db_configuration, + ) + assert re.search( + r"innodb_buffer_pool_size\s*=\s*256M", + db_configuration, + ) + assert re.search( + r"innodb_log_file_size\s*=\s*76M", + db_configuration, + ) + assert re.search( + r"innodb_log_buffer_size\s*=\s*76M", + db_configuration, + ) + assert re.search( + r"authentication_policy\s*=\s*'caching_sha2_password,,'", + db_configuration, + ) + + def test_configuration_options_settings(self): + """ + Test MySQL container configuration options. + """ + cid_config_test = "config_test" + assert self.db.create_container( + cid_file_name=cid_config_test, + container_args=[ + "--env MYSQL_USER=config_test_user", + "--env MYSQL_PASSWORD=config_test", + "--env MYSQL_DATABASE=db", + "--env MYSQL_LOWER_CASE_TABLE_NAMES=1", + "--env MYSQL_LOG_QUERIES_ENABLED=1", + "--env MYSQL_MAX_CONNECTIONS=1337", + "--env MYSQL_FT_MIN_WORD_LEN=8", + "--env MYSQL_FT_MAX_WORD_LEN=15", + "--env MYSQL_MAX_ALLOWED_PACKET=10M", + "--env MYSQL_TABLE_OPEN_CACHE=100", + "--env MYSQL_SORT_BUFFER_SIZE=256K", + "--env MYSQL_KEY_BUFFER_SIZE=16M", + "--env MYSQL_READ_BUFFER_SIZE=16M", + "--env MYSQL_INNODB_BUFFER_POOL_SIZE=16M", + "--env MYSQL_INNODB_LOG_FILE_SIZE=4M", + "--env MYSQL_INNODB_LOG_BUFFER_SIZE=4M", + "--env MYSQL_AUTHENTICATION_POLICY=sha256_password", + ], + ) + cip = self.db.get_cip(cid_file_name=cid_config_test) + assert cip + assert self.db.test_db_connection( + container_ip=cip, username="config_test_user", password="config_test" + ) + cip = self.db.get_cip(cid_file_name=cid_config_test) + assert cip + return_value = self.db.test_db_connection( + container_ip=cip, + username="config_test_user", + password="config_test", + max_attempts=10, + ) + assert return_value + cid = self.db.get_cid(cid_file_name=cid_config_test) + db_configuration = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="cat /etc/my.cnf /etc/my.cnf.d/*", + ) + assert db_configuration + assert re.search( + r"lower_case_table_names\s*=\s*1", + db_configuration, + ) + assert re.search( + r"general_log\s*=\s*1", + db_configuration, + ) + assert re.search( + r"max_connections\s*=\s*1337", + db_configuration, + re.MULTILINE | re.IGNORECASE, + ) + assert re.search(r"ft_min_word_len\s*=\s*8", db_configuration) + assert re.search( + r"ft_max_word_len\s*=\s*15", + db_configuration, + ) + assert re.search(r"max_allowed_packet\s*=\s*10M", db_configuration) + assert re.search(r"table_open_cache\s*=\s*100", db_configuration) + assert re.search(r"sort_buffer_size\s*=\s*256K", db_configuration) + assert re.search(r"key_buffer_size\s*=\s*16M", db_configuration) + assert re.search(r"read_buffer_size\s*=\s*16M", db_configuration) + assert re.search(r"innodb_buffer_pool_size\s*=\s*16M", db_configuration) + assert re.search(r"innodb_log_file_size\s*=\s*4M", db_configuration) + assert re.search(r"innodb_log_buffer_size\s*=\s*4M", db_configuration) + assert re.search( + r"authentication_policy\s*=\s*'sha256_password'", + db_configuration, + re.MULTILINE, + ) diff --git a/test/test_container_general.py b/test/test_container_general.py new file mode 100644 index 00000000..4685305d --- /dev/null +++ b/test/test_container_general.py @@ -0,0 +1,207 @@ +import re +import pytest +import tempfile + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper + +from conftest import VARS + + +class TestMySqlGeneralContainer: + """ + Test MySQL container configuration. + """ + + def setup_method(self): + self.s2i_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.s2i_db.set_new_db_type(db_type="mysql") + + def teardown_method(self): + self.s2i_db.cleanup() + + @pytest.mark.parametrize( + "docker_args, username, password, root_password", + [ + ("", "user", "pass", ""), + ("", "user1", "pass1", "r00t"), + ("--user 12345", "user", "pass", ""), + ("--user 12345", "user1", "pass1", "r00t"), + ], + ) + def test_run(self, docker_args, username, password, root_password): + """ + Test container creation fails with invalid combinations of arguments. + """ + root_password_arg = ( + f"-e MYSQL_ROOT_PASSWORD={root_password}" if root_password else "" + ) + cid_file_name = f"test_{username}_{password}_{root_password}" + assert self.s2i_db.create_container( + cid_file_name=cid_file_name, + container_args=[ + f"-e MYSQL_USER={username}", + f"-e MYSQL_PASSWORD={password}", + "-e MYSQL_DATABASE=db", + f"{root_password_arg}", + f"{docker_args}", + ], + command="run-mysqld --innodb_buffer_pool_size=5242880", + ) + cip = self.s2i_db.get_cip(cid_file_name=cid_file_name) + assert cip + assert self.s2i_db.test_db_connection( + container_ip=cip, username=username, password=password + ) + cid = self.s2i_db.get_cid(cid_file_name=cid_file_name) + output = PodmanCLIWrapper.podman_exec_shell_command( + cid_file_name=cid, + cmd="mysql --version", + ) + assert VARS.VERSION in output + self.s2i_db.db_lib.assert_login_access( + container_ip=cip, + username=username, + password=password, + expected_success=True, + ) + self.s2i_db.db_lib.assert_login_access( + container_ip=cip, + username=username, + password=f"{password}_foo", + expected_success=False, + ) + if root_password: + self.s2i_db.db_lib.assert_login_access( + container_ip=cip, + username="root", + password=root_password, + expected_success=True, + ) + self.s2i_db.db_lib.assert_login_access( + container_ip=cip, + username="root", + password=f"{root_password}_foo", + expected_success=False, + ) + else: + self.s2i_db.db_lib.assert_login_access( + container_ip=cip, + username="root", + password="foo", + expected_success=False, + ) + self.s2i_db.db_lib.assert_login_access( + container_ip=cip, + username="root", + password="", + expected_success=False, + ) + output = self.s2i_db.db_lib.assert_local_access(container_id=cid) + assert output + podman_cmd = ( + f"--rm {VARS.IMAGE_NAME} mysql --host {cip} -u{username} -p{password}" + ) + assert PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e 'CREATE TABLE tbl (col1 VARCHAR(20), col2 VARCHAR(20));' db", + ) + values = 'INSERT INTO tbl VALUES ("foo1", "bar1");' + assert PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e '{values}' db", + ) + values = 'INSERT INTO tbl VALUES ("foo2", "bar2");' + assert PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e '{values}' db", + ) + values = 'INSERT INTO tbl VALUES ("foo3", "bar3");' + assert PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e '{values}' db", + ) + output = PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e 'SELECT * FROM tbl;' db", + ) + assert re.search(r"foo1\t*bar1", output) + assert re.search(r"foo2\t*bar2", output) + assert re.search(r"foo3\t*bar3", output) + PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e 'DROP TABLE tbl;' db", + ) + + def test_datadir_actions(self): + """ + Test container creation fails with invalid combinations of arguments. + """ + cid_testupg1 = "testupg1" + datadir = tempfile.mkdtemp(prefix="/tmp/mysql-datadir-actions") + assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"mkdir -p {datadir}/data", + f"chmod -R a+rwx {datadir}", + ] + ) + assert self.s2i_db.create_container( + cid_file_name=cid_testupg1, + container_args=[ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=foo", + "-e MYSQL_DATABASE=db", + f"-v {datadir}/data:/var/lib/mysql/data:Z", + ], + ) + cip = self.s2i_db.get_cip(cid_file_name=cid_testupg1) + assert cip + assert self.s2i_db.test_db_connection( + container_ip=cip, username="user", password="foo" + ) + cid = self.s2i_db.get_cid(cid_file_name=cid_testupg1) + assert cid + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + + cid_testupg5 = "testupg5" + assert self.s2i_db.create_container( + cid_file_name=cid_testupg5, + container_args=[ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=foo", + "-e MYSQL_DATABASE=db", + f"-v {datadir}/data:/var/lib/mysql/data:Z", + "-e MYSQL_DATADIR_ACTION=analyze", + ], + ) + cip = self.s2i_db.get_cip(cid_file_name=cid_testupg5) + assert cip + assert self.s2i_db.test_db_connection( + container_ip=cip, username="user", password="foo" + ) + cid = self.s2i_db.get_cid(cid_file_name=cid_testupg5) + assert cid + output = PodmanCLIWrapper.podman_logs( + container_id=cid, + ) + assert re.search(r"--analyze --all-databases", output) + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + + cid_testupg6 = "testupg6" + assert self.s2i_db.create_container( + cid_file_name=cid_testupg6, + container_args=[ + "-e MYSQL_USER=user", + "-e MYSQL_PASSWORD=foo", + "-e MYSQL_DATABASE=db", + f"-v {datadir}/data:/var/lib/mysql/data:Z", + "-e MYSQL_DATADIR_ACTION=optimize", + ], + ) + cip = self.s2i_db.get_cip(cid_file_name=cid_testupg6) + assert cip + assert self.s2i_db.test_db_connection( + container_ip=cip, username="user", password="foo" + ) + cid = self.s2i_db.get_cid(cid_file_name=cid_testupg6) + assert cid + output = PodmanCLIWrapper.podman_logs( + container_id=cid, + ) + assert re.search(r"--optimize --all-databases", output) + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") diff --git a/test/test_container_password.py b/test/test_container_password.py new file mode 100644 index 00000000..89bfaf5e --- /dev/null +++ b/test/test_container_password.py @@ -0,0 +1,127 @@ +import tempfile + +from container_ci_suite.container_lib import ContainerTestLib, DatabaseWrapper +from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper + +from conftest import VARS + + +class TestMySqlPasswordContainer: + """ + Test MySQL container configuration. + """ + + def setup_method(self): + self.ssl_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.ssl_db.set_new_db_type(db_type="mysql") + self.db_connector = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="mysql") + + def teardown_method(self): + self.ssl_db.cleanup() + + def test_password_change(self): + """ """ + cid_file_name = "test_password_change" + pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") + username = "user" + password = "foo" + assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"chmod -R a+rwx {pwd_dir}", + ] + ) + assert self.ssl_db.create_container( + cid_file_name=cid_file_name, + container_args=[ + f"-e MYSQL_USER={username}", + f"-e MYSQL_PASSWORD={password}", + "-e MYSQL_DATABASE=db", + f"-v {pwd_dir}:/var/lib/mysql/data:Z", + ], + ) + cip = self.ssl_db.get_cip(cid_file_name=cid_file_name) + assert cip + assert self.ssl_db.test_db_connection( + container_ip=cip, username=username, password=password + ) + cid = self.ssl_db.get_cid(cid_file_name=cid_file_name) + assert cid + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + cid_file_name = "test_password_change_2" + new_password = "bar" + assert self.ssl_db.create_container( + cid_file_name=cid_file_name, + container_args=[ + f"-e MYSQL_USER={username}", + f"-e MYSQL_PASSWORD={new_password}", + "-e MYSQL_DATABASE=db", + f"-v {pwd_dir}:/var/lib/mysql/data:Z", + ], + ) + podman_cmd = ( + f"--rm {VARS.IMAGE_NAME} mysql --host {cip} -u{username} -p{password}" + ) + output = PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e 'SELECT 1;' db", + ) + assert output == "1" + + def test_password_change_new_user_test(self): + """ """ + cid_file_name = "test_password_change1" + pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") + username1 = "user" + password1 = "foo" + assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"chmod -R a+rwx {pwd_dir}", + ] + ) + assert self.ssl_db.create_container( + cid_file_name=cid_file_name, + container_args=[ + f"-e MYSQL_USER={username1}", + f"-e MYSQL_PASSWORD={password1}", + "-e MYSQL_DATABASE=db", + f"-v {pwd_dir}:/var/lib/mysql/data:Z", + ], + ) + cip = self.ssl_db.get_cip(cid_file_name=cid_file_name) + assert cip + assert self.ssl_db.test_db_connection( + container_ip=cip, username=username1, password=password1 + ) + cid = self.ssl_db.get_cid(cid_file_name=cid_file_name) + assert cid + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + cid_file_name = "test_password_change2" + username2 = "user2" + password2 = "bar" + # Create second container with changed password + assert self.ssl_db.create_container( + cid_file_name=cid_file_name, + container_args=[ + f"-e MYSQL_USER={username2}", + f"-e MYSQL_PASSWORD={password2}", + "-e MYSQL_DATABASE=db", + f"-v {pwd_dir}:/var/lib/mysql/data:Z", + ], + ) + cip2 = self.ssl_db.get_cip(cid_file_name=cid_file_name) + assert cip2 + assert self.ssl_db.test_db_connection( + container_ip=cip2, username=username1, password=password1 + ) + cid2 = self.ssl_db.get_cid(cid_file_name=cid_file_name) + mysql_logs = PodmanCLIWrapper.podman_logs( + conatiner_id=cid2, + ) + assert "User user2 does not exist in database" in mysql_logs + podman_cmd = ( + f"--rm {VARS.IMAGE_NAME} mysql --host {cip2} -u{username1} -p{password2}" + ) + output = PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e 'SELECT 1;' db", + ) + assert output == "1" diff --git a/test/test_container_ssl.py b/test/test_container_ssl.py new file mode 100644 index 00000000..02ef17b9 --- /dev/null +++ b/test/test_container_ssl.py @@ -0,0 +1,82 @@ +import tempfile +import re + +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper + +from conftest import VARS + + +class TestMySqlGeneralContainer: + """ + Test MySQL container configuration. + """ + + def setup_method(self): + self.ssl_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.ssl_db.set_new_db_type(db_type="mysql") + + def teardown_method(self): + self.ssl_db.cleanup() + + def test_ssl(self): + """ """ + ssl_dir = tempfile.mkdtemp(prefix="/tmp/mysql-ssl_data") + username = "ssl_test_user" + password = "ssl_test" + with open(f"{ssl_dir}/ssl.cnf", mode="wt+") as f: + lines = [ + "[mysqld]", + "ssl-key=${APP_DATA}/mysql-certs/server-key.pem", + "ssl-cert=${APP_DATA}/mysql-certs/server-cert-selfsigned.pem", + ] + f.write("\n".join(lines)) + server_key_pem = f"{ssl_dir}/server-key.pem" + server_req_pem = f"{ssl_dir}/server-req.pem" + server_cert_selfsigned_pem = f"{ssl_dir}/server-cert-selfsigned.pem" + ContainerTestLibUtils.run_command( + cmd=f"openssl req -newkey rsa:2048 -nodes -keyout {server_key_pem} -subj " + + f"/C=GB/ST=Berkshire/L=Newbury/O=My Server Company' > {server_req_pem}" + ) + ContainerTestLibUtils.run_command( + cmd=f"openssl req -new -x509 -nodes -key {server_key_pem} -batch > " + + f"{server_cert_selfsigned_pem}" + ) + assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"mkdir -p {ssl_dir}/mysql-certs {ssl_dir}/mysql-cfg", + f"cp {ssl_dir}/server-cert-selfsigned.pem {ssl_dir}/mysql-certs/server-cert-selfsigned.pem", + f"cp {ssl_dir}/server-key.pem {ssl_dir}/mysql-certs/server-key.pem", + f"cp {ssl_dir}/ssl.cnf {ssl_dir}/mysql-cfg/ssl.cnf", + f"chown -R 27:27 {ssl_dir}", + ] + ) + + ca_cert_path = "/opt/app-root/src/mysql-certs/server-cert-selfsigned.pem" + cid_file_name = "s2i_test_ssl" + assert self.ssl_db.create_container( + cid_file_name=cid_file_name, + container_args=[ + f"-e MYSQL_USER={username}", + f"-e MYSQL_PASSWORD={password}", + "-e MYSQL_DATABASE=db", + f"-v {ssl_dir}:/opt/app-root/src/:z", + ], + ) + cip = self.ssl_db.get_cip(cid_file_name=cid_file_name) + assert cip + assert self.ssl_db.test_db_connection( + container_ip=cip, username=username, password=password + ) + cid = self.ssl_db.get_cid(cid_file_name=cid_file_name) + assert cid + + mysql_cmd = ( + f"mysql --host {cip} -u{username} -p{password} --ssl-ca={ca_cert_path}" + + "-e 'show status like \"Ssl_cipher\" \\G' db" + ) + ssl_output = PodmanCLIWrapper.podman_run_command( + cmd=f"--rm -v {ssl_dir}:/opt/app-root/src/:z {VARS.IMAGE_NAME} {mysql_cmd}", + ) + assert re.search(r"Value: [A-Z][A-Z0-9-]*", ssl_output) diff --git a/test/test_ocp_mysql_latest_imagestreams.py b/test/test_ocp_mysql_latest_imagestreams.py index b30c384b..fa460341 100644 --- a/test/test_ocp_mysql_latest_imagestreams.py +++ b/test/test_ocp_mysql_latest_imagestreams.py @@ -1,6 +1,5 @@ import os import sys -import pytest from pathlib import Path @@ -15,11 +14,10 @@ class TestLatestImagestreams: - def setup_method(self): self.isc = ImageStreamChecker(working_dir=TEST_DIR.parent) def test_latest_imagestream(self): self.latest_version = self.isc.get_latest_version() - assert self.latest_version != "" + assert self.latest_version self.isc.check_imagestreams(self.latest_version) diff --git a/test/test_ocp_mysql_local_template.py b/test/test_ocp_mysql_local_template.py index fee611ec..6beb4874 100644 --- a/test/test_ocp_mysql_local_template.py +++ b/test/test_ocp_mysql_local_template.py @@ -7,6 +7,7 @@ from container_ci_suite.utils import check_variables from constants import TAGS + if not check_variables(): print("At least one variable from IMAGE_NAME, OS, VERSION is missing.") sys.exit(1) @@ -21,20 +22,17 @@ class TestMySQLDeployTemplate: - def setup_method(self): - self.oc_api = OpenShiftAPI(pod_name_prefix="mysql-testing", version=VERSION, shared_cluster=True) + self.oc_api = OpenShiftAPI( + pod_name_prefix="mysql-testing", version=VERSION, shared_cluster=True + ) self.oc_api.import_is("imagestreams/mysql-rhel.json", "", skip_check=True) def teardown_method(self): self.oc_api.delete_project() @pytest.mark.parametrize( - "template", - [ - "mysql-ephemeral-template.json", - "mysql-persistent-template.json" - ] + "template", ["mysql-ephemeral-template.json", "mysql-persistent-template.json"] ) def test_template_inside_cluster(self, template): short_version = VERSION.replace(".", "") @@ -45,10 +43,10 @@ def test_template_inside_cluster(self, template): openshift_args=[ f"MYSQL_VERSION={VERSION}{TAG}", f"DATABASE_SERVICE_NAME={self.oc_api.pod_name_prefix}", - f"MYSQL_USER=testu", - f"MYSQL_PASSWORD=testp", - f"MYSQL_DATABASE=testdb" - ] + "MYSQL_USER=testu", + "MYSQL_PASSWORD=testp", + "MYSQL_DATABASE=testdb", + ], ) assert self.oc_api.is_pod_running(pod_name_prefix=self.oc_api.pod_name_prefix) @@ -56,5 +54,5 @@ def test_template_inside_cluster(self, template): image_name=f"registry.redhat.io/{OS}/mysql-{short_version}", service_name=self.oc_api.pod_name_prefix, cmd="echo 'SELECT 42 as testval\\g' | mysql --connect-timeout=15 -h testdb -utestu -ptestp", - expected_output="42" + expected_output="42", ) diff --git a/test/test_ocp_mysql_shared_helm_template.py b/test/test_ocp_mysql_shared_helm_template.py index c80d0e1e..31c5046a 100644 --- a/test/test_ocp_mysql_shared_helm_template.py +++ b/test/test_ocp_mysql_shared_helm_template.py @@ -1,29 +1,21 @@ -import os - -import pytest -from pathlib import Path - from container_ci_suite.helm import HelmChartsAPI -from constants import TAGS -test_dir = Path(os.path.abspath(os.path.dirname(__file__))) - -VERSION = os.getenv("VERSION") -IMAGE_NAME = os.getenv("IMAGE_NAME") -OS = os.getenv("TARGET") +from conftest import VARS -TAG = TAGS.get(OS) - class TestHelmMySQLDBPersistent: - def setup_method(self): package_name = "redhat-mysql-persistent" - path = test_dir - self.hc_api = HelmChartsAPI(path=path, package_name=package_name, tarball_dir=test_dir, shared_cluster=True) + self.hc_api = HelmChartsAPI( + path=VARS.TEST_DIR, + package_name=package_name, + tarball_dir=VARS.TEST_DIR, + shared_cluster=True, + ) self.hc_api.clone_helm_chart_repo( - repo_url="https://github.com/sclorg/helm-charts", repo_name="helm-charts", - subdir="charts/redhat" + repo_url="https://github.com/sclorg/helm-charts", + repo_name="helm-charts", + subdir="charts/redhat", ) def teardown_method(self): @@ -37,9 +29,9 @@ def test_package_persistent(self): assert self.hc_api.helm_package() assert self.hc_api.helm_installation( values={ - "mysql_version": f"{VERSION}{TAG}", + "mysql_version": f"{VARS.VERSION}{VARS.TAG}", "namespace": self.hc_api.namespace, - "database_service_name": "mysql" + "database_service_name": "mysql", } ) assert self.hc_api.is_pod_running(pod_name_prefix="mysql") From cfd849da7ea690ade38a17cbe738778f75654a0e Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 1 Dec 2025 12:59:17 +0100 Subject: [PATCH 2/7] Add container tests including replication Signed-off-by: Petr "Stone" Hracek --- test/run | 1 - test/test_container_password.py | 73 +++++++++++--------- test/test_container_replication.py | 104 +++++++++++++++++++++++++++++ test/test_container_ssl.py | 17 ++--- 4 files changed, 155 insertions(+), 40 deletions(-) create mode 100644 test/test_container_replication.py diff --git a/test/run b/test/run index d233e106..1664318c 100755 --- a/test/run +++ b/test/run @@ -8,7 +8,6 @@ set -o nounset shopt -s nullglob - THISDIR=$(dirname ${BASH_SOURCE[0]}) source ${THISDIR}/test-lib.sh diff --git a/test/test_container_password.py b/test/test_container_password.py index 89bfaf5e..a18be6d7 100644 --- a/test/test_container_password.py +++ b/test/test_container_password.py @@ -13,16 +13,15 @@ class TestMySqlPasswordContainer: """ def setup_method(self): - self.ssl_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) - self.ssl_db.set_new_db_type(db_type="mysql") - self.db_connector = DatabaseWrapper(image_name=VARS.IMAGE_NAME, db_type="mysql") + self.pwd_change = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.pwd_change.set_new_db_type(db_type="mysql") def teardown_method(self): - self.ssl_db.cleanup() + self.pwd_change.cleanup() def test_password_change(self): """ """ - cid_file_name = "test_password_change" + cid_file_name1 = "test_password_change" pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") username = "user" password = "foo" @@ -31,8 +30,8 @@ def test_password_change(self): f"chmod -R a+rwx {pwd_dir}", ] ) - assert self.ssl_db.create_container( - cid_file_name=cid_file_name, + assert self.pwd_change.create_container( + cid_file_name=cid_file_name1, container_args=[ f"-e MYSQL_USER={username}", f"-e MYSQL_PASSWORD={password}", @@ -40,18 +39,18 @@ def test_password_change(self): f"-v {pwd_dir}:/var/lib/mysql/data:Z", ], ) - cip = self.ssl_db.get_cip(cid_file_name=cid_file_name) - assert cip - assert self.ssl_db.test_db_connection( - container_ip=cip, username=username, password=password + cip1 = self.pwd_change.get_cip(cid_file_name=cid_file_name1) + assert cip1 + assert self.pwd_change.test_db_connection( + container_ip=cip1, username=username, password=password ) - cid = self.ssl_db.get_cid(cid_file_name=cid_file_name) - assert cid - PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") - cid_file_name = "test_password_change_2" + cid1 = self.pwd_change.get_cid(cid_file_name=cid_file_name1) + assert cid1 + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid1}") + cid_file_name2 = "test_password_change_2" new_password = "bar" - assert self.ssl_db.create_container( - cid_file_name=cid_file_name, + assert self.pwd_change.create_container( + cid_file_name=cid_file_name2, container_args=[ f"-e MYSQL_USER={username}", f"-e MYSQL_PASSWORD={new_password}", @@ -59,13 +58,22 @@ def test_password_change(self): f"-v {pwd_dir}:/var/lib/mysql/data:Z", ], ) + cip2 = self.pwd_change.get_cip(cid_file_name=cid_file_name2) + assert cip2 + assert self.pwd_change.test_db_connection( + container_ip=cip2, username=username, password=new_password + ) podman_cmd = ( - f"--rm {VARS.IMAGE_NAME} mysql --host {cip} -u{username} -p{password}" + f"--rm {VARS.IMAGE_NAME} mysql --host {cip2} -u{username} -p{password}" ) output = PodmanCLIWrapper.podman_run_command( cmd=f"{podman_cmd} -e 'SELECT 1;' db", + ignore_error=True, + ) + print(output) + assert f"Access denied for user '{username}'@" in output, ( + f"The old password {password} should not work, but it does" ) - assert output == "1" def test_password_change_new_user_test(self): """ """ @@ -78,7 +86,7 @@ def test_password_change_new_user_test(self): f"chmod -R a+rwx {pwd_dir}", ] ) - assert self.ssl_db.create_container( + assert self.pwd_change.create_container( cid_file_name=cid_file_name, container_args=[ f"-e MYSQL_USER={username1}", @@ -87,19 +95,19 @@ def test_password_change_new_user_test(self): f"-v {pwd_dir}:/var/lib/mysql/data:Z", ], ) - cip = self.ssl_db.get_cip(cid_file_name=cid_file_name) - assert cip - assert self.ssl_db.test_db_connection( - container_ip=cip, username=username1, password=password1 + cip1 = self.pwd_change.get_cip(cid_file_name=cid_file_name) + assert cip1 + assert self.pwd_change.test_db_connection( + container_ip=cip1, username=username1, password=password1 ) - cid = self.ssl_db.get_cid(cid_file_name=cid_file_name) + cid = self.pwd_change.get_cid(cid_file_name=cid_file_name) assert cid PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") cid_file_name = "test_password_change2" username2 = "user2" password2 = "bar" # Create second container with changed password - assert self.ssl_db.create_container( + assert self.pwd_change.create_container( cid_file_name=cid_file_name, container_args=[ f"-e MYSQL_USER={username2}", @@ -108,14 +116,14 @@ def test_password_change_new_user_test(self): f"-v {pwd_dir}:/var/lib/mysql/data:Z", ], ) - cip2 = self.ssl_db.get_cip(cid_file_name=cid_file_name) + cip2 = self.pwd_change.get_cip(cid_file_name=cid_file_name) assert cip2 - assert self.ssl_db.test_db_connection( + assert self.pwd_change.test_db_connection( container_ip=cip2, username=username1, password=password1 ) - cid2 = self.ssl_db.get_cid(cid_file_name=cid_file_name) + cid2 = self.pwd_change.get_cid(cid_file_name=cid_file_name) mysql_logs = PodmanCLIWrapper.podman_logs( - conatiner_id=cid2, + container_id=cid2, ) assert "User user2 does not exist in database" in mysql_logs podman_cmd = ( @@ -123,5 +131,8 @@ def test_password_change_new_user_test(self): ) output = PodmanCLIWrapper.podman_run_command( cmd=f"{podman_cmd} -e 'SELECT 1;' db", + ignore_error=True, + ) + assert f"Access denied for user '{username1}'@" in output, ( + f"The new password {password2} should not work, but it does" ) - assert output == "1" diff --git a/test/test_container_replication.py b/test/test_container_replication.py new file mode 100644 index 00000000..5873723f --- /dev/null +++ b/test/test_container_replication.py @@ -0,0 +1,104 @@ +import tempfile +import re +from time import sleep + +from container_ci_suite.container_lib import ContainerTestLib, DatabaseWrapper +from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper + +from conftest import VARS + + +class TestMySqlReplicationContainer: + """ + Test MySQL container configuration. + """ + + def setup_method(self): + self.replication_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.replication_db.set_new_db_type(db_type="mysql") + + def teardown_method(self): + self.replication_db.cleanup() + + def test_replication(self): + """ """ + cluster_args = "-e MYSQL_SOURCE_USER=source -e MYSQL_SOURCE_PASSWORD=source -e MYSQL_DATABASE=db" + source_cid = "source.cid" + username = "user" + password = "foo" + # Run the MySQL source + assert self.replication_db.create_container( + cid_file_name=source_cid, + container_args=[ + f"-e MYSQL_USER={username}", + f"-e MYSQL_PASSWORD={password}", + "-e MYSQL_ROOT_PASSWORD=root", + "-e MYSQL_INNODB_BUFFER_POOL_SIZE=5M", + ], + docker_args=cluster_args, + command="run-mysqld-source", + ) + source_cip = self.replication_db.get_cip(cid_file_name=source_cid) + print(f"Source IP: {source_cip}") + source_cid = self.replication_db.get_cid(cid_file_name=source_cid) + assert source_cid + # Run the MySQL replica + replica_cid = "replica.cid" + assert self.replication_db.create_container( + cid_file_name=replica_cid, + container_args=[ + f"-e MYSQL_SOURCE_SERVICE_NAME={source_cip}", + "-e MYSQL_INNODB_BUFFER_POOL_SIZE=5M", + ], + docker_args=cluster_args, + command="run-mysqld-replica", + ) + replica_cip = self.replication_db.get_cip(cid_file_name=replica_cid) + assert replica_cip + replica_cid = self.replication_db.get_cid(cid_file_name=replica_cid) + assert replica_cid + print(f"Replica IP: {replica_cip}") + # Now wait till the SOURCE will see the REPLICA + result = self.replication_db.test_db_connection( + container_ip=replica_cip, + username=username, + password=password, + ) + result = PodmanCLIWrapper.call_podman_command( + cmd=f"exec {source_cid} mysql -e 'SHOW REPLICAS;' db", + ignore_error=True, + debug=True, + ) + print(f"Showing replicas: {result}") + assert replica_cip in result, ( + f"Replica {replica_cip} not found in SOURCE {source_cip}" + ) + # do some real work to test replication in practice + result = self.replication_db.test_db_connection( + container_ip=source_cip, + username=username, + password=password, + max_attempts=120, + sql_cmd="-e 'CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (24);'", + ) + print(f"Creating table: {result}") + # let's wait for the table to be created and available for replication + sleep(3) + + result = self.replication_db.test_db_connection( + container_ip=replica_cip, + username=username, + password=password, + max_attempts=120, + sql_cmd="-e 'select * from t1;'", + ) + assert result + podman_cmd = f"--rm {VARS.IMAGE_NAME} mysql --host {replica_cip} -u{username} -p{password}" + table_output = PodmanCLIWrapper.podman_run_command( + cmd=f"{podman_cmd} -e 'select * from t1;' db", + ) + print(f"Selecting from table: {table_output}") + assert re.search(r"^a\n^24", table_output.strip(), re.MULTILINE), ( + f"Replica {replica_cip} did not get value from SOURCE {source_cip}" + ) diff --git a/test/test_container_ssl.py b/test/test_container_ssl.py index 02ef17b9..5aa1f55f 100644 --- a/test/test_container_ssl.py +++ b/test/test_container_ssl.py @@ -32,16 +32,17 @@ def test_ssl(self): "ssl-cert=${APP_DATA}/mysql-certs/server-cert-selfsigned.pem", ] f.write("\n".join(lines)) - server_key_pem = f"{ssl_dir}/server-key.pem" - server_req_pem = f"{ssl_dir}/server-req.pem" - server_cert_selfsigned_pem = f"{ssl_dir}/server-cert-selfsigned.pem" + srv_key_pem = f"{ssl_dir}/server-key.pem" + srv_req_pem = f"{ssl_dir}/server-req.pem" + srv_self_pem = f"{ssl_dir}/server-cert-selfsigned.pem" + openssl_cmd = "openssl req -newkey rsa:2048 -nodes" + openssl_cmd_new = "openssl req -new -x509 -nodes" + subj = "/C=GB/ST=Berkshire/L=Newbury/O=My Server Company" ContainerTestLibUtils.run_command( - cmd=f"openssl req -newkey rsa:2048 -nodes -keyout {server_key_pem} -subj " - + f"/C=GB/ST=Berkshire/L=Newbury/O=My Server Company' > {server_req_pem}" + cmd=f"{openssl_cmd} -keyout {srv_key_pem} -subj '{subj}' > {srv_req_pem}" ) ContainerTestLibUtils.run_command( - cmd=f"openssl req -new -x509 -nodes -key {server_key_pem} -batch > " - + f"{server_cert_selfsigned_pem}" + cmd=f"{openssl_cmd_new} -key {srv_key_pem} -batch > {srv_self_pem}" ) assert ContainerTestLibUtils.commands_to_run( commands_to_run=[ @@ -74,7 +75,7 @@ def test_ssl(self): mysql_cmd = ( f"mysql --host {cip} -u{username} -p{password} --ssl-ca={ca_cert_path}" - + "-e 'show status like \"Ssl_cipher\" \\G' db" + + " -e 'show status like \"Ssl_cipher\" \\G' db" ) ssl_output = PodmanCLIWrapper.podman_run_command( cmd=f"--rm -v {ssl_dir}:/opt/app-root/src/:z {VARS.IMAGE_NAME} {mysql_cmd}", From cf87fd80ef062c21b15c772d4a823d35dab4eec7 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Fri, 12 Dec 2025 12:49:27 +0100 Subject: [PATCH 3/7] Use class DatabaseWrapper to call sql commands directly Check configuration files in one command instead of several ones. Signed-off-by: Petr "Stone" Hracek --- test/test_container_basics.py | 5 - test/test_container_configuration.py | 169 ++++++++++++++------------- test/test_container_general.py | 142 ++++++++++++---------- test/test_container_password.py | 39 ++++--- test/test_container_replication.py | 61 +++++----- test/test_container_ssl.py | 12 +- 6 files changed, 238 insertions(+), 190 deletions(-) diff --git a/test/test_container_basics.py b/test/test_container_basics.py index ae034fb6..e17edcaf 100644 --- a/test/test_container_basics.py +++ b/test/test_container_basics.py @@ -104,10 +104,5 @@ def test_s2i_usage_with_mount(self): ) cid = self.s2i_db.get_cid(cid_file_name=cid_s2i_test_mount) assert cid - db_configuration = PodmanCLIWrapper.podman_exec_shell_command( - cid_file_name=cid, - cmd="cat /etc/my.cnf /etc/my.cnf.d/*", - ) - assert db_configuration PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") shutil.rmtree(data_dir) diff --git a/test/test_container_configuration.py b/test/test_container_configuration.py index db2eaddc..08094880 100644 --- a/test/test_container_configuration.py +++ b/test/test_container_configuration.py @@ -3,6 +3,7 @@ import pytest from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.database import DatabaseWrapper from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper from conftest import VARS @@ -14,9 +15,15 @@ class TestMySqlConfigurationContainer: """ def setup_method(self): + """ + Setup the test environment. + """ self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME) def teardown_method(self): + """ + Teardown the test environment. + """ self.db.cleanup() def test_container_creation_fails(self): @@ -108,78 +115,95 @@ class TestMySqlConfigurationTests: """ def setup_method(self): - self.db = ContainerTestLib(image_name=VARS.IMAGE_NAME) - self.db.set_new_db_type(db_type="mysql") + """ + Setup the test environment. + """ + self.db_config = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.db_config.set_new_db_type(db_type="mysql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): - self.db.cleanup() + """ + Teardown the test environment. + """ + self.db_config.cleanup() def test_configuration_auto_calculated_settings(self): """ Test MySQL container configuration auto-calculated settings. """ cid_config_test = "auto-config_test" - assert self.db.create_container( + username = "config_test_user" + password = "config_test" + assert self.db_config.create_container( cid_file_name=cid_config_test, container_args=[ "--env MYSQL_COLLATION=latin2_czech_cs", "--env MYSQL_CHARSET=latin2", - "--env MYSQL_USER=config_test_user", - "--env MYSQL_PASSWORD=config_test", + f"--env MYSQL_USER={username}", + f"--env MYSQL_PASSWORD={password}", "--env MYSQL_DATABASE=db", ], docker_args="--memory=512m", ) - cip = self.db.get_cip(cid_file_name=cid_config_test) + cip = self.db_config.get_cip(cid_file_name=cid_config_test) assert cip - return_value = self.db.test_db_connection( + assert self.db_config.test_db_connection( container_ip=cip, - username="config_test_user", - password="config_test", + username=username, + password=password, max_attempts=10, ) - assert return_value - cid = self.db.get_cid(cid_file_name=cid_config_test) + cid = self.db_config.get_cid(cid_file_name=cid_config_test) db_configuration = PodmanCLIWrapper.podman_exec_shell_command( cid_file_name=cid, cmd="cat /etc/my.cnf /etc/my.cnf.d/*", ) - assert db_configuration - assert re.search( - r"key_buffer_size\s*=\s*51M", - db_configuration, - ) - assert re.search( - r"read_buffer_size\s*=\s*25M", - db_configuration, - ) - assert re.search( - r"innodb_buffer_pool_size\s*=\s*256M", - db_configuration, - ) - assert re.search( - r"innodb_log_file_size\s*=\s*76M", - db_configuration, - ) - assert re.search( - r"innodb_log_buffer_size\s*=\s*76M", - db_configuration, + words = [ + "key_buffer_size\\s*=\\s*51M", + "read_buffer_size\\s*=\\s*25M", + "innodb_buffer_pool_size\\s*=\\s*256M", + "innodb_log_file_size\\s*=\\s*76M", + "innodb_log_buffer_size\\s*=\\s*76M", + "authentication_policy\\s*=\\s*'caching_sha2_password,,'", + ] + for word in words: + assert re.search(word, db_configuration), ( + f"Word {word} not found in {db_configuration}" + ) + # do some real work to test replication in practice + self.db_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=cid, + sql_cmd="CREATE TABLE tbl (col VARCHAR(20));", + podman_run_command="exec", ) - assert re.search( - r"authentication_policy\s*=\s*'caching_sha2_password,,'", - db_configuration, + show_table_output = self.db_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=cid, + sql_cmd="SHOW CREATE TABLE tbl;", + podman_run_command="exec", ) + assert "CHARSET=latin2" in show_table_output + assert "COLLATE=latin2_czech_cs" in show_table_output + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") def test_configuration_options_settings(self): """ Test MySQL container configuration options. """ cid_config_test = "config_test" - assert self.db.create_container( + username = "config_test_user" + password = "config_test" + assert self.db_config.create_container( cid_file_name=cid_config_test, container_args=[ - "--env MYSQL_USER=config_test_user", - "--env MYSQL_PASSWORD=config_test", + f"--env MYSQL_USER={username}", + f"--env MYSQL_PASSWORD={password}", "--env MYSQL_DATABASE=db", "--env MYSQL_LOWER_CASE_TABLE_NAMES=1", "--env MYSQL_LOG_QUERIES_ENABLED=1", @@ -197,54 +221,41 @@ def test_configuration_options_settings(self): "--env MYSQL_AUTHENTICATION_POLICY=sha256_password", ], ) - cip = self.db.get_cip(cid_file_name=cid_config_test) + cip = self.db_config.get_cip(cid_file_name=cid_config_test) assert cip - assert self.db.test_db_connection( - container_ip=cip, username="config_test_user", password="config_test" + assert self.db_config.test_db_connection( + container_ip=cip, username=username, password=password ) - cip = self.db.get_cip(cid_file_name=cid_config_test) + cip = self.db_config.get_cip(cid_file_name=cid_config_test) assert cip - return_value = self.db.test_db_connection( + assert self.db_config.test_db_connection( container_ip=cip, - username="config_test_user", - password="config_test", + username=username, + password=password, max_attempts=10, ) - assert return_value - cid = self.db.get_cid(cid_file_name=cid_config_test) + cid = self.db_config.get_cid(cid_file_name=cid_config_test) db_configuration = PodmanCLIWrapper.podman_exec_shell_command( cid_file_name=cid, cmd="cat /etc/my.cnf /etc/my.cnf.d/*", ) - assert db_configuration - assert re.search( - r"lower_case_table_names\s*=\s*1", - db_configuration, - ) - assert re.search( - r"general_log\s*=\s*1", - db_configuration, - ) - assert re.search( - r"max_connections\s*=\s*1337", - db_configuration, - re.MULTILINE | re.IGNORECASE, - ) - assert re.search(r"ft_min_word_len\s*=\s*8", db_configuration) - assert re.search( - r"ft_max_word_len\s*=\s*15", - db_configuration, - ) - assert re.search(r"max_allowed_packet\s*=\s*10M", db_configuration) - assert re.search(r"table_open_cache\s*=\s*100", db_configuration) - assert re.search(r"sort_buffer_size\s*=\s*256K", db_configuration) - assert re.search(r"key_buffer_size\s*=\s*16M", db_configuration) - assert re.search(r"read_buffer_size\s*=\s*16M", db_configuration) - assert re.search(r"innodb_buffer_pool_size\s*=\s*16M", db_configuration) - assert re.search(r"innodb_log_file_size\s*=\s*4M", db_configuration) - assert re.search(r"innodb_log_buffer_size\s*=\s*4M", db_configuration) - assert re.search( - r"authentication_policy\s*=\s*'sha256_password'", - db_configuration, - re.MULTILINE, - ) + words = [ + "lower_case_table_names\\s*=\\s*1", + "general_log\\s*=\\s*1", + "max_connections\\s*=\\s*1337", + "ft_min_word_len\\s*=\\s*8", + "ft_max_word_len\\s*=\\s*15", + "max_allowed_packet\\s*=\\s*10M", + "table_open_cache\\s*=\\s*100", + "sort_buffer_size\\s*=\\s*256K", + "key_buffer_size\\s*=\\s*16M", + "read_buffer_size\\s*=\\s*16M", + "innodb_log_file_size\\s*=\\s*4M", + "innodb_log_buffer_size\\s*=\\s*4M", + "authentication_policy\\s*=\\s*'sha256_password'", + ] + for word in words: + assert re.search(word, db_configuration), ( + f"Word {word} not found in {db_configuration}" + ) + PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") diff --git a/test/test_container_general.py b/test/test_container_general.py index 4685305d..504a6314 100644 --- a/test/test_container_general.py +++ b/test/test_container_general.py @@ -4,6 +4,7 @@ from container_ci_suite.container_lib import ContainerTestLib from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.database import DatabaseWrapper from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper from conftest import VARS @@ -15,11 +16,12 @@ class TestMySqlGeneralContainer: """ def setup_method(self): - self.s2i_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) - self.s2i_db.set_new_db_type(db_type="mysql") + self.db_image = ContainerTestLib(image_name=VARS.IMAGE_NAME) + self.db_image.set_new_db_type(db_type="mysql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): - self.s2i_db.cleanup() + self.db_image.cleanup() @pytest.mark.parametrize( "docker_args, username, password, root_password", @@ -38,7 +40,7 @@ def test_run(self, docker_args, username, password, root_password): f"-e MYSQL_ROOT_PASSWORD={root_password}" if root_password else "" ) cid_file_name = f"test_{username}_{password}_{root_password}" - assert self.s2i_db.create_container( + assert self.db_image.create_container( cid_file_name=cid_file_name, container_args=[ f"-e MYSQL_USER={username}", @@ -49,83 +51,96 @@ def test_run(self, docker_args, username, password, root_password): ], command="run-mysqld --innodb_buffer_pool_size=5242880", ) - cip = self.s2i_db.get_cip(cid_file_name=cid_file_name) + cip = self.db_image.get_cip(cid_file_name=cid_file_name) assert cip - assert self.s2i_db.test_db_connection( + assert self.db_image.test_db_connection( container_ip=cip, username=username, password=password ) - cid = self.s2i_db.get_cid(cid_file_name=cid_file_name) + cid = self.db_image.get_cid(cid_file_name=cid_file_name) output = PodmanCLIWrapper.podman_exec_shell_command( cid_file_name=cid, cmd="mysql --version", ) assert VARS.VERSION in output - self.s2i_db.db_lib.assert_login_access( + self.db_image.db_lib.assert_login_access( container_ip=cip, username=username, password=password, expected_success=True, ) - self.s2i_db.db_lib.assert_login_access( + self.db_image.db_lib.assert_login_access( container_ip=cip, username=username, password=f"{password}_foo", expected_success=False, ) if root_password: - self.s2i_db.db_lib.assert_login_access( + self.db_image.db_lib.assert_login_access( container_ip=cip, username="root", password=root_password, expected_success=True, ) - self.s2i_db.db_lib.assert_login_access( + self.db_image.db_lib.assert_login_access( container_ip=cip, username="root", password=f"{root_password}_foo", expected_success=False, ) else: - self.s2i_db.db_lib.assert_login_access( + self.db_image.db_lib.assert_login_access( container_ip=cip, username="root", password="foo", expected_success=False, ) - self.s2i_db.db_lib.assert_login_access( + self.db_image.db_lib.assert_login_access( container_ip=cip, username="root", password="", expected_success=False, ) - output = self.s2i_db.db_lib.assert_local_access(container_id=cid) - assert output - podman_cmd = ( - f"--rm {VARS.IMAGE_NAME} mysql --host {cip} -u{username} -p{password}" - ) - assert PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e 'CREATE TABLE tbl (col1 VARCHAR(20), col2 VARCHAR(20));' db", - ) - values = 'INSERT INTO tbl VALUES ("foo1", "bar1");' - assert PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e '{values}' db", - ) - values = 'INSERT INTO tbl VALUES ("foo2", "bar2");' - assert PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e '{values}' db", - ) - values = 'INSERT INTO tbl VALUES ("foo3", "bar3");' - assert PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e '{values}' db", + assert self.db_image.db_lib.assert_local_access(container_id=cid) + self.db_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + sql_cmd=[ + "CREATE TABLE tbl (col1 VARCHAR(20), col2 VARCHAR(20));", + ], ) - output = PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e 'SELECT * FROM tbl;' db", + self.db_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + sql_cmd=[ + 'INSERT INTO tbl VALUES ("foo1", "bar1");', + 'INSERT INTO tbl VALUES ("foo2", "bar2");', + 'INSERT INTO tbl VALUES ("foo3", "bar3");', + ], ) - assert re.search(r"foo1\t*bar1", output) - assert re.search(r"foo2\t*bar2", output) - assert re.search(r"foo3\t*bar3", output) - PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e 'DROP TABLE tbl;' db", + output = self.db_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + sql_cmd="SELECT * FROM tbl;", + ) + words = [ + "foo1\t*bar1", + "foo2\t*bar2", + "foo3\t*bar3", + ] + for word in words: + assert re.search(word, output), f"Word {word} not found in {output}" + self.db_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + sql_cmd="DROP TABLE tbl;", ) def test_datadir_actions(self): @@ -140,41 +155,44 @@ def test_datadir_actions(self): f"chmod -R a+rwx {datadir}", ] ) - assert self.s2i_db.create_container( + mysql_user = "user" + mysql_password = "foo" + mysql_database = "db" + assert self.db_image.create_container( cid_file_name=cid_testupg1, container_args=[ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=foo", - "-e MYSQL_DATABASE=db", + f"-e MYSQL_USER={mysql_user}", + f"-e MYSQL_PASSWORD={mysql_password}", + f"-e MYSQL_DATABASE={mysql_database}", f"-v {datadir}/data:/var/lib/mysql/data:Z", ], ) - cip = self.s2i_db.get_cip(cid_file_name=cid_testupg1) + cip = self.db_image.get_cip(cid_file_name=cid_testupg1) assert cip - assert self.s2i_db.test_db_connection( + assert self.db_image.test_db_connection( container_ip=cip, username="user", password="foo" ) - cid = self.s2i_db.get_cid(cid_file_name=cid_testupg1) + cid = self.db_image.get_cid(cid_file_name=cid_testupg1) assert cid PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") cid_testupg5 = "testupg5" - assert self.s2i_db.create_container( + assert self.db_image.create_container( cid_file_name=cid_testupg5, container_args=[ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=foo", - "-e MYSQL_DATABASE=db", + f"-e MYSQL_USER={mysql_user}", + f"-e MYSQL_PASSWORD={mysql_password}", + f"-e MYSQL_DATABASE={mysql_database}", f"-v {datadir}/data:/var/lib/mysql/data:Z", "-e MYSQL_DATADIR_ACTION=analyze", ], ) - cip = self.s2i_db.get_cip(cid_file_name=cid_testupg5) + cip = self.db_image.get_cip(cid_file_name=cid_testupg5) assert cip - assert self.s2i_db.test_db_connection( - container_ip=cip, username="user", password="foo" + assert self.db_image.test_db_connection( + container_ip=cip, username=mysql_user, password=mysql_password ) - cid = self.s2i_db.get_cid(cid_file_name=cid_testupg5) + cid = self.db_image.get_cid(cid_file_name=cid_testupg5) assert cid output = PodmanCLIWrapper.podman_logs( container_id=cid, @@ -183,22 +201,22 @@ def test_datadir_actions(self): PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") cid_testupg6 = "testupg6" - assert self.s2i_db.create_container( + assert self.db_image.create_container( cid_file_name=cid_testupg6, container_args=[ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=foo", - "-e MYSQL_DATABASE=db", + f"-e MYSQL_USER={mysql_user}", + f"-e MYSQL_PASSWORD={mysql_password}", + f"-e MYSQL_DATABASE={mysql_database}", f"-v {datadir}/data:/var/lib/mysql/data:Z", "-e MYSQL_DATADIR_ACTION=optimize", ], ) - cip = self.s2i_db.get_cip(cid_file_name=cid_testupg6) + cip = self.db_image.get_cip(cid_file_name=cid_testupg6) assert cip - assert self.s2i_db.test_db_connection( - container_ip=cip, username="user", password="foo" + assert self.db_image.test_db_connection( + container_ip=cip, username=mysql_user, password=mysql_password ) - cid = self.s2i_db.get_cid(cid_file_name=cid_testupg6) + cid = self.db_image.get_cid(cid_file_name=cid_testupg6) assert cid output = PodmanCLIWrapper.podman_logs( container_id=cid, diff --git a/test/test_container_password.py b/test/test_container_password.py index a18be6d7..cf0a3217 100644 --- a/test/test_container_password.py +++ b/test/test_container_password.py @@ -1,7 +1,8 @@ import tempfile -from container_ci_suite.container_lib import ContainerTestLib, DatabaseWrapper +from container_ci_suite.container_lib import ContainerTestLib from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.database import DatabaseWrapper from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper from conftest import VARS @@ -13,14 +14,23 @@ class TestMySqlPasswordContainer: """ def setup_method(self): + """ + Setup the test environment. + """ self.pwd_change = ContainerTestLib(image_name=VARS.IMAGE_NAME) self.pwd_change.set_new_db_type(db_type="mysql") + self.dw_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): + """ + Teardown the test environment. + """ self.pwd_change.cleanup() def test_password_change(self): - """ """ + """ + Test password change. + """ cid_file_name1 = "test_password_change" pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") username = "user" @@ -63,20 +73,21 @@ def test_password_change(self): assert self.pwd_change.test_db_connection( container_ip=cip2, username=username, password=new_password ) - podman_cmd = ( - f"--rm {VARS.IMAGE_NAME} mysql --host {cip2} -u{username} -p{password}" - ) - output = PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e 'SELECT 1;' db", + output = self.dw_api.run_sql_command( + container_ip=cip2, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, ignore_error=True, ) - print(output) assert f"Access denied for user '{username}'@" in output, ( f"The old password {password} should not work, but it does" ) def test_password_change_new_user_test(self): - """ """ + """ + Test password change for new user. + """ cid_file_name = "test_password_change1" pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") username1 = "user" @@ -126,11 +137,11 @@ def test_password_change_new_user_test(self): container_id=cid2, ) assert "User user2 does not exist in database" in mysql_logs - podman_cmd = ( - f"--rm {VARS.IMAGE_NAME} mysql --host {cip2} -u{username1} -p{password2}" - ) - output = PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e 'SELECT 1;' db", + output = self.dw_api.run_sql_command( + container_ip=cip2, + username=username1, + password=password2, + container_id=VARS.IMAGE_NAME, ignore_error=True, ) assert f"Access denied for user '{username1}'@" in output, ( diff --git a/test/test_container_replication.py b/test/test_container_replication.py index 5873723f..68a33c11 100644 --- a/test/test_container_replication.py +++ b/test/test_container_replication.py @@ -2,8 +2,8 @@ import re from time import sleep -from container_ci_suite.container_lib import ContainerTestLib, DatabaseWrapper -from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.container_lib import ContainerTestLib +from container_ci_suite.engines.database import DatabaseWrapper from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper from conftest import VARS @@ -15,14 +15,23 @@ class TestMySqlReplicationContainer: """ def setup_method(self): + """ + Setup the test environment. + """ self.replication_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) self.replication_db.set_new_db_type(db_type="mysql") + self.db_wrapper_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): + """ + Teardown the test environment. + """ self.replication_db.cleanup() def test_replication(self): - """ """ + """ + Test replication. + """ cluster_args = "-e MYSQL_SOURCE_USER=source -e MYSQL_SOURCE_PASSWORD=source -e MYSQL_DATABASE=db" source_cid = "source.cid" username = "user" @@ -40,7 +49,6 @@ def test_replication(self): command="run-mysqld-source", ) source_cip = self.replication_db.get_cip(cid_file_name=source_cid) - print(f"Source IP: {source_cip}") source_cid = self.replication_db.get_cid(cid_file_name=source_cid) assert source_cid # Run the MySQL replica @@ -58,47 +66,42 @@ def test_replication(self): assert replica_cip replica_cid = self.replication_db.get_cid(cid_file_name=replica_cid) assert replica_cid - print(f"Replica IP: {replica_cip}") # Now wait till the SOURCE will see the REPLICA result = self.replication_db.test_db_connection( container_ip=replica_cip, - username=username, - password=password, + username="root", + password="root", ) - result = PodmanCLIWrapper.call_podman_command( - cmd=f"exec {source_cid} mysql -e 'SHOW REPLICAS;' db", - ignore_error=True, - debug=True, + result = self.db_wrapper_api.run_sql_command( + container_ip=source_cip, + username="root", + password="root", + container_id=source_cid, + sql_cmd="SHOW REPLICAS;", + podman_run_command="exec", ) - print(f"Showing replicas: {result}") assert replica_cip in result, ( f"Replica {replica_cip} not found in SOURCE {source_cip}" ) # do some real work to test replication in practice - result = self.replication_db.test_db_connection( + table_output = self.db_wrapper_api.run_sql_command( container_ip=source_cip, - username=username, - password=password, - max_attempts=120, - sql_cmd="-e 'CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (24);'", + username="root", + password="root", + container_id=source_cid, + sql_cmd="CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (24);", + podman_run_command="exec", ) - print(f"Creating table: {result}") # let's wait for the table to be created and available for replication sleep(3) - result = self.replication_db.test_db_connection( + table_output = self.db_wrapper_api.run_sql_command( container_ip=replica_cip, - username=username, - password=password, - max_attempts=120, - sql_cmd="-e 'select * from t1;'", - ) - assert result - podman_cmd = f"--rm {VARS.IMAGE_NAME} mysql --host {replica_cip} -u{username} -p{password}" - table_output = PodmanCLIWrapper.podman_run_command( - cmd=f"{podman_cmd} -e 'select * from t1;' db", + username="root", + password="root", + container_id=VARS.IMAGE_NAME, + sql_cmd="select * from t1;", ) - print(f"Selecting from table: {table_output}") assert re.search(r"^a\n^24", table_output.strip(), re.MULTILINE), ( f"Replica {replica_cip} did not get value from SOURCE {source_cip}" ) diff --git a/test/test_container_ssl.py b/test/test_container_ssl.py index 5aa1f55f..1e77b3e0 100644 --- a/test/test_container_ssl.py +++ b/test/test_container_ssl.py @@ -3,6 +3,7 @@ from container_ci_suite.container_lib import ContainerTestLib from container_ci_suite.container_lib import ContainerTestLibUtils +from container_ci_suite.engines.database import DatabaseWrapper from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper from conftest import VARS @@ -14,14 +15,23 @@ class TestMySqlGeneralContainer: """ def setup_method(self): + """ + Setup the test environment. + """ self.ssl_db = ContainerTestLib(image_name=VARS.IMAGE_NAME) self.ssl_db.set_new_db_type(db_type="mysql") + self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): + """ + Teardown the test environment. + """ self.ssl_db.cleanup() def test_ssl(self): - """ """ + """ + Test SSL. + """ ssl_dir = tempfile.mkdtemp(prefix="/tmp/mysql-ssl_data") username = "ssl_test_user" password = "ssl_test" From 0b9003c519762239c1fe191ef664d4cf3e24b345 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Fri, 27 Feb 2026 12:41:15 +0100 Subject: [PATCH 4/7] Update code based on the already reviewed mariadb-container https://github.com/sclorg/mariadb-container/pull/311 Signed-off-by: Petr "Stone" Hracek --- test/conftest.py | 4 +- test/test_container_basics.py | 54 +++++---- test/test_container_configuration.py | 165 +++++++++++++++----------- test/test_container_general.py | 138 +++++++++++---------- test/test_container_password.py | 171 +++++++++++---------------- 5 files changed, 267 insertions(+), 265 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index d7cb40b7..ec628020 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -31,8 +31,8 @@ VERSION = os.getenv("VERSION") OS = os.getenv("TARGET").lower() TEST_APP = TEST_DIR / "test-app" -VERY_LONG_DB_NAME = "very_long_database_name_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -VERY_LONG_USER_NAME = "very_long_user_name_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +VERY_LONG_DB_NAME = "very_long_database_name_" + "x" * 40 +VERY_LONG_USER_NAME = "very_long_user_name_" + "x" * 40 VARS = Vars( OS=OS, VERSION=VERSION, diff --git a/test/test_container_basics.py b/test/test_container_basics.py index e17edcaf..9276d460 100644 --- a/test/test_container_basics.py +++ b/test/test_container_basics.py @@ -27,18 +27,28 @@ class TestMySqlBasicsContainer: """ def setup_method(self): - self.s2i_db = build_s2i_app(app_path=VARS.TEST_DIR / "test-app") - self.s2i_db.set_new_db_type(db_type="mysql") + """ + Setup the test environment. + """ + self.app_image = build_s2i_app(app_path=VARS.TEST_DIR / "test-app") + self.app_image.set_new_db_type(db_type="mysql") def teardown_method(self): - self.s2i_db.cleanup() + """ + Teardown the test environment. + """ + self.app_image.cleanup() def test_s2i_usage(self): """ - Test container creation fails with invalid combinations of arguments. + Test if MySQL container failed in case of invalid combinations. + Steps are: + 1. Test if the container creation fails with invalid combinations of arguments + 2. Test if the container creation succeeds with valid combinations of arguments + 3. Test if the database connection works """ - cid_config_build = "s2i_config_build" - self.s2i_db.assert_container_creation_fails( + cid_config_build = "s2i_usage_build" + self.app_image.assert_container_creation_fails( cid_file_name=cid_config_build, command="", container_args=[ @@ -48,7 +58,7 @@ def test_s2i_usage(self): "-e MYSQL_ROOT_PASSWORD=pass", ], ) - assert self.s2i_db.create_container( + assert self.app_image.create_container( cid_file_name=cid_config_build, container_args=[ "-e MYSQL_USER=config_test_user", @@ -58,22 +68,20 @@ def test_s2i_usage(self): "-e MYSQL_OPERATIONS_PASSWORD=operations_user", ], ) - cip = self.s2i_db.get_cip(cid_file_name=cid_config_build) - assert cip - assert self.s2i_db.test_db_connection( + cip, cid = self.app_image.get_cip_cid(cid_file_name=cid_config_build) + assert cip, cid + assert self.app_image.test_db_connection( container_ip=cip, username="operations_user", password="operations_user" ) - cid = self.s2i_db.get_cid(cid_file_name=cid_config_build) - db_configuration = PodmanCLIWrapper.podman_exec_shell_command( - cid_file_name=cid, - cmd="cat /etc/my.cnf /etc/my.cnf.d/*", - ) - assert db_configuration PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") def test_s2i_usage_with_mount(self): """ - Test container creation fails with invalid combinations of arguments. + Test if the MySQL container works properly with mounted application directory. + Steps are: + 1. Copy the test-app directory to a temporary directory and set proper permissions + 2. Create a container with the mounted directory + 3. Test if the database connection works with the operations user """ data_dir = tempfile.mkdtemp(prefix="/tmp/mysql-test_data") shutil.copytree(VARS.TEST_DIR / "test-app", f"{data_dir}/test-app") @@ -83,7 +91,7 @@ def test_s2i_usage_with_mount(self): ] ) cid_s2i_test_mount = "s2i_test_mount" - self.s2i_db.create_container( + self.app_image.create_container( cid_file_name=cid_s2i_test_mount, container_args=[ "-e MYSQL_USER=config_test_user", @@ -94,15 +102,13 @@ def test_s2i_usage_with_mount(self): f"-v {data_dir}/test-app:/opt/app-root/src/:z", ], ) - cip_test_mount = self.s2i_db.get_cip(cid_file_name=cid_s2i_test_mount) - assert cip_test_mount - assert self.s2i_db.test_db_connection( - container_ip=cip_test_mount, + cip, cid = self.app_image.get_cip_cid(cid_file_name=cid_s2i_test_mount) + assert cip, cid + assert self.app_image.test_db_connection( + container_ip=cip, username="operations_user", password="operations_pass", max_attempts=10, ) - cid = self.s2i_db.get_cid(cid_file_name=cid_s2i_test_mount) - assert cid PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") shutil.rmtree(data_dir) diff --git a/test/test_container_configuration.py b/test/test_container_configuration.py index 08094880..af65513d 100644 --- a/test/test_container_configuration.py +++ b/test/test_container_configuration.py @@ -52,60 +52,81 @@ def test_try_image_invalid_combinations(self, container_args): ) @pytest.mark.parametrize( - "container_args", + "mysql_user, mysql_password, mysql_database, mysql_root_password", [ - ["-e", "MYSQL_USER=user", "-e", "MYSQL_PASSWORD=pass"], + ["user", "pass", "", ""], [ - "-e MYSQL_USER=$invalid", - "-e MYSQL_PASSWORD=pass", - "-e MYSQL_DATABASE=db", - "-e MYSQL_ROOT_PASSWORD=root_pass", + "$invalid", + "pass", + "db", + "root_pass", ], [ - f"-e MYSQL_USER={VARS.VERY_LONG_USER_NAME}", - "-e MYSQL_PASSWORD=pass", - "-e MYSQL_DATABASE=db", - "-e MYSQL_ROOT_PASSWORD=root_pass", + VARS.VERY_LONG_USER_NAME, + "pass", + "db", + "root_pass", ], [ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=", - "-e MYSQL_DATABASE=db", - "-e MYSQL_ROOT_PASSWORD=root_pass", + "user", + "", + "db", + "root_pass", ], [ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=pass", - "-e MYSQL_DATABASE=$invalid", - "-e MYSQL_ROOT_PASSWORD=root_pass", + "user", + "pass", + "$invalid", + "root_pass", ], [ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=pass", - f"-e MYSQL_DATABASE={VARS.VERY_LONG_DB_NAME}", - "-e MYSQL_ROOT_PASSWORD=root_pass", + "user", + "pass", + VARS.VERY_LONG_DB_NAME, + "root_pass", ], [ - "-e MYSQL_USER=user", - "-e MYSQL_PASSWORD=pass", - "-e MYSQL_DATABASE=db", - "-e MYSQL_ROOT_PASSWORD=", + "user", + "pass", + "db", + "", ], [ - "-e MYSQL_USER=root", - "-e MYSQL_PASSWORD=pass", - "-e MYSQL_DATABASE=db", - "-e MYSQL_ROOT_PASSWORD=pass", + "root", + "pass", + "db", + "pass", ], ], ) - def test_invalid_configuration_tests(self, container_args): + def test_invalid_configuration_tests( + self, mysql_user, mysql_password, mysql_database, mysql_root_password + ): """ Test invalid configuration combinations for MySQL container. """ cid_config_test = "invalid_configuration_tests" + mysql_user_arg = f"-e MYSQL_USER={mysql_user}" if mysql_user else "" + mysql_password_arg = ( + f"-e MYSQL_PASSWORD={mysql_password}" if mysql_password else "" + ) + mysql_database_arg = ( + f"-e MYSQL_DATABASE={mysql_database}" if mysql_database else "" + ) + mysql_root_password_arg = ( + f"-e MYSQL_ROOT_PASSWORD={mysql_root_password}" + if mysql_root_password + else "" + ) assert self.db.assert_container_creation_fails( - cid_file_name=cid_config_test, container_args=container_args, command="" + cid_file_name=cid_config_test, + container_args=[ + mysql_user_arg, + mysql_password_arg, + mysql_database_arg, + mysql_root_password_arg, + ], + command="", ) @@ -119,7 +140,6 @@ def setup_method(self): Setup the test environment. """ self.db_config = ContainerTestLib(image_name=VARS.IMAGE_NAME) - self.db_config.set_new_db_type(db_type="mysql") self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): @@ -131,6 +151,12 @@ def teardown_method(self): def test_configuration_auto_calculated_settings(self): """ Test MySQL container configuration auto-calculated settings. + Steps are: + 1. Create a container with the given arguments + 2. Check if the container is created successfully + 3. Check if the database connection works + 4. Check if the database configurations are correct + 5. Stop the container """ cid_config_test = "auto-config_test" username = "config_test_user" @@ -146,30 +172,29 @@ def test_configuration_auto_calculated_settings(self): ], docker_args="--memory=512m", ) - cip = self.db_config.get_cip(cid_file_name=cid_config_test) - assert cip + cip, cid = self.db_config.get_cip_cid(cid_file_name=cid_config_test) + assert cip, cid assert self.db_config.test_db_connection( container_ip=cip, username=username, password=password, max_attempts=10, ) - cid = self.db_config.get_cid(cid_file_name=cid_config_test) db_configuration = PodmanCLIWrapper.podman_exec_shell_command( cid_file_name=cid, cmd="cat /etc/my.cnf /etc/my.cnf.d/*", ) - words = [ - "key_buffer_size\\s*=\\s*51M", - "read_buffer_size\\s*=\\s*25M", - "innodb_buffer_pool_size\\s*=\\s*256M", - "innodb_log_file_size\\s*=\\s*76M", - "innodb_log_buffer_size\\s*=\\s*76M", - "authentication_policy\\s*=\\s*'caching_sha2_password,,'", + expected_values = [ + r"key_buffer_size\s*=\s*51M", + r"read_buffer_size\s*=\s*25M", + r"innodb_buffer_pool_size\s*=\s*256M", + r"innodb_log_file_size\s*=\s*76M", + r"innodb_log_buffer_size\s*=\s*76M", + r"authentication_policy\s*=\s*'caching_sha2_password,,'", ] - for word in words: - assert re.search(word, db_configuration), ( - f"Word {word} not found in {db_configuration}" + for value in expected_values: + assert re.search(value, db_configuration), ( + f"Expected value {value} not found in {db_configuration}" ) # do some real work to test replication in practice self.db_api.run_sql_command( @@ -192,6 +217,7 @@ def test_configuration_auto_calculated_settings(self): assert "COLLATE=latin2_czech_cs" in show_table_output PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") + # FIX def test_configuration_options_settings(self): """ Test MySQL container configuration options. @@ -218,44 +244,39 @@ def test_configuration_options_settings(self): "--env MYSQL_INNODB_BUFFER_POOL_SIZE=16M", "--env MYSQL_INNODB_LOG_FILE_SIZE=4M", "--env MYSQL_INNODB_LOG_BUFFER_SIZE=4M", - "--env MYSQL_AUTHENTICATION_POLICY=sha256_password", + "--env MYSQL_AUTHENTICATION_POLICY=sha256_password,,", + "--env WORKAROUND_DOCKER_BUG_14203=", ], ) - cip = self.db_config.get_cip(cid_file_name=cid_config_test) - assert cip - assert self.db_config.test_db_connection( - container_ip=cip, username=username, password=password - ) - cip = self.db_config.get_cip(cid_file_name=cid_config_test) - assert cip + cip, cid = self.db_config.get_cip_cid(cid_file_name=cid_config_test) + assert cip, cid assert self.db_config.test_db_connection( container_ip=cip, username=username, password=password, max_attempts=10, ) - cid = self.db_config.get_cid(cid_file_name=cid_config_test) db_configuration = PodmanCLIWrapper.podman_exec_shell_command( cid_file_name=cid, cmd="cat /etc/my.cnf /etc/my.cnf.d/*", ) - words = [ - "lower_case_table_names\\s*=\\s*1", - "general_log\\s*=\\s*1", - "max_connections\\s*=\\s*1337", - "ft_min_word_len\\s*=\\s*8", - "ft_max_word_len\\s*=\\s*15", - "max_allowed_packet\\s*=\\s*10M", - "table_open_cache\\s*=\\s*100", - "sort_buffer_size\\s*=\\s*256K", - "key_buffer_size\\s*=\\s*16M", - "read_buffer_size\\s*=\\s*16M", - "innodb_log_file_size\\s*=\\s*4M", - "innodb_log_buffer_size\\s*=\\s*4M", - "authentication_policy\\s*=\\s*'sha256_password'", + expected_values = [ + r"lower_case_table_names\s*=\s*1", + r"general_log\s*=\s*1", + r"max_connections\s*=\s*1337", + r"ft_min_word_len\s*=\s*8", + r"ft_max_word_len\s*=\s*15", + r"max_allowed_packet\s*=\s*10M", + r"table_open_cache\s*=\s*100", + r"sort_buffer_size\s*=\s*256K", + r"key_buffer_size\s*=\s*16M", + r"read_buffer_size\s*=\s*16M", + r"innodb_log_file_size\s*=\s*4M", + r"innodb_log_buffer_size\s*=\s*4M", + r"authentication_policy\s*=\s*'sha256_password,,'", ] - for word in words: - assert re.search(word, db_configuration), ( - f"Word {word} not found in {db_configuration}" + for value in expected_values: + assert re.search(value, db_configuration), ( + f"Expected value {value} not found in {db_configuration}" ) PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") diff --git a/test/test_container_general.py b/test/test_container_general.py index 504a6314..ee9e8408 100644 --- a/test/test_container_general.py +++ b/test/test_container_general.py @@ -16,11 +16,17 @@ class TestMySqlGeneralContainer: """ def setup_method(self): + """ + Setup the test environment. + """ self.db_image = ContainerTestLib(image_name=VARS.IMAGE_NAME) self.db_image.set_new_db_type(db_type="mysql") self.db_api = DatabaseWrapper(image_name=VARS.IMAGE_NAME) def teardown_method(self): + """ + Teardown the test environment. + """ self.db_image.cleanup() @pytest.mark.parametrize( @@ -34,7 +40,16 @@ def teardown_method(self): ) def test_run(self, docker_args, username, password, root_password): """ - Test container creation fails with invalid combinations of arguments. + Test if the MySQL container works properly with the different arguments + like docker_args, username, password, and root_password. + Steps are: + 1. Create a container with the given arguments + 2. Check if the container is created successfully + 3. Check if the database connection works + 4. Check if mariadb version is correct + 5. Check if the login access works + 6. Check if the local access works + 7. Test the database creation """ root_password_arg = ( f"-e MYSQL_ROOT_PASSWORD={root_password}" if root_password else "" @@ -51,63 +66,68 @@ def test_run(self, docker_args, username, password, root_password): ], command="run-mysqld --innodb_buffer_pool_size=5242880", ) - cip = self.db_image.get_cip(cid_file_name=cid_file_name) - assert cip + cip, cid = self.db_image.get_cip_cid(cid_file_name=cid_file_name) + assert cip, cid assert self.db_image.test_db_connection( container_ip=cip, username=username, password=password ) - cid = self.db_image.get_cid(cid_file_name=cid_file_name) output = PodmanCLIWrapper.podman_exec_shell_command( cid_file_name=cid, cmd="mysql --version", ) assert VARS.VERSION in output - self.db_image.db_lib.assert_login_access( - container_ip=cip, - username=username, - password=password, - expected_success=True, - ) - self.db_image.db_lib.assert_login_access( - container_ip=cip, - username=username, - password=f"{password}_foo", - expected_success=False, - ) - if root_password: - self.db_image.db_lib.assert_login_access( - container_ip=cip, - username="root", - password=root_password, - expected_success=True, - ) - self.db_image.db_lib.assert_login_access( + login_access = True + for user, pwd, ret_value in [ + (username, password, True), + (username, f"{password}_foo", False), + ("root", "foo", False), + ("root", "", False), + ]: + test_assert = self.db_image.db_lib.assert_login_access( container_ip=cip, - username="root", - password=f"{root_password}_foo", - expected_success=False, - ) - else: - self.db_image.db_lib.assert_login_access( - container_ip=cip, - username="root", - password="foo", - expected_success=False, - ) - self.db_image.db_lib.assert_login_access( - container_ip=cip, - username="root", - password="", - expected_success=False, + username=user, + password=pwd, + expected_success=ret_value, ) + if not test_assert: + print( + f"Login access failed for {user}:{pwd} with expected success {ret_value}" + ) + login_access = False + assert login_access + if root_password: + root_login_access = True + for user, pwd, ret_value in [ + ("root", root_password, True), + ("root", f"{root_password}_foo", False), + ]: + test_assert = self.db_image.db_lib.assert_login_access( + container_ip=cip, + username=user, + password=pwd, + expected_success=ret_value, + ) + if not test_assert: + print( + f"Root login access failed for {user}:{pwd} with expected success {ret_value}" + ) + root_login_access = False + continue + assert root_login_access assert self.db_image.db_lib.assert_local_access(container_id=cid) + self.database_test(cip, username, password) + + def database_test(self, cip, username, password): + """ + Test MariaDB database table creation and data insertion is valid. + """ self.db_api.run_sql_command( container_ip=cip, username=username, password=password, container_id=VARS.IMAGE_NAME, sql_cmd=[ - "CREATE TABLE tbl (col1 VARCHAR(20), col2 VARCHAR(20));", + "CREATE TABLE tbl (a integer, b integer);", ], ) self.db_api.run_sql_command( @@ -116,9 +136,9 @@ def test_run(self, docker_args, username, password, root_password): password=password, container_id=VARS.IMAGE_NAME, sql_cmd=[ - 'INSERT INTO tbl VALUES ("foo1", "bar1");', - 'INSERT INTO tbl VALUES ("foo2", "bar2");', - 'INSERT INTO tbl VALUES ("foo3", "bar3");', + "INSERT INTO tbl VALUES (1, 2);", + "INSERT INTO tbl VALUES (3, 4);", + "INSERT INTO tbl VALUES (5, 6);", ], ) output = self.db_api.run_sql_command( @@ -128,13 +148,13 @@ def test_run(self, docker_args, username, password, root_password): container_id=VARS.IMAGE_NAME, sql_cmd="SELECT * FROM tbl;", ) - words = [ - "foo1\t*bar1", - "foo2\t*bar2", - "foo3\t*bar3", + expected_db_output = [ + r"1\s*\t*2", + r"3\s*\t*4", + r"5\s*\t*6", ] - for word in words: - assert re.search(word, output), f"Word {word} not found in {output}" + for row in expected_db_output: + assert re.search(row, output), f"Row {row} not found in {output}" self.db_api.run_sql_command( container_ip=cip, username=username, @@ -167,13 +187,11 @@ def test_datadir_actions(self): f"-v {datadir}/data:/var/lib/mysql/data:Z", ], ) - cip = self.db_image.get_cip(cid_file_name=cid_testupg1) - assert cip + cip, cid = self.db_image.get_cip_cid(cid_file_name=cid_testupg1) + assert cip, cid assert self.db_image.test_db_connection( container_ip=cip, username="user", password="foo" ) - cid = self.db_image.get_cid(cid_file_name=cid_testupg1) - assert cid PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") cid_testupg5 = "testupg5" @@ -187,13 +205,11 @@ def test_datadir_actions(self): "-e MYSQL_DATADIR_ACTION=analyze", ], ) - cip = self.db_image.get_cip(cid_file_name=cid_testupg5) - assert cip + cip, cid = self.db_image.get_cip_cid(cid_file_name=cid_testupg5) + assert cip, cid assert self.db_image.test_db_connection( container_ip=cip, username=mysql_user, password=mysql_password ) - cid = self.db_image.get_cid(cid_file_name=cid_testupg5) - assert cid output = PodmanCLIWrapper.podman_logs( container_id=cid, ) @@ -211,13 +227,11 @@ def test_datadir_actions(self): "-e MYSQL_DATADIR_ACTION=optimize", ], ) - cip = self.db_image.get_cip(cid_file_name=cid_testupg6) - assert cip + cip, cid = self.db_image.get_cip_cid(cid_file_name=cid_testupg6) + assert cip, cid assert self.db_image.test_db_connection( container_ip=cip, username=mysql_user, password=mysql_password ) - cid = self.db_image.get_cid(cid_file_name=cid_testupg6) - assert cid output = PodmanCLIWrapper.podman_logs( container_id=cid, ) diff --git a/test/test_container_password.py b/test/test_container_password.py index cf0a3217..15ba1334 100644 --- a/test/test_container_password.py +++ b/test/test_container_password.py @@ -1,4 +1,5 @@ import tempfile +import pytest from container_ci_suite.container_lib import ContainerTestLib from container_ci_suite.container_lib import ContainerTestLibUtils @@ -7,6 +8,20 @@ from conftest import VARS +pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") +assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"chmod -R a+rwx {pwd_dir}", + ] +) + +user_dir = tempfile.mkdtemp(prefix="/tmp/mysql-user") +assert ContainerTestLibUtils.commands_to_run( + commands_to_run=[ + f"chmod -R a+rwx {user_dir}", + ] +) + class TestMySqlPasswordContainer: """ @@ -27,123 +42,69 @@ def teardown_method(self): """ self.pwd_change.cleanup() - def test_password_change(self): + @pytest.mark.parametrize( + "username, password, pwd_change, user_change, test_dir", + [ + ("user", "foo", False, False, pwd_dir), + ("user", "bar", True, False, pwd_dir), + ("user", "foo", False, True, user_dir), + ("user2", "bar", False, True, user_dir), + ], + ) + def test_password_change( + self, username, password, pwd_change, user_change, test_dir + ): """ Test password change. """ - cid_file_name1 = "test_password_change" - pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") - username = "user" - password = "foo" - assert ContainerTestLibUtils.commands_to_run( - commands_to_run=[ - f"chmod -R a+rwx {pwd_dir}", - ] - ) - assert self.pwd_change.create_container( - cid_file_name=cid_file_name1, - container_args=[ - f"-e MYSQL_USER={username}", - f"-e MYSQL_PASSWORD={password}", - "-e MYSQL_DATABASE=db", - f"-v {pwd_dir}:/var/lib/mysql/data:Z", - ], - ) - cip1 = self.pwd_change.get_cip(cid_file_name=cid_file_name1) - assert cip1 - assert self.pwd_change.test_db_connection( - container_ip=cip1, username=username, password=password - ) - cid1 = self.pwd_change.get_cid(cid_file_name=cid_file_name1) - assert cid1 - PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid1}") - cid_file_name2 = "test_password_change_2" - new_password = "bar" - assert self.pwd_change.create_container( - cid_file_name=cid_file_name2, - container_args=[ - f"-e MYSQL_USER={username}", - f"-e MYSQL_PASSWORD={new_password}", - "-e MYSQL_DATABASE=db", - f"-v {pwd_dir}:/var/lib/mysql/data:Z", - ], - ) - cip2 = self.pwd_change.get_cip(cid_file_name=cid_file_name2) - assert cip2 - assert self.pwd_change.test_db_connection( - container_ip=cip2, username=username, password=new_password - ) - output = self.dw_api.run_sql_command( - container_ip=cip2, + self.password_change_test( username=username, password=password, - container_id=VARS.IMAGE_NAME, - ignore_error=True, - ) - assert f"Access denied for user '{username}'@" in output, ( - f"The old password {password} should not work, but it does" + pwd_change=pwd_change, + user_change=user_change, + test_dir=test_dir, ) - def test_password_change_new_user_test(self): + def password_change_test( + self, username, password, pwd_change, user_change, test_dir + ): """ - Test password change for new user. + Test password change. + Steps are: + 1. Create a container with the given arguments + 2. Check if the container is created successfully + 3. Check if the database connection works + 4. If user_change is True, then 'user' and 'foo' are used for testing connection + 4. Check if the userchange, then user2 does exist in the database logs + 5. Check if the userchange, then sql command should work with the 'user' and 'bar' should + not work and should return an error message + 6. If pwd_change is True, then 'user' and 'pwdfoo' should not work and should return an error message """ - cid_file_name = "test_password_change1" - pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") - username1 = "user" - password1 = "foo" - assert ContainerTestLibUtils.commands_to_run( - commands_to_run=[ - f"chmod -R a+rwx {pwd_dir}", - ] - ) + cid_file_name = f"test_{username}_{password}_{user_change}" + container_args = [ + f"-e MYSQL_USER={username}", + f"-e MYSQL_PASSWORD={password}", + "-e MYSQL_DATABASE=db", + f"-v {test_dir}:/var/lib/mysql/data:Z", + ] assert self.pwd_change.create_container( cid_file_name=cid_file_name, - container_args=[ - f"-e MYSQL_USER={username1}", - f"-e MYSQL_PASSWORD={password1}", - "-e MYSQL_DATABASE=db", - f"-v {pwd_dir}:/var/lib/mysql/data:Z", - ], + container_args=container_args, ) - cip1 = self.pwd_change.get_cip(cid_file_name=cid_file_name) - assert cip1 + cip, cid = self.pwd_change.get_cip_cid(cid_file_name=cid_file_name) + assert cip, cid assert self.pwd_change.test_db_connection( - container_ip=cip1, username=username1, password=password1 + container_ip=cip, username=username, password=password ) - cid = self.pwd_change.get_cid(cid_file_name=cid_file_name) - assert cid + if pwd_change: + output = self.dw_api.run_sql_command( + container_ip=cip, + username=username, + password=password, + container_id=VARS.IMAGE_NAME, + ignore_error=True, + ) + assert f"Access denied for user '{username}'@" in output, ( + f"The old password {password} should not work, but it does" + ) PodmanCLIWrapper.call_podman_command(cmd=f"stop {cid}") - cid_file_name = "test_password_change2" - username2 = "user2" - password2 = "bar" - # Create second container with changed password - assert self.pwd_change.create_container( - cid_file_name=cid_file_name, - container_args=[ - f"-e MYSQL_USER={username2}", - f"-e MYSQL_PASSWORD={password2}", - "-e MYSQL_DATABASE=db", - f"-v {pwd_dir}:/var/lib/mysql/data:Z", - ], - ) - cip2 = self.pwd_change.get_cip(cid_file_name=cid_file_name) - assert cip2 - assert self.pwd_change.test_db_connection( - container_ip=cip2, username=username1, password=password1 - ) - cid2 = self.pwd_change.get_cid(cid_file_name=cid_file_name) - mysql_logs = PodmanCLIWrapper.podman_logs( - container_id=cid2, - ) - assert "User user2 does not exist in database" in mysql_logs - output = self.dw_api.run_sql_command( - container_ip=cip2, - username=username1, - password=password2, - container_id=VARS.IMAGE_NAME, - ignore_error=True, - ) - assert f"Access denied for user '{username1}'@" in output, ( - f"The new password {password2} should not work, but it does" - ) From 6e98ee061e3356d7ed13c8d7f9ee7e390ccee3e4 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Fri, 27 Feb 2026 12:47:57 +0100 Subject: [PATCH 5/7] Update run-pytest and run-openshift-pytest to select proper Python version Signed-off-by: Petr "Stone" Hracek --- test/run-openshift-pytest | 8 ++++++-- test/run-pytest | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/run-openshift-pytest b/test/run-openshift-pytest index e47a9a5c..4eb6f293 100755 --- a/test/run-openshift-pytest +++ b/test/run-openshift-pytest @@ -9,5 +9,9 @@ THISDIR=$(dirname ${BASH_SOURCE[0]}) git show -s - -cd "${THISDIR}" && python3.12 -m pytest -s -rA --showlocals -vv test_ocp_*.py +if python3 -c 'import sys; sys.exit(0 if sys.version_info < (3,13) else 1)'; then + PYTHON_VERSION="3.12" +else + PYTHON_VERSION="3" +fi +cd "${THISDIR}" && "python${PYTHON_VERSION}" -m pytest -s -rA --showlocals -vv test_ocp_*.py diff --git a/test/run-pytest b/test/run-pytest index cc76ee7b..fe298e56 100755 --- a/test/run-pytest +++ b/test/run-pytest @@ -10,8 +10,9 @@ THISDIR=$(dirname ${BASH_SOURCE[0]}) git show -s -PYTHON_VERSION="3.12" -if [[ ! -f "/usr/bin/python$PYTHON_VERSION" ]]; then - PYTHON_VERSION="3.13" +if python3 -c 'import sys; sys.exit(0 if sys.version_info < (3,13) else 1)'; then + PYTHON_VERSION="3.12" +else + PYTHON_VERSION="3" fi cd "${THISDIR}" && "python${PYTHON_VERSION}" -m pytest -s -rA --showlocals -vv test_container_*.py From 4a285e34e950258e5eee44eb504f07ddd9627e31 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Fri, 27 Feb 2026 13:09:06 +0100 Subject: [PATCH 6/7] Fix test_container_password typos in parameterization. Update also test_replication.py file (pre-commit issues) Signed-off-by: Petr "Stone" Hracek --- test/test_container_password.py | 52 ++++++++++++++++++++++-------- test/test_container_replication.py | 3 +- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/test/test_container_password.py b/test/test_container_password.py index 15ba1334..a6e2a165 100644 --- a/test/test_container_password.py +++ b/test/test_container_password.py @@ -8,17 +8,17 @@ from conftest import VARS -pwd_dir = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") +pwd_dir_change = tempfile.mkdtemp(prefix="/tmp/mysql-pwd") assert ContainerTestLibUtils.commands_to_run( commands_to_run=[ - f"chmod -R a+rwx {pwd_dir}", + f"chmod -R a+rwx {pwd_dir_change}", ] ) -user_dir = tempfile.mkdtemp(prefix="/tmp/mysql-user") +user_dir_change = tempfile.mkdtemp(prefix="/tmp/mysql-user") assert ContainerTestLibUtils.commands_to_run( commands_to_run=[ - f"chmod -R a+rwx {user_dir}", + f"chmod -R a+rwx {user_dir_change}", ] ) @@ -45,10 +45,10 @@ def teardown_method(self): @pytest.mark.parametrize( "username, password, pwd_change, user_change, test_dir", [ - ("user", "foo", False, False, pwd_dir), - ("user", "bar", True, False, pwd_dir), - ("user", "foo", False, True, user_dir), - ("user2", "bar", False, True, user_dir), + ("user", "foo", False, False, pwd_dir_change), + ("user", "bar", True, False, pwd_dir_change), + ("user", "foo", False, False, user_dir_change), + ("user2", "bar", False, True, user_dir_change), ], ) def test_password_change( @@ -60,13 +60,13 @@ def test_password_change( self.password_change_test( username=username, password=password, - pwd_change=pwd_change, + pwd_dir=test_dir, user_change=user_change, - test_dir=test_dir, + pwd_change=pwd_change, ) def password_change_test( - self, username, password, pwd_change, user_change, test_dir + self, username, password, pwd_dir, user_change=False, pwd_change=False ): """ Test password change. @@ -81,11 +81,12 @@ def password_change_test( 6. If pwd_change is True, then 'user' and 'pwdfoo' should not work and should return an error message """ cid_file_name = f"test_{username}_{password}_{user_change}" + container_args = [ f"-e MYSQL_USER={username}", f"-e MYSQL_PASSWORD={password}", "-e MYSQL_DATABASE=db", - f"-v {test_dir}:/var/lib/mysql/data:Z", + f"-v {pwd_dir}:/var/lib/mysql/data:Z", ] assert self.pwd_change.create_container( cid_file_name=cid_file_name, @@ -93,10 +94,22 @@ def password_change_test( ) cip, cid = self.pwd_change.get_cip_cid(cid_file_name=cid_file_name) assert cip, cid + if user_change: + username = "user" + password = "foo" + # Test if the database connection works with the old connection parameters assert self.pwd_change.test_db_connection( - container_ip=cip, username=username, password=password + container_ip=cip, + username=username, + password=password, ) - if pwd_change: + if user_change: + mariadb_logs = PodmanCLIWrapper.podman_logs( + container_id=cid, + ) + assert "User user2 does not exist in database" in mariadb_logs + username = "user" + password = "bar" output = self.dw_api.run_sql_command( container_ip=cip, username=username, @@ -104,6 +117,17 @@ def password_change_test( container_id=VARS.IMAGE_NAME, ignore_error=True, ) + assert f"Access denied for user '{username}'@" in output, ( + f"The new password {password} should not work, but it does" + ) + if pwd_change: + output = self.dw_api.run_sql_command( + container_ip=cip, + username=username, + password="pwdfoo", + container_id=VARS.IMAGE_NAME, + ignore_error=True, + ) assert f"Access denied for user '{username}'@" in output, ( f"The old password {password} should not work, but it does" ) diff --git a/test/test_container_replication.py b/test/test_container_replication.py index 68a33c11..6e97b2b2 100644 --- a/test/test_container_replication.py +++ b/test/test_container_replication.py @@ -1,10 +1,9 @@ -import tempfile import re + from time import sleep from container_ci_suite.container_lib import ContainerTestLib from container_ci_suite.engines.database import DatabaseWrapper -from container_ci_suite.engines.podman_wrapper import PodmanCLIWrapper from conftest import VARS From cf47c9b3281ec9bfdfcd4599a4b7798aeeb56b99 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Fri, 27 Feb 2026 13:49:20 +0100 Subject: [PATCH 7/7] Fixing OpenShift 4 PyTest. Use conftest.py and VARS tuple from this. Signed-off-by: Petr "Stone" Hracek --- test/test_ocp_mysql_imagestream.py | 32 ++++--------------- test/test_ocp_mysql_imagestream_template.py | 30 ++++------------- test/test_ocp_mysql_latest_imagestreams.py | 14 ++------ test/test_ocp_mysql_local_template.py | 28 ++++------------ ...test_ocp_mysql_shared_helm_imagestreams.py | 19 +++++------ 5 files changed, 32 insertions(+), 91 deletions(-) diff --git a/test/test_ocp_mysql_imagestream.py b/test/test_ocp_mysql_imagestream.py index fed19dd6..b4b4c836 100644 --- a/test/test_ocp_mysql_imagestream.py +++ b/test/test_ocp_mysql_imagestream.py @@ -1,46 +1,28 @@ -import os -import sys - import pytest from container_ci_suite.openshift import OpenShiftAPI -from container_ci_suite.utils import check_variables - -from constants import TAGS -if not check_variables(): - print("At least one variable from IMAGE_NAME, OS, VERSION is missing.") - sys.exit(1) - -VERSION = os.getenv("VERSION") -IMAGE_NAME = os.getenv("IMAGE_NAME") -OS = os.getenv("TARGET") -TAG = TAGS.get(OS) +from conftest import VARS class TestMySQLImagestreamTemplate: - def setup_method(self): - self.oc_api = OpenShiftAPI(pod_name_prefix="mysql", version=VERSION, shared_cluster=True) + self.oc_api = OpenShiftAPI( + pod_name_prefix="mysql", version=VARS.VERSION, shared_cluster=True + ) def teardown_method(self): self.oc_api.delete_project() @pytest.mark.parametrize( - "template", - [ - "mysql-ephemeral-template.json", - "mysql-persistent-template.json" - ] + "template", ["mysql-ephemeral-template.json", "mysql-persistent-template.json"] ) def test_imagestream_template(self, template): - os_name = ''.join(i for i in OS if not i.isdigit()) + os_name = "".join(i for i in VARS.OS if not i.isdigit()) assert self.oc_api.deploy_image_stream_template( imagestream_file=f"imagestreams/mysql-{os_name}.json", template_file=f"examples/{template}", app_name=self.oc_api.pod_name_prefix, - openshift_args=[ - f"MYSQL_VERSION={VERSION}{TAG}" - ] + openshift_args=[f"MYSQL_VERSION={VARS.VERSION}{VARS.TAG}"], ) assert self.oc_api.is_pod_running(pod_name_prefix=self.oc_api.pod_name_prefix) diff --git a/test/test_ocp_mysql_imagestream_template.py b/test/test_ocp_mysql_imagestream_template.py index 9442953c..75429805 100644 --- a/test/test_ocp_mysql_imagestream_template.py +++ b/test/test_ocp_mysql_imagestream_template.py @@ -1,43 +1,27 @@ -import os -import sys - import pytest from container_ci_suite.openshift import OpenShiftAPI -from container_ci_suite.utils import check_variables - -from constants import TAGS -if not check_variables(): - print("At least one variable from IMAGE_NAME, OS, VERSION is missing.") - sys.exit(1) - -VERSION = os.getenv("VERSION") -IMAGE_NAME = os.getenv("IMAGE_NAME") -OS = os.getenv("TARGET") -TAG = TAGS.get(OS) +from conftest import VARS class TestMySQLImagestreamTemplate: - def setup_method(self): - self.oc_api = OpenShiftAPI(pod_name_prefix="mysql", version=VERSION, shared_cluster=True) + self.oc_api = OpenShiftAPI( + pod_name_prefix="mysql", version=VARS.VERSION, shared_cluster=True + ) def teardown_method(self): self.oc_api.delete_project() @pytest.mark.parametrize( - "template", - [ - "mysql-ephemeral-template.json", - "mysql-persistent-template.json" - ] + "template", ["mysql-ephemeral-template.json", "mysql-persistent-template.json"] ) def test_imagestream_template(self, template): - os_name = ''.join(i for i in OS if not i.isdigit()) + os_name = "".join(i for i in VARS.OS if not i.isdigit()) assert self.oc_api.deploy_image_stream_template( imagestream_file=f"imagestreams/mysql-{os_name}.json", template_file=f"examples/{template}", - app_name=self.oc_api.pod_name_prefix + app_name=self.oc_api.pod_name_prefix, ) assert self.oc_api.is_pod_running(pod_name_prefix=self.oc_api.pod_name_prefix) diff --git a/test/test_ocp_mysql_latest_imagestreams.py b/test/test_ocp_mysql_latest_imagestreams.py index fa460341..b357060f 100644 --- a/test/test_ocp_mysql_latest_imagestreams.py +++ b/test/test_ocp_mysql_latest_imagestreams.py @@ -1,21 +1,11 @@ -import os -import sys - -from pathlib import Path - from container_ci_suite.imagestreams import ImageStreamChecker -from container_ci_suite.utils import check_variables - -TEST_DIR = Path(os.path.abspath(os.path.dirname(__file__))) -if not check_variables(): - print("At least one variable from IMAGE_NAME, OS, VERSION is missing.") - sys.exit(1) +from conftest import VARS class TestLatestImagestreams: def setup_method(self): - self.isc = ImageStreamChecker(working_dir=TEST_DIR.parent) + self.isc = ImageStreamChecker(working_dir=VARS.TEST_DIR.parent) def test_latest_imagestream(self): self.latest_version = self.isc.get_latest_version() diff --git a/test/test_ocp_mysql_local_template.py b/test/test_ocp_mysql_local_template.py index 6beb4874..3bb3a15b 100644 --- a/test/test_ocp_mysql_local_template.py +++ b/test/test_ocp_mysql_local_template.py @@ -1,30 +1,14 @@ -import os -import sys - import pytest from container_ci_suite.openshift import OpenShiftAPI -from container_ci_suite.utils import check_variables - -from constants import TAGS - -if not check_variables(): - print("At least one variable from IMAGE_NAME, OS, VERSION is missing.") - sys.exit(1) - - -VERSION = os.getenv("VERSION") -IMAGE_NAME = os.getenv("IMAGE_NAME") -OS = os.getenv("TARGET") - -TAG = TAGS.get(OS) +from conftest import VARS class TestMySQLDeployTemplate: def setup_method(self): self.oc_api = OpenShiftAPI( - pod_name_prefix="mysql-testing", version=VERSION, shared_cluster=True + pod_name_prefix="mysql-testing", version=VARS.VERSION, shared_cluster=True ) self.oc_api.import_is("imagestreams/mysql-rhel.json", "", skip_check=True) @@ -35,13 +19,13 @@ def teardown_method(self): "template", ["mysql-ephemeral-template.json", "mysql-persistent-template.json"] ) def test_template_inside_cluster(self, template): - short_version = VERSION.replace(".", "") + short_version = VARS.VERSION.replace(".", "") assert self.oc_api.deploy_template_with_image( - image_name=IMAGE_NAME, + image_name=VARS.IMAGE_NAME, template=template, name_in_template="mysql", openshift_args=[ - f"MYSQL_VERSION={VERSION}{TAG}", + f"MYSQL_VERSION={VARS.VERSION}{VARS.TAG}", f"DATABASE_SERVICE_NAME={self.oc_api.pod_name_prefix}", "MYSQL_USER=testu", "MYSQL_PASSWORD=testp", @@ -51,7 +35,7 @@ def test_template_inside_cluster(self, template): assert self.oc_api.is_pod_running(pod_name_prefix=self.oc_api.pod_name_prefix) assert self.oc_api.check_command_internal( - image_name=f"registry.redhat.io/{OS}/mysql-{short_version}", + image_name=f"registry.redhat.io/{VARS.OS}/mysql-{short_version}", service_name=self.oc_api.pod_name_prefix, cmd="echo 'SELECT 42 as testval\\g' | mysql --connect-timeout=15 -h testdb -utestu -ptestp", expected_output="42", diff --git a/test/test_ocp_mysql_shared_helm_imagestreams.py b/test/test_ocp_mysql_shared_helm_imagestreams.py index 4eaa61a3..d71e71bc 100644 --- a/test/test_ocp_mysql_shared_helm_imagestreams.py +++ b/test/test_ocp_mysql_shared_helm_imagestreams.py @@ -1,22 +1,23 @@ -import os - import pytest -from pathlib import Path from container_ci_suite.helm import HelmChartsAPI -test_dir = Path(os.path.abspath(os.path.dirname(__file__))) +from conftest import VARS class TestHelmRHELMySQLImageStreams: - def setup_method(self): package_name = "redhat-mysql-imagestreams" - path = test_dir - self.hc_api = HelmChartsAPI(path=path, package_name=package_name, tarball_dir=test_dir, shared_cluster=True) + self.hc_api = HelmChartsAPI( + path=VARS.TEST_DIR, + package_name=package_name, + tarball_dir=VARS.TEST_DIR, + shared_cluster=True, + ) self.hc_api.clone_helm_chart_repo( - repo_url="https://github.com/sclorg/helm-charts", repo_name="helm-charts", - subdir="charts/redhat" + repo_url="https://github.com/sclorg/helm-charts", + repo_name="helm-charts", + subdir="charts/redhat", ) def teardown_method(self):