Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docker-image-tests/percona-mysql-router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Before running set environment variables, eg.:

export DOCKER_ACC="percona"
export PS_VERSION="8.0.32-24"
export ROUTER_VERSION="8.0.32"
export TESTING_BRANCH="master"
This is based on: https://testinfra.readthedocs.io/en/latest/examples.html#test-docker-images
5 changes: 5 additions & 0 deletions docker-image-tests/percona-mysql-router/cleanup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
rm -f report.xml
rm -rf .pytest_cache
rm -rf __pycache__
rm -rf tests/__pycache__
15 changes: 15 additions & 0 deletions docker-image-tests/percona-mysql-router/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
atomicwrites==1.3.0
attrs==19.3.0
importlib-metadata==0.23
more-itertools==7.2.0
packaging==19.2
pluggy==0.13.0
py==1.10.0
pyparsing==2.4.2
pytest==5.2.1
six==1.12.0
testinfra==3.2.0
wcwidth==0.1.7
zipp==0.6.0
requests==2.27.1
docker==5.0.3
2 changes: 2 additions & 0 deletions docker-image-tests/percona-mysql-router/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
pytest -v --junit-xml report.xml $@
14 changes: 14 additions & 0 deletions docker-image-tests/percona-mysql-router/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import os

router_version = os.getenv('ROUTER_VERSION')
docker_tag = os.getenv('ROUTER_VERSION')
docker_acc = os.getenv('DOCKER_ACC')

docker_product = 'percona-mysql-router'
docker_image = docker_acc + "/" + docker_product + ":" + docker_tag
ps_pwd = 'inno'

RHEL_DISTS = ["redhat", "centos", "rhel", "oracleserver", "ol", "amzn"]

DEB_DISTS = ["debian", "ubuntu"]

Empty file.
38 changes: 38 additions & 0 deletions docker-image-tests/percona-mysql-router/tests/test_router_attr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import pytest
import subprocess
import testinfra
import json
from settings import *

container_name = 'router-docker-test-inspect'

@pytest.fixture(scope='module')
def inspect_data():
docker_id = subprocess.check_output(
['docker', 'run', '--name', container_name, '-d', docker_image], stderr=subprocess.STDOUT ).decode().strip()
inspect_data = json.loads(subprocess.check_output(['docker','inspect',container_name]))
yield inspect_data[0]
subprocess.check_call(['docker', 'rm', '-f', docker_id])


class TestContainerAttributes:
def test_status(self, inspect_data):
assert inspect_data['State']['Status'] == 'running'
assert inspect_data['State']['Running'] == True

def test_config(self, inspect_data):
assert len(inspect_data['Config']['Cmd']) == 1
assert inspect_data['Config']['Cmd'][0] == 'mysqlrouter'

def test_image_name(self, inspect_data):
assert inspect_data['Config']['Image'] == docker_image

def test_volumes(self, inspect_data):
assert len(inspect_data['Config']['Volumes']) == 1
assert '/var/lib/mysqlrouter' in inspect_data['Config']['Volumes']

def test_entrypoint(self, inspect_data):
assert len(inspect_data['Config']['Entrypoint']) == 1
assert inspect_data['Config']['Entrypoint'][0] == '/entrypoint.sh'

243 changes: 243 additions & 0 deletions docker-image-tests/percona-mysql-router/tests/test_router_static.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
#!/usr/bin/env python3
import subprocess
import time
import json
import os
import pytest
import testinfra

container_name_mysql_router = 'mysql-router'
network_name = 'innodbnet'
docker_tag = os.getenv('ROUTER_VERSION')
docker_acc = os.getenv('DOCKER_ACC')
ps_version = os.getenv('PS_VERSION')
router_docker_image = f"{docker_acc}/percona-mysql-router:{docker_tag}"
percona_docker_image = f"{docker_acc}/percona-server:{ps_version}"

def create_network():
subprocess.run(['docker', 'network', 'create', network_name], check=True)

def create_mysql_config():
for N in range(1, 5):
with open(f'my{N}.cnf', 'w') as file:
file.write(
f"[mysqld]\n"
f"plugin_load_add='group_replication.so'\n"
f"server_id={(hash(str(time.time()) + str(N))) % 40 + 10}\n"
f"binlog_checksum=NONE\n"
f"enforce_gtid_consistency=ON\n"
f"gtid_mode=ON\n"
f"relay_log=mysql{N}-relay-bin\n"
f"innodb_dedicated_server=ON\n"
f"binlog_transaction_dependency_tracking=WRITESET\n"
f"slave_preserve_commit_order=ON\n"
f"slave_parallel_type=LOGICAL_CLOCK\n"
f"transaction_write_set_extraction=XXHASH64\n"
)

def start_mysql_containers():
for N in range(1, 5):
subprocess.run([
'docker', 'run', '-d',
f'--name=mysql{N}',
f'--hostname=mysql{N}',
'--net=innodbnet',
'-v', f"{os.getcwd()}/my{N}.cnf:/etc/my.cnf",
'-e', 'MYSQL_ROOT_PASSWORD=root', percona_docker_image
], check=True)
time.sleep(60)

def create_new_user():
for N in range(1, 5):
subprocess.run([
'docker', 'exec', f'mysql{N}',
'mysql', '-uroot', '-proot',
'-e', "CREATE USER 'inno'@'%' IDENTIFIED BY 'inno'; GRANT ALL privileges ON *.* TO 'inno'@'%' with grant option; FLUSH PRIVILEGES;"
], check=True)

def verify_new_user():
for N in range(1, 5):
subprocess.run([
'docker', 'exec', f'mysql{N}',
'mysql', '-uinno', '-pinno',
'-e', "SHOW VARIABLES WHERE Variable_name = 'hostname';",
'-e', "SELECT user FROM mysql.user WHERE user = 'inno';"
], check=True)
time.sleep(30)

def docker_restart():
subprocess.run(['docker', 'restart', 'mysql1', 'mysql2', 'mysql3', 'mysql4'], check=True)
time.sleep(10)

def create_cluster():
subprocess.run([
'docker', 'exec', 'mysql1',
'mysqlsh', '-uinno', '-pinno', '--', 'dba', 'create-cluster', 'testCluster'
], check=True)

def add_slave():
try:
# Try adding the first slave with 'incremental' recovery method
result = subprocess.run([
'docker', 'exec', 'mysql1',
'mysqlsh', '-uinno', '-pinno', '--',
'cluster', 'add-instance', '--uri=inno@mysql2', '--recoveryMethod=increamental'
], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

time.sleep(120) # Wait for the first instance to finish

# Log the result of the first subprocess call
print(f"STDOUT (mysql2): {result.stdout.decode()}")
print(f"STDERR (mysql2): {result.stderr.decode()}")

# Check for GTID error and handle it
if "GTID state is not compatible" in result.stderr.decode():
print("GTID compatibility issue detected. Trying to clean the GTID state before adding the instance.")
# Here, you may want to reset GTID or handle the error.
# You could issue a command to reset GTID sets (only if this is acceptable in your case)
reset_gtid = subprocess.run([
'docker', 'exec', 'mysql2',
'mysqlsh', '-uinno', '-pinno', '--',
'dba', 'reset', '--gtid'
], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"GTID reset result: {reset_gtid.stdout.decode()}")
print(f"GTID reset error: {reset_gtid.stderr.decode()}")

# Retry adding the instance with 'incremental' recovery method
result = subprocess.run([
'docker', 'exec', 'mysql1',
'mysqlsh', '-uinno', '-pinno', '--',
'cluster', 'add-instance', '--uri=inno@mysql2', '--recoveryMethod=increamental'
], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

time.sleep(120)
print(f"STDOUT (mysql2 - retry): {result.stdout.decode()}")
print(f"STDERR (mysql2 - retry): {result.stderr.decode()}")

# Now, try adding the second instance (mysql3)
result = subprocess.run([
'docker', 'exec', 'mysql1',
'mysqlsh', '-uinno', '-pinno', '--',
'cluster', 'add-instance', '--uri=inno@mysql3', '--recoveryMethod=increamental'
], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
time.sleep(120)
print(f"STDOUT (mysql3): {result.stdout.decode()}")
print(f"STDERR (mysql3): {result.stderr.decode()}")

# Similarly, try adding the third instance (mysql4)
result = subprocess.run([
'docker', 'exec', 'mysql1',
'mysqlsh', '-uinno', '-pinno', '--',
'cluster', 'add-instance', '--uri=inno@mysql4', '--recoveryMethod=increamental'
], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
time.sleep(120)
print(f"STDOUT (mysql4): {result.stdout.decode()}")
print(f"STDERR (mysql4): {result.stderr.decode()}")

except subprocess.CalledProcessError as e:
print(f"An error occurred while adding instance: {e}")
print(f"STDOUT: {e.stdout.decode() if e.stdout else 'No output'}")
print(f"STDERR: {e.stderr.decode() if e.stderr else 'No error output'}")

@pytest.fixture(scope='module')
def host():
""" Simulates the `Router_Bootstrap` function """
# Run mysql-router container
docker_id = subprocess.check_output(
['docker', 'run', '-d', '--name', container_name_mysql_router, '--net', network_name,
'-e', 'MYSQL_HOST=mysql1', '-e', 'MYSQL_PORT=3306', '-e', 'MYSQL_USER=inno',
'-e', 'MYSQL_PASSWORD=inno', '-e', 'MYSQL_INNODB_CLUSTER_MEMBERS=4', router_docker_image]).decode().strip()
subprocess.check_call(['docker','exec','--user','root',container_name_mysql_router,'microdnf','install','net-tools'])
time.sleep(20)
yield testinfra.get_host("docker://root@" + docker_id)
subprocess.check_call(['docker', 'rm', '-f', docker_id])


#def test_data_add():
# """ Simulates the `data_add` function """
# # Start mysql-client container
# command = [
# 'docker', 'run', '-d', '--name', 'mysql-client', '--hostname', 'mysql-client', '--net', network_name,
# '-e', 'MYSQL_ROOT_PASSWORD=root', percona_docker_image
# ]
# docker_run(command)

# Give time for the container to initialize
# time.sleep(10)

# Create sbtest user and schema
# command = [
# 'docker', 'exec', '-it', 'mysql-client', 'mysql', '-h', 'mysql-router', '-P', '6446', '-uinno', '-pinno',
# '-e', "CREATE SCHEMA sbtest; CREATE USER sbtest@'%' IDENTIFIED with mysql_native_password by 'password';",
# '-e', "GRANT ALL PRIVILEGES ON sbtest.* to sbtest@'%';"
# ]
# docker_run(command)

# Verify sbtest user
# command = [
# 'docker', 'exec', '-it', 'mysql-client', 'mysql', '-h', 'mysql-router', '-P', '6447', '-uinno', '-pinno',
# '-e', "select host , user from mysql.user where user='sbtest';"
# ]
# docker_run(command)

# Run sysbench for data insertion
# command = [
# 'docker', 'run', '--rm', '--net', network_name, '--name', 'sb-prepare', 'severalnines/sysbench',
# 'sysbench', '--db-driver=mysql', '--table-size=10000', '--tables=1', '--threads=1', '--mysql-host=mysql-router',
# '--mysql-port=6446', '--mysql-user=sbtest', '--mysql-password=password', '/usr/share/sysbench/oltp_insert.lua', 'prepare'
# ]
# docker_run(command)

# Wait for the data to insert
# time.sleep(20)

# Verify if the data has been inserted
# command = [
# 'docker', 'exec', '-it', 'mysql-client', 'mysql', '-h', 'mysql-router', '-P', '6447', '-uinno', '-pinno',
# '-e', "SELECT count(*) from sbtest.sbtest1;"
# ]
# docker_run(command)

create_network()
create_mysql_config()
start_mysql_containers()
create_new_user()
verify_new_user()
docker_restart()
create_cluster()
add_slave()
#test_data_add()

class TestRouterEnvironment:
def test_mysqlrouter_version(self, host):
command = "mysqlrouter --version"
output = host.check_output(command)
assert docker_tag in output

def test_mysqlsh_version(self, host):
command = "mysqlsh --version"
output = host.check_output(command)
assert ps_version in output

def test_mysqlrouter_directory_permissions(self, test_router_bootstrap):
assert host.file('/var/lib/mysqlrouter').user == 'mysql'
assert host.file('/var/lib/mysqlrouter').group == 'mysql'
assert oct(host.file('/var/lib/mysqlrouter').mode) == '0o755'

def test_mysql_user(self, test_router_bootstrap):
mysql_user = host.user('mysql')
print(f"Username: {mysql_user.name}, UID: {mysql_user.uid}")
assert mysql_user.exists
assert mysql_user.uid == 1001

def test_mysqlrouter_ports(self, test_router_bootstrap):
host.socket("tcp://6446").is_listening
host.socket("tcp://6447").is_listening
host.socket("tcp://64460").is_listening
host.socket("tcp://64470").is_listening

def test_mysqlrouter_config(self, test_router_bootstrap):
assert host.file("/etc/mysqlrouter/mysqlrouter.conf").exists
assert host.file("/etc/mysqlrouter/mysqlrouter.conf").user == "root"
assert host.file("/etc/mysqlrouter/mysqlrouter.conf").group == "root"
assert oct(host.file("/etc/mysqlrouter/mysqlrouter.conf").mode) == "0o644"