From 7ec2e07c4f39383c54b75dc26803ab0edab29f97 Mon Sep 17 00:00:00 2001 From: Marc Ermshaus Date: Tue, 29 Nov 2022 14:29:20 +0100 Subject: [PATCH 1/6] Update tests to work in PHP 8 Docker Compose setup --- README.md | 14 ++++------ docker-compose.yml | 10 +++---- docker/bunny/Dockerfile | 3 ++- docker/rabbitmq/entrypoint.sh | 39 ++++++++++++++++++---------- phpunit.xml | 49 +++++++++++++++-------------------- 5 files changed, 56 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 1524f45..230c387 100644 --- a/README.md +++ b/README.md @@ -211,18 +211,14 @@ There is [amqp interop](https://github.com/queue-interop/amqp-interop) compatibl ## Testing -You need access to a RabbitMQ instance to run the test suite. You can either connect to an existing instance or use the -provided Docker Compose setup to create an isolated environment, including a RabbitMQ container, to run the test suite -in. +Create client/server SSL certificates by running: -**Local RabbitMQ** +``` +$ cd test/ssl && make all && cd - +``` -- Change `TEST_RABBITMQ_CONNECTION_URI` in `phpunit.xml` to fit your environment. Then run: +You need access to a RabbitMQ instance in order to run the test suite. The easiest way is to use the provided Docker Compose setup to create an isolated environment, including a RabbitMQ container, to run the test suite in. - ``` - $ vendor/bin/phpunit - ``` - **Docker Compose** - Use Docker Compose to create a network with a RabbitMQ container and a PHP container to run the tests in. The project diff --git a/docker-compose.yml b/docker-compose.yml index c9d3367..f4d262a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,18 +18,14 @@ services: - main hostname: rabbit_node_1 ports: - - "15672:15672" - "5672:5672" + - "5673:5673" + - "15672:15672" tty: true bunny: build: docker/bunny - init: true environment: - SSL_TEST: 'yes' - SSL_CA: ssl/ca.pem - SSL_PEER_NAME: server.rmq - SSL_CLIENT_CERT: ssl/client.pem - SSL_CLIENT_KEY: ssl/client.key + TEST_RABBITMQ_CONNECTION_URI: "amqp://testuser:testpassword@bunny_rabbit_node_1_1:5672/testvhost" volumes: - .:/opt/bunny networks: diff --git a/docker/bunny/Dockerfile b/docker/bunny/Dockerfile index ec8306f..4304c13 100644 --- a/docker/bunny/Dockerfile +++ b/docker/bunny/Dockerfile @@ -1,4 +1,4 @@ -FROM php:7.4-cli +FROM php:8.1-cli RUN apt-get update \ && apt-get dist-upgrade -y \ @@ -10,6 +10,7 @@ RUN apt-get update \ RUN pecl install xdebug \ && docker-php-ext-enable xdebug +RUN echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini RUN curl --silent --show-error https://getcomposer.org/installer | php RUN mv composer.phar /usr/local/bin/composer diff --git a/docker/rabbitmq/entrypoint.sh b/docker/rabbitmq/entrypoint.sh index 47334f6..32aa26a 100755 --- a/docker/rabbitmq/entrypoint.sh +++ b/docker/rabbitmq/entrypoint.sh @@ -1,26 +1,37 @@ #!/bin/bash -set -eu +set -eux is_rabbitmq_gte_3_7_0() { [[ "3.7.0" = $(echo -e "3.7.0\n$RABBITMQ_VERSION" | sort -V | head -n1) ]] } +install_certs() { + local certs_dir="/rabbitmq-certs" + + rm -rf "${certs_dir}" + mkdir "${certs_dir}" + cp "${TEST_DATA_ROOT}"/{ca.pem,server.pem,server.key} "${certs_dir}" + chown rabbitmq "${certs_dir}"/{ca.pem,server.pem,server.key} + chmod 0400 "${certs_dir}"/{ca.pem,server.pem,server.key} +} + +install_config() { + if [[ -n "$CONFIG_NAME" ]]; then + if is_rabbitmq_gte_3_7_0; then + cp "${TEST_DATA_ROOT}/${CONFIG_NAME}.conf" /etc/rabbitmq/rabbitmq.conf + chown rabbitmq /etc/rabbitmq/rabbitmq.conf + else + cp "${TEST_DATA_ROOT}/${CONFIG_NAME}.config" /etc/rabbitmq/rabbitmq.config + chown rabbitmq /etc/rabbitmq/rabbitmq.config + fi + fi +} + TEST_DATA_ROOT=/opt/bunny/test/ssl CONFIG_NAME=${CONFIG_NAME:-} -cp ${TEST_DATA_ROOT}/{ca.pem,server.pem,server.key} /etc/rabbitmq/ -chown rabbitmq /etc/rabbitmq/{ca.pem,server.pem,server.key} -chmod 0400 /etc/rabbitmq/{ca.pem,server.pem,server.key} - -if [[ -n "$CONFIG_NAME" ]]; then - if is_rabbitmq_gte_3_7_0; then - cp ${TEST_DATA_ROOT}/${CONFIG_NAME}.conf /etc/rabbitmq/rabbitmq.conf - chown rabbitmq /etc/rabbitmq/rabbitmq.conf - else - cp ${TEST_DATA_ROOT}/${CONFIG_NAME}.config /etc/rabbitmq/rabbitmq.config - chown rabbitmq /etc/rabbitmq/rabbitmq.config - fi -fi +install_certs +install_config exec "$@" diff --git a/phpunit.xml b/phpunit.xml index ae0be8c..d9e0569 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,31 +1,24 @@ - - - - - test/Bunny - - - - - - src - - - - - - - - - - + + + + src + + + + + test/Bunny + + + + + + + + + + + From 8f4b86950dc178eed059cb889fdfaa7a6685bb62 Mon Sep 17 00:00:00 2001 From: Marc Ermshaus Date: Tue, 29 Nov 2022 14:44:31 +0100 Subject: [PATCH 2/6] Set SSL_TEST=no when matrix.ssl_test=no --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a67afc..1835b7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,6 +80,8 @@ jobs: - name: Run UnitTests if: matrix.ssl_test == 'no' run: ./vendor/bin/phpunit + env: + SSL_TEST: "no" - name: Run UnitTests if: matrix.ssl_test == 'yes' run: ./vendor/bin/phpunit From ab2edbb1c67939d172d66eb56d8f754d5ba72ce7 Mon Sep 17 00:00:00 2001 From: Marc Ermshaus Date: Tue, 29 Nov 2022 14:48:29 +0100 Subject: [PATCH 3/6] Respect SSL_TEST=no setting in tests --- test/Bunny/SSLTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bunny/SSLTest.php b/test/Bunny/SSLTest.php index edba721..de319a0 100644 --- a/test/Bunny/SSLTest.php +++ b/test/Bunny/SSLTest.php @@ -112,7 +112,7 @@ public function testConnectWithWrongPeerName() protected function getOptions() { // should we do SSL-tests - if (empty(getenv('SSL_TEST'))) { + if (empty(getenv('SSL_TEST')) || getenv('SSL_TEST') !== 'yes') { $this->markTestSkipped('Skipped due empty ENV-variable "SSL_TEST"'); } From 40f7167ef19bf9dc09a8416ab196be38c1e25b24 Mon Sep 17 00:00:00 2001 From: Marc Ermshaus Date: Tue, 29 Nov 2022 15:22:05 +0100 Subject: [PATCH 4/6] Respect SSL_TEST=client distinction in tests --- .github/workflows/ci.yml | 2 +- phpunit.xml | 8 +++++++- test/Bunny/SSLTest.php | 8 +++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1835b7e..34c5013 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,7 @@ jobs: if: matrix.ssl_test == 'client' run: ./vendor/bin/phpunit env: - SSL_TEST: "yes" + SSL_TEST: "client" SSL_CA: "ssl/ca.pem" SSL_PEER_NAME: "server.rmq" SSL_CLIENT_CERT: "ssl/client.pem" diff --git a/phpunit.xml b/phpunit.xml index d9e0569..2992277 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,7 +11,13 @@ - + + diff --git a/test/Bunny/SSLTest.php b/test/Bunny/SSLTest.php index de319a0..2cedd3a 100644 --- a/test/Bunny/SSLTest.php +++ b/test/Bunny/SSLTest.php @@ -78,7 +78,9 @@ public function testConnectWithMissingClientCert() // let's try without client certificate - it should fail unset($options['ssl']['local_cert'], $options['ssl']['local_pk']); - $this->expectException(ClientException::class); + if (getenv('SSL_TEST') === 'client') { + $this->expectException(ClientException::class); + } $client = $this->helper->createClient($options); $client->connect(); @@ -112,8 +114,8 @@ public function testConnectWithWrongPeerName() protected function getOptions() { // should we do SSL-tests - if (empty(getenv('SSL_TEST')) || getenv('SSL_TEST') !== 'yes') { - $this->markTestSkipped('Skipped due empty ENV-variable "SSL_TEST"'); + if (empty(getenv('SSL_TEST')) || !in_array(getenv('SSL_TEST'), ['yes', 'client'], true)) { + $this->markTestSkipped('Skipped because env var SSL_TEST not set to "yes" or "client"'); } // checking CA-file From 55765e5cc6e92dde8656c097d84785f500c72c59 Mon Sep 17 00:00:00 2001 From: Marc Ermshaus Date: Tue, 29 Nov 2022 15:38:32 +0100 Subject: [PATCH 5/6] Remove default values for env variables in phpunit.xml --- .github/workflows/ci.yml | 3 +++ docker-compose.yml | 5 +++++ phpunit.xml | 9 --------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34c5013..7d78f24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,7 @@ jobs: run: ./vendor/bin/phpunit env: SSL_TEST: "no" + TEST_RABBITMQ_CONNECTION_URI: "amqp://guest:guest@localhost:5672/" - name: Run UnitTests if: matrix.ssl_test == 'yes' run: ./vendor/bin/phpunit @@ -89,6 +90,7 @@ jobs: SSL_TEST: "yes" SSL_CA: "ssl/ca.pem" SSL_PEER_NAME: "server.rmq" + TEST_RABBITMQ_CONNECTION_URI: "amqp://guest:guest@localhost:5672/" - name: Run UnitTests if: matrix.ssl_test == 'client' run: ./vendor/bin/phpunit @@ -98,6 +100,7 @@ jobs: SSL_PEER_NAME: "server.rmq" SSL_CLIENT_CERT: "ssl/client.pem" SSL_CLIENT_KEY: "ssl/client.key" + TEST_RABBITMQ_CONNECTION_URI: "amqp://guest:guest@localhost:5672/" - name: Docker Logs if: ${{ failure() }} run: docker logs rabbitmq diff --git a/docker-compose.yml b/docker-compose.yml index f4d262a..52a6284 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,11 @@ services: bunny: build: docker/bunny environment: + SSL_TEST: "client" + SSL_CA: "ssl/ca.pem" + SSL_CLIENT_CERT: "ssl/client.pem" + SSL_CLIENT_KEY: "ssl/client.key" + SSL_PEER_NAME: "server.rmq" TEST_RABBITMQ_CONNECTION_URI: "amqp://testuser:testpassword@bunny_rabbit_node_1_1:5672/testvhost" volumes: - .:/opt/bunny diff --git a/phpunit.xml b/phpunit.xml index 2992277..dbac131 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -17,14 +17,5 @@ - "yes" -> run all SSL tests, do *not* expect peer cert (c.f. rabbitmq.ssl.verify_none.conf) - "no" (or other) -> skip SSL-related tests --> - - - - - - - - From c099a4538c35741476cc5e59d6fe8320ba7d7365 Mon Sep 17 00:00:00 2001 From: Marc Ermshaus Date: Tue, 29 Nov 2022 16:33:47 +0100 Subject: [PATCH 6/6] Encapsulate env var read access in class for better documentation --- phpunit.xml | 8 --- test/Bunny/SSLTest.php | 24 ++++----- test/Library/AsynchronousClientHelper.php | 2 +- test/Library/Environment.php | 65 +++++++++++++++++++++++ test/Library/EnvironmentException.php | 15 ++++++ test/Library/SynchronousClientHelper.php | 2 +- 6 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 test/Library/Environment.php create mode 100644 test/Library/EnvironmentException.php diff --git a/phpunit.xml b/phpunit.xml index dbac131..d353aad 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,12 +10,4 @@ test/Bunny - - - diff --git a/test/Bunny/SSLTest.php b/test/Bunny/SSLTest.php index 2cedd3a..19ecc7a 100644 --- a/test/Bunny/SSLTest.php +++ b/test/Bunny/SSLTest.php @@ -6,6 +6,7 @@ use Bunny\Exception\ClientException; use Bunny\Test\Exception\TimeoutException; use Bunny\Test\Library\AsynchronousClientHelper; +use Bunny\Test\Library\Environment; use Bunny\Test\Library\SynchronousClientHelper; use PHPUnit\Framework\TestCase; @@ -13,7 +14,6 @@ use function dirname; use function file_exists; -use function getenv; use function is_file; use function putenv; @@ -78,7 +78,7 @@ public function testConnectWithMissingClientCert() // let's try without client certificate - it should fail unset($options['ssl']['local_cert'], $options['ssl']['local_pk']); - if (getenv('SSL_TEST') === 'client') { + if (Environment::getSslTest() === 'client') { $this->expectException(ClientException::class); } @@ -114,26 +114,20 @@ public function testConnectWithWrongPeerName() protected function getOptions() { // should we do SSL-tests - if (empty(getenv('SSL_TEST')) || !in_array(getenv('SSL_TEST'), ['yes', 'client'], true)) { + if (!in_array(Environment::getSslTest(), ['yes', 'client'], true)) { $this->markTestSkipped('Skipped because env var SSL_TEST not set to "yes" or "client"'); } // checking CA-file - $caFile = getenv('SSL_CA'); - if (empty($caFile)) { - $this->fail('Missing CA file ENV-variable: "SSL_CA"'); - } + $caFile = Environment::getSslCa(); + $testsDir = dirname(__DIR__); $caFile = $testsDir . '/' . $caFile; if (!file_exists($caFile) || !is_file($caFile)) { $this->fail('Missing CA file: "' . $caFile . '"'); } - $peerName = getenv('SSL_PEER_NAME'); - if (empty($peerName)) { - // setting default value from tests/ssl/Makefile - $peerName = 'server.rmq'; - } + $peerName = Environment::getSslPeerName(); // minimal SSL-options $options = [ @@ -147,8 +141,9 @@ protected function getOptions() ]; - $certFile = getenv('SSL_CLIENT_CERT'); - $keyFile = getenv('SSL_CLIENT_KEY'); + $certFile = Environment::getSslClientCert(); + $keyFile = Environment::getSslClientKey(); + if (!empty($certFile) && !empty($keyFile)) { $certFile = $testsDir . '/' . $certFile; $keyFile = $testsDir . '/' . $keyFile; @@ -161,6 +156,7 @@ protected function getOptions() $options['ssl']['local_cert'] = $certFile; $options['ssl']['local_pk'] = $keyFile; } + return $options; } } diff --git a/test/Library/AsynchronousClientHelper.php b/test/Library/AsynchronousClientHelper.php index a112442..691854c 100644 --- a/test/Library/AsynchronousClientHelper.php +++ b/test/Library/AsynchronousClientHelper.php @@ -29,7 +29,7 @@ public function getDefaultOptions(): array { $options = []; - $options = array_merge($options, $this->parseAmqpUri(getenv('TEST_RABBITMQ_CONNECTION_URI'))); + $options = array_merge($options, $this->parseAmqpUri(Environment::getTestRabbitMqConnectionUri())); return $options; } diff --git a/test/Library/Environment.php b/test/Library/Environment.php new file mode 100644 index 0000000..817f34f --- /dev/null +++ b/test/Library/Environment.php @@ -0,0 +1,65 @@ + run all SSL tests, expect peer cert (c.f. rabbitmq.ssl.verify_peer.conf) + * - "yes" -> run all SSL tests, do *not* expect peer cert (c.f. rabbitmq.ssl.verify_none.conf) + * - "no" (default) -> skip SSL-related tests + */ + public static function getSslTest(): string + { + $value = self::getenv('SSL_TEST', 'no'); + + switch ($value) { + case 'client': + case 'no': + case 'yes': + return $value; + } + + throw new EnvironmentException('SSL_TEST'); + } + + public static function getTestRabbitMqConnectionUri(): string + { + return trim(self::getenv('TEST_RABBITMQ_CONNECTION_URI')); + } + + private static function getenv(string $envVariable, ?string $default = null):string + { + $value = getenv($envVariable); + + if ($value === false && $default === null) { + throw new EnvironmentException($envVariable); + } + + return $value!==false?$value:$default; + } +} diff --git a/test/Library/EnvironmentException.php b/test/Library/EnvironmentException.php new file mode 100644 index 0000000..7693a75 --- /dev/null +++ b/test/Library/EnvironmentException.php @@ -0,0 +1,15 @@ +parseAmqpUri(getenv('TEST_RABBITMQ_CONNECTION_URI'))); + $options = array_merge($options, $this->parseAmqpUri(Environment::getTestRabbitMqConnectionUri())); return $options; }