From 92fd3c7b63e9d6b78128ac5d4cf1da4b08c2eae3 Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 18:46:05 +0200 Subject: [PATCH 1/8] feat: better error message in case of failed raw HTTP requests to Solr --- CHANGELOG.md | 4 ++++ src/CollectionManager.php | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97db26d..e35155e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] (YYYY-mm-dd) + +- feat: better error message in case of failed raw HTTP requests to Solr + ## 2.0.0 (2024-01-04) - feat: Support Solr Cloud collection configuration during creation (#3) diff --git a/src/CollectionManager.php b/src/CollectionManager.php index fda411e..80a33b8 100644 --- a/src/CollectionManager.php +++ b/src/CollectionManager.php @@ -218,9 +218,13 @@ protected function rawApiRequest(string $path): array $base_uri = "{$scheme}://{$host}:{$port}/solr"; $url = "{$base_uri}/{$path}"; - $res = file_get_contents($url); - assert(false !== $res, "Request failed: $url"); + $res = file_get_contents($url); + if (false === $res) { + $error = error_get_last(); + $errorMessage = isset($error['message']) ? $error['message'] : 'Unknown error'; + throw new \RuntimeException("Request failed: $url. Error: $errorMessage"); + } $json = json_decode($res, true); From 493425e03a212072fb47f6fb4c0591ad2e09b581 Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 18:49:30 +0200 Subject: [PATCH 2/8] fix: authentication credentials configured for an endpoint are not used for raw HTTP requests to Solr --- CHANGELOG.md | 1 + src/CollectionManager.php | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e35155e..1603453 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] (YYYY-mm-dd) +- fix: authentication credentials configured for an endpoint are not used for raw HTTP requests to Solr - feat: better error message in case of failed raw HTTP requests to Solr ## 2.0.0 (2024-01-04) diff --git a/src/CollectionManager.php b/src/CollectionManager.php index 80a33b8..9830cf6 100644 --- a/src/CollectionManager.php +++ b/src/CollectionManager.php @@ -219,7 +219,17 @@ protected function rawApiRequest(string $path): array $url = "{$base_uri}/{$path}"; - $res = file_get_contents($url); + $auth = $endpoint->getAuthentication(); + $contextOptions = []; + if (!empty($auth['username']) && !empty($auth['password'])) { + $authHeader = 'Authorization: Basic ' . base64_encode("{$auth['username']}:{$auth['password']}"); + $contextOptions['http'] = [ + 'header' => $authHeader + ]; + } + + $context = stream_context_create($contextOptions); + $res = file_get_contents($url, false, $context); if (false === $res) { $error = error_get_last(); $errorMessage = isset($error['message']) ? $error['message'] : 'Unknown error'; From 43d7883a9bb3bc7ff60e15d0bb4ef29b13ebe20e Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 18:53:42 +0200 Subject: [PATCH 3/8] test: move to bitnami/solr for an (easier) setup that requires authenticated solr requests --- CHANGELOG.md | 8 +- docker-compose.yml | 164 ++++++++++++++++----------------- tests/Integration/TestCase.php | 5 + tests/config.php | 2 + 4 files changed, 94 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1603453..fb2bfe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] (YYYY-mm-dd) -- fix: authentication credentials configured for an endpoint are not used for raw HTTP requests to Solr -- feat: better error message in case of failed raw HTTP requests to Solr +This release ensures that authentication configuration of the Solr client is properly utilized also for raw Solr HTTP requests. +We do this by now using Solr test setup that requires authentication and thus force all our integration tests to be properly authenticated to work. + +- fix: Authentication credentials configured for an endpoint are not used for raw HTTP requests to Solr +- feat: Better error message in case of failed raw HTTP requests to Solr +- test: Move to [bitnami/solr](https://hub.docker.com/r/bitnami/solr) docker image for better authentication support ## 2.0.0 (2024-01-04) diff --git a/docker-compose.yml b/docker-compose.yml index ecbdad3..f68188e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,108 +1,106 @@ # Start a simple cluster with three ZooKeeper nodes and three Solr nodes. -# Source: https://github.com/docker-solr/docker-solr-examples/blob/master/docker-compose/docker-compose.yml +# Using the [bitnami/solr](https://hub.docker.com/r/bitnami/solr) image for easier authentication setup. + +x-zookeeper: &zookeeper + image: docker.io/bitnami/zookeeper:3.9 + ports: + - '2181' + - '2888' + - '3888' + environment: &zookeeper-environment + ZOO_SERVER_ID: 1 # NOTE: If you want to have an zookeeper ensemble, each must define their own unique ID + ALLOW_ANONYMOUS_LOGIN: yes + ZOO_4LW_COMMANDS_WHITELIST: srvr,mntr,conf,ruok + BITNAMI_DEBUG: true + + healthcheck: + test: nc -z localhost 2181 || exit -1 + interval: 30s + timeout: 5s + retries: 3 + start_period: 30s + +x-solr: &solr + image: docker.io/bitnami/solr:9.6.0 + ports: + - '8983' + depends_on: + - zoo1 + - zoo2 + - zoo3 + environment: &solr-environment + BITNAMI_DEBUG: true + + SOLR_LOG_LEVEL: WARN + SOLR_HEAP: "${SOLR_HEAP_SIZE:-512m}" + + SOLR_ZK_HOSTS: zoo1:2181,zoo2:2181,zoo3:2181 + + SOLR_ENABLE_CLOUD_MODE: yes # This will enable cloud mode and requires zookeeper (SOLR_ZK_HOSTS) + SOLR_CLOUD_BOOTSTRAP: yes # This will bootstap Solr cloud; in case of multiple Solr instances, only one of them should enable this + + SOLR_ENABLE_AUTHENTICATION: yes + + # NOTE: We need to set the SOLR_AUTH* environment variables so that solr scripts can + # still talk to the Solr instances, once authentication is enabled. + # The SOLR_AUTHENTICATION_OPTS should be specified in the Solr services that use this template. + # We don't really need this right now, but set this to avoid potential issues in the future. + # e.g. + # - Collection creation should work : docker compose exec solr-records solr create -c foo + # - Configuring invalid credentials fails collection creation : docker compose exec --env SOLR_AUTHENTICATION_OPTS='-Dbasicauth=foo:bar' solr-records solr create -c foo + SOLR_AUTH_TYPE: "basic" + SOLR_ADMIN_USERNAME: "admin" + SOLR_ADMIN_PASSWORD: "Bitnami" + SOLR_AUTHENTICATION_OPTS: "-Dbasicauth=admin:Bitnami" + + healthcheck: + # NOTE: Is available publicly, even when auth is enabled + # See `health` permission in default security.json (https://github.com/apache/solr/blob/main/solr/core/src/resources/security.json) + # See Solr docs for health endpoint: https://solr.apache.org/guide/solr/latest/configuration-guide/implicit-requesthandlers.html + test: ["CMD", "curl", "-f", "localhost:8983/api/node/health"] + start_period: 30s + interval: 30s + retries: 3 -version: '3.7' services: solr1: - image: solr:9.4 + <<: *solr container_name: solr1 ports: - "8983:8983" environment: - - ZK_HOST=zoo1:2181,zoo2:2181,zoo3:2181 - networks: - - solr - depends_on: - - zoo1 - - zoo2 - - zoo3 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8983/solr/admin/collections?action=CLUSTERSTATUS"] - interval: 1s - retries: 30 + <<: *solr-environment + solr2: - image: solr:9.4 + <<: *solr container_name: solr2 environment: - - ZK_HOST=zoo1:2181,zoo2:2181,zoo3:2181 - networks: - - solr - depends_on: - - zoo1 - - zoo2 - - zoo3 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8983/solr/admin/collections?action=CLUSTERSTATUS"] - interval: 1s - retries: 30 + <<: *solr-environment solr3: - image: solr:9.4 + <<: *solr container_name: solr3 environment: - - ZK_HOST=zoo1:2181,zoo2:2181,zoo3:2181 - networks: - - solr - depends_on: - - zoo1 - - zoo2 - - zoo3 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8983/solr/admin/collections?action=CLUSTERSTATUS"] - interval: 1s - retries: 30 + <<: *solr-environment zoo1: - image: zookeeper:3.9 - container_name: zoo1 - restart: always - hostname: zoo1 + <<: *zookeeper environment: - ZOO_MY_ID: 1 - ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 - ZOO_4LW_COMMANDS_WHITELIST: mntr, conf, ruok - ZOO_CFG_EXTRA: "metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider metricsProvider.httpPort=7000 metricsProvider.exportJvmInfo=true" - networks: - - solr - healthcheck: - test: nc -z localhost 2181 || exit -1 - interval: 1s - retries: 30 + <<: *zookeeper-environment + ZOO_SERVERS: zoo1:2888:3888,zoo2:2888:3888,zoo3:2888:3888 + ZOO_SERVER_ID: 1 zoo2: - image: zookeeper:3.9 - container_name: zoo2 - restart: always - hostname: zoo2 + <<: *zookeeper environment: - ZOO_MY_ID: 2 - ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 - ZOO_4LW_COMMANDS_WHITELIST: mntr, conf, ruok - ZOO_CFG_EXTRA: "metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider metricsProvider.httpPort=7000 metricsProvider.exportJvmInfo=true" - networks: - - solr - healthcheck: - test: nc -z localhost 2181 || exit -1 - interval: 1s - retries: 30 + <<: *zookeeper-environment + ZOO_SERVERS: zoo1:2888:3888,zoo2:2888:3888,zoo3:2888:3888 + ZOO_SERVER_ID: 2 zoo3: - image: zookeeper:3.9 - container_name: zoo3 - restart: always - hostname: zoo3 + <<: *zookeeper environment: - ZOO_MY_ID: 3 - ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 - ZOO_4LW_COMMANDS_WHITELIST: mntr, conf, ruok - ZOO_CFG_EXTRA: "metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider metricsProvider.httpPort=7000 metricsProvider.exportJvmInfo=true" - networks: - - solr - healthcheck: - test: nc -z localhost 2181 || exit -1 - interval: 1s - retries: 30 - -networks: - solr: + <<: *zookeeper-environment + ZOO_SERVERS: zoo1:2888:3888,zoo2:2888:3888,zoo3:2888:3888 + ZOO_SERVER_ID: 3 diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index 2cdf82b..77e57d3 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -53,6 +53,11 @@ protected function getManager(): CollectionManager $client = new Client($adapter, $eventDispatcher, $config); + $client->getEndpoint()->setAuthentication( + SOLR_USERNAME, + SOLR_PASSWORD, + ); + return new CollectionManager($client); } } diff --git a/tests/config.php b/tests/config.php index 2176156..5086609 100644 --- a/tests/config.php +++ b/tests/config.php @@ -4,3 +4,5 @@ define('SOLR_PORT', getenv('TEST_SOLR_PORT') ? getenv('TEST_SOLR_PORT') : '8983'); define('SOLR_PATH', getenv('TEST_SOLR_PATH') ? getenv('TEST_SOLR_PATH') : '/'); define('SOLR_TIMEOUT', getenv('TEST_SOLR_TIMEOUT') ? getenv('TEST_SOLR_TIMEOUT') : '10'); +define('SOLR_USERNAME', getenv('TEST_SOLR_USERNAME') ? getenv('TEST_SOLR_USERNAME') : 'admin'); +define('SOLR_PASSWORD', getenv('TEST_SOLR_PASSWORD') ? getenv('TEST_SOLR_PASSWORD') : 'Bitnami'); From 1bb22816e568df20e2314db03aa38b1754929dfc Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 19:09:56 +0200 Subject: [PATCH 4/8] ci: Change `docker-compose` to `docker compose` to fix failing job --- .github/workflows/ci-workflow.yml | 2 +- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index ea9ab5e..4dc8070 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -30,7 +30,7 @@ jobs: - run: composer install - name: Start SolrCloud - run: docker-compose up -d + run: docker compose up -d - name: Sleep a few seconds to wait for Solr to be ready uses: jakejarvis/wait-action@master diff --git a/CHANGELOG.md b/CHANGELOG.md index fb2bfe4..2a22d3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ We do this by now using Solr test setup that requires authentication and thus fo - fix: Authentication credentials configured for an endpoint are not used for raw HTTP requests to Solr - feat: Better error message in case of failed raw HTTP requests to Solr - test: Move to [bitnami/solr](https://hub.docker.com/r/bitnami/solr) docker image for better authentication support +- ci: Change `docker-compose` to `docker compose` to fix failing job ## 2.0.0 (2024-01-04) From 8ece32716b3e56f851b7d529c2729ef3f3d1d997 Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 19:22:10 +0200 Subject: [PATCH 5/8] ci: wait for docker compose services --- .github/workflows/ci-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 4dc8070..8202270 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -30,7 +30,7 @@ jobs: - run: composer install - name: Start SolrCloud - run: docker compose up -d + run: docker compose up -d --wait - name: Sleep a few seconds to wait for Solr to be ready uses: jakejarvis/wait-action@master From efdf7266dbd475b9b5fee60188a198a2bd7dee06 Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 19:27:03 +0200 Subject: [PATCH 6/8] test: only set SOLR_CLOUD_BOOTSTRAP for first solr instance --- docker-compose.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index f68188e..66eb978 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,7 @@ x-solr: &solr SOLR_ZK_HOSTS: zoo1:2181,zoo2:2181,zoo3:2181 SOLR_ENABLE_CLOUD_MODE: yes # This will enable cloud mode and requires zookeeper (SOLR_ZK_HOSTS) - SOLR_CLOUD_BOOTSTRAP: yes # This will bootstap Solr cloud; in case of multiple Solr instances, only one of them should enable this + # SOLR_CLOUD_BOOTSTRAP is set per service to avoid race conditions SOLR_ENABLE_AUTHENTICATION: yes @@ -70,6 +70,7 @@ services: - "8983:8983" environment: <<: *solr-environment + SOLR_CLOUD_BOOTSTRAP: yes # Only solr1 should bootstrap solr2: @@ -77,12 +78,14 @@ services: container_name: solr2 environment: <<: *solr-environment + SOLR_CLOUD_BOOTSTRAP: no # Disable bootstrap for other nodes solr3: <<: *solr container_name: solr3 environment: <<: *solr-environment + SOLR_CLOUD_BOOTSTRAP: no # Disable bootstrap for other nodes zoo1: <<: *zookeeper From 85e9a688947554aec921b3a6f1836cea83a3c745 Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 19:27:55 +0200 Subject: [PATCH 7/8] ci: remove manual 10s wait in favor of `--wait` command of docker compose --- .github/workflows/ci-workflow.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 8202270..20c432d 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -32,11 +32,6 @@ jobs: - name: Start SolrCloud run: docker compose up -d --wait - - name: Sleep a few seconds to wait for Solr to be ready - uses: jakejarvis/wait-action@master - with: - time: "10s" - - name: PHPUnit run: ./vendor/bin/phpunit --testdox --colors=always From a4231d010ddbe34738b45b7213569b8dc62aba43 Mon Sep 17 00:00:00 2001 From: Tim Sterker Date: Mon, 7 Jul 2025 20:48:41 +0200 Subject: [PATCH 8/8] docs: Changelog tag --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a22d3d..effcb38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] (YYYY-mm-dd) +## 2.1.0 (2025-07-07) This release ensures that authentication configuration of the Solr client is properly utilized also for raw Solr HTTP requests. We do this by now using Solr test setup that requires authentication and thus force all our integration tests to be properly authenticated to work.