Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
13e4be0
Initial plan
Copilot Sep 23, 2025
699c0c8
Add Docker SolrCloud BATS test with 2-node cluster and embedded ZooKe…
Copilot Sep 23, 2025
f734ba5
Enhance Docker SolrCloud test: add 3rd node, 300MB memory limit, erro…
Copilot Sep 23, 2025
f1bb301
Fix memory issues and remove queries from Docker SolrCloud test
Copilot Sep 24, 2025
de274ec
Remove warning checks and add Docker volume-based rolling upgrade test
Copilot Sep 24, 2025
d80c1e2
Address PR feedback: remove save_home_on_failure, parameterize images…
Copilot Sep 24, 2025
5a1ce45
Add Docker Compose SolrCloud test with separate ZooKeeper container
Copilot Sep 24, 2025
66875ef
packaging build.gradle integrationTests improvements
dsmiley Sep 26, 2025
535e4ab
Overhaul Docker SolrCloud test: use custom network, --rm flag, solr a…
Copilot Sep 26, 2025
b1fd519
Add Docker SolrCloud BATS test with custom networking and rolling upg…
Copilot Sep 26, 2025
490d40f
Finalize Docker SolrCloud test: remove redundant env vars, use --host…
Copilot Sep 26, 2025
6ba5863
Simplify
dsmiley Sep 26, 2025
b28decd
Fix Docker volume permissions by adding --user=solr flag to all conta…
Copilot Sep 26, 2025
769a8e4
Fix Docker volume permissions: create volumes in setup() and set prop…
Copilot Sep 26, 2025
35610fe
Revert "Fix Docker volume permissions: create volumes in setup() and …
dsmiley Sep 26, 2025
456275a
fixes
dsmiley Sep 26, 2025
19e9d23
fixes
dsmiley Sep 26, 2025
aa72b52
Add wait_for utility, fix volume mount, add healthcheck - test valida…
Copilot Sep 26, 2025
159da8c
fixes
dsmiley Sep 26, 2025
a371565
Better diagnostics. Support Solr 10.
dsmiley Sep 26, 2025
9132401
More logging
dsmiley Sep 26, 2025
8068314
to enable to run on apple silicon, need to specify platform
epugh Dec 16, 2025
f7718ab
Use more bats idiomatic pattern for asserting.
epugh Dec 16, 2025
20ec9db
Missing platform request
epugh Dec 16, 2025
8280cfa
Merge remote-tracking branch 'upstream/main' into pr/3706
epugh Dec 16, 2025
5875be2
refactoring of helpers
epugh Dec 16, 2025
8f02891
Avoid failing bats test when timeout not installed.
epugh Dec 16, 2025
54eaf5e
simplify logic
epugh Dec 16, 2025
7d78041
Respond to comment.
epugh Dec 16, 2025
8a05ba8
more specific docs on how to override
epugh Dec 16, 2025
39e5291
Make sure indexed data is still there
epugh Dec 17, 2025
4bebd3c
Solr now has multi arch build for docker image
epugh Jan 5, 2026
6af75d4
Revert "Solr now has multi arch build for docker image"
epugh Jan 5, 2026
fcf77ac
Simplify docker missing handling.
epugh Jan 5, 2026
be2dc1d
Bring in deeper check for docker. assert more commands.
epugh Jan 5, 2026
d413f42
More accurate name of what the test is about
epugh Jan 5, 2026
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
11 changes: 7 additions & 4 deletions solr/packaging/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,14 @@ class BatsTask extends Exec {
protected void exec() {
executable "$project.ext.nodeProjectDir/node_modules/bats/bin/bats"

def batsArgs = []
if (logger.isInfoEnabled()) {
batsArgs << '--verbose-run'
}
batsArgs += ['-T', '--print-output-on-failure', '--report-formatter', 'junit', '--output', "$project.buildDir/test-output"]
// Note: tests to run must be listed after all other arguments
// Additional debugging output: -x, --verbose-run
setArgs(['-T', '--print-output-on-failure', '--report-formatter', 'junit', '--output', "$project.buildDir/test-output"] + (testFiles.empty ? testDir : testFiles))


batsArgs += testFiles.empty ? [testDir] : testFiles
setArgs(batsArgs)
super.exec()
}
}
48 changes: 14 additions & 34 deletions solr/packaging/test/bats_helper.bash
Original file line number Diff line number Diff line change
Expand Up @@ -97,41 +97,21 @@ collection_exists() {
return 1
}

# Wait for a collection to be queryable
wait_for_collection() {
local collection="$1"
local timeout=${2:-180}
local start_ts
start_ts=$(date +%s)
while true; do
if curl -s -S -f "http://localhost:${SOLR_PORT}/solr/${collection}/select?q=*:*" | grep -q '"responseHeader"'; then
# Utility function to retry a command until it succeeds or times out
wait_for() {
local timeout="${1:-30}" # Default 30 seconds timeout
local interval="${2:-1}" # Default 1 second between retries
shift 2 # Remove timeout and interval from args
local command=("$@") # Remaining args are the command to execute

local end_time=$(($(date +%s) + timeout))

while [ $(date +%s) -lt $end_time ]; do
if "${command[@]}"; then
return 0
fi
local now
now=$(date +%s)
if [ $(( now - start_ts )) -ge ${timeout} ]; then
echo "Timed out waiting for collection '${collection}' to become queryable" >&2
return 1
fi
sleep 3
sleep "$interval"
done
}

# Apply the ExtractingRequestHandler via Config API and print error body on failure
apply_extract_handler() {
local collection="$1"
local json="{\"add-requesthandler\":{\"name\":\"/update/extract\",\"class\":\"org.apache.solr.handler.extraction.ExtractingRequestHandler\",\"tikaserver.url\":\"http://localhost:${TIKA_PORT}\",\"defaults\":{\"lowernames\":\"true\",\"captureAttr\":\"true\"}}}"
local url="http://localhost:${SOLR_PORT}/solr/${collection}/config"
# Capture body and status code
local resp code body
sleep 5
resp=$(curl -s -S -w "\n%{http_code}" -X POST -H 'Content-type:application/json' -d "$json" "$url")
code="${resp##*$'\n'}"
body="${resp%$'\n'*}"
if [ "$code" = "200" ]; then
return 0
else
echo "Config API error applying ExtractingRequestHandler to ${collection} (HTTP ${code}): ${body}" >&3
return 1
fi

return 1 # Timeout reached
}
36 changes: 26 additions & 10 deletions solr/packaging/test/test_extraction.bats
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why change this test in this PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping to get both of them using better docker patterns, I'll back it out...

Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,33 @@

load bats_helper

# Apply the ExtractingRequestHandler via Config API and print error body on failure
apply_extract_handler() {
local collection="$1"
local json="{\"add-requesthandler\":{\"name\":\"/update/extract\",\"class\":\"org.apache.solr.handler.extraction.ExtractingRequestHandler\",\"tikaserver.url\":\"http://localhost:${TIKA_PORT}\",\"defaults\":{\"lowernames\":\"true\",\"captureAttr\":\"true\"}}}"
local url="http://localhost:${SOLR_PORT}/solr/${collection}/config"
# Capture body and status code
local resp code body
sleep 5
resp=$(curl -s -S -w "\n%{http_code}" -X POST -H 'Content-type:application/json' -d "$json" "$url")
code="${resp##*$'\n'}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be using a run and a assert_success type bats calls?

body="${resp%$'\n'*}"
if [ "$code" = "200" ]; then
return 0
else
echo "Config API error applying ExtractingRequestHandler to ${collection} (HTTP ${code}): ${body}" >&3
return 1
fi
}

setup_file() {
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
export TIKA_PORT=$((SOLR_PORT+5))
docker run --rm -p ${TIKA_PORT}:9998 --name bats_tika -d apache/tika:3.2.3.0-full >/dev/null 2>&1 || true
echo "Tika Server started on port ${TIKA_PORT}" >&3
else
export DOCKER_UNAVAILABLE=1
echo "WARNING: Docker not available (CLI missing or daemon not running); Tika-dependent tests will be bypassed and marked as passed." >&3
echo "WARNING: Docker not available (CLI missing or daemon not running); Tika-dependent tests will be bypassed." >&3
fi
}

Expand All @@ -51,8 +70,7 @@ teardown() {
@test "using curl to extract a single pdf file" {

if [ -n "${DOCKER_UNAVAILABLE:-}" ]; then
echo "WARNING: Docker not available; bypassing test." >&3
return 0
skip "Docker is not available"
fi

# Disable security manager to allow extraction
Expand All @@ -61,7 +79,7 @@ teardown() {
solr start -Dsolr.modules=extraction

solr create -c gettingstarted -d _default
wait_for_collection gettingstarted 30
wait_for 30 3 curl -s -S -f "http://localhost:${SOLR_PORT}/solr/gettingstarted/select?q=*:*" -o /dev/null
apply_extract_handler gettingstarted

curl "http://localhost:${SOLR_PORT}/solr/gettingstarted/update/extract?literal.id=doc1&commit=true" -F "myfile=@${SOLR_TIP}/example/exampledocs/solr-word.pdf"
Expand All @@ -73,8 +91,7 @@ teardown() {
@test "using the bin/solr post tool to extract content from pdf" {

if [ -n "${DOCKER_UNAVAILABLE:-}" ]; then
echo "WARNING: Docker not available; bypassing test." >&3
return 0
skip "Docker is not available"
fi

# Disable security manager to allow extraction
Expand All @@ -83,7 +100,7 @@ teardown() {
solr start -Dsolr.modules=extraction

solr create -c content_extraction -d _default
wait_for_collection content_extraction 30
wait_for 30 3 curl -s -S -f "http://localhost:${SOLR_PORT}/solr/content_extraction/select?q=*:*" -o /dev/null
apply_extract_handler content_extraction

# We filter to pdf to invoke the Extract handler.
Expand All @@ -99,8 +116,7 @@ teardown() {
@test "using the bin/solr post tool to crawl web site" {

if [ -n "${DOCKER_UNAVAILABLE:-}" ]; then
echo "WARNING: Docker not available; bypassing test." >&3
return 0
skip "Docker is not available"
fi

# Disable security manager to allow extraction
Expand All @@ -109,7 +125,7 @@ teardown() {
solr start -Dsolr.modules=extraction

solr create -c website_extraction -d _default
wait_for_collection website_extraction 30
wait_for 30 3 curl -s -S -f "http://localhost:${SOLR_PORT}/solr/website_extraction/select?q=*:*" -o /dev/null
apply_extract_handler website_extraction

# Change to --recursive 1 to crawl multiple pages, but may be too slow.
Expand Down
172 changes: 172 additions & 0 deletions solr/packaging/test/test_rolling_upgrade.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#!/usr/bin/env bats

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

load bats_helper

# You can test alternative images via
# export SOLR_BEGIN_IMAGE="apache/solr-nightly:9.9.0-slim" and then running
# ./gradlw iTest --tests test_docker_solrcloud.bats
SOLR_BEGIN_IMAGE="${SOLR_BEGIN_IMAGE:-apache/solr-nightly:9.10.0-SNAPSHOT-slim}"
SOLR_END_IMAGE="${SOLR_END_IMAGE:-apache/solr-nightly:10.0.0-SNAPSHOT-slim}"

setup() {
common_clean_setup

# Pre-checks
if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then
skip "Docker is not available"
fi
docker pull "$SOLR_BEGIN_IMAGE" || skip "Docker image $SOLR_BEGIN_IMAGE is not available"
docker pull "$SOLR_END_IMAGE" || skip "Docker image $SOLR_END_IMAGE is not available"

# Record test start time for scoping logs on failure
TEST_STARTED_AT_ISO=$(date -Iseconds)
export TEST_STARTED_AT_ISO

# Persist artifacts under Gradle’s test-output
ARTIFACT_DIR="${TEST_OUTPUT_DIR}/docker"
mkdir -p "$ARTIFACT_DIR"
export ARTIFACT_DIR
}

teardown() {
failed=$([[ -z "${BATS_TEST_COMPLETED:-}" ]] && [[ -z "${BATS_TEST_SKIPPED:-}" ]] && echo 1 || echo 0)
if [[ "$failed" -eq 1 ]]; then
echo "# Test failed - capturing Docker diagnostics" >&3
echo "# === docker ps (summary) ===" >&3
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}\t{{.Ports}}' >&3 2>&3 || true
fi

for container in solr-node1 solr-node2 solr-node3; do
if docker ps -a --format '{{.Names}}' | grep -q "^${container}$" 2>/dev/null; then
if [[ "$failed" -eq 1 ]]; then
echo "# === Docker logs for $container ===" >&3
docker logs --timestamps --since "$TEST_STARTED_AT_ISO" "$container" >&3 2>&3 || echo "# Failed to get logs for $container" >&3
echo "# === Docker inspect for $container ===" >&3
docker inspect "$container" | jq '.[] | {Name: .Name, State: .State, Ports: .NetworkSettings.Ports}' >&3 2>&3 || true
fi
# Persist artifacts
docker logs --timestamps "$container" >"$ARTIFACT_DIR/${container}.log" 2>&1 || true
docker inspect "$container" >"$ARTIFACT_DIR/${container}.inspect.json" 2>&1 || true
docker exec "$container" ps aux >"$ARTIFACT_DIR/${container}.ps.txt" 2>&1 || true
fi
done

echo "# Docker artifacts saved to: $ARTIFACT_DIR" >&3

docker stop solr-node1 solr-node2 solr-node3 2>/dev/null || true
docker rm solr-node1 solr-node2 solr-node3 2>/dev/null || true
docker volume rm solr-data1 solr-data2 solr-data3 2>/dev/null || true
docker network rm solrcloud-test 2>/dev/null || true
}

@test "Docker SolrCloud rolling upgrade" {
# Networking & volumes
docker network create solrcloud-test
docker volume create solr-data1
docker volume create solr-data2
docker volume create solr-data3

echo "Starting solr-node1 with embedded ZooKeeper"
docker run --name solr-node1 -d \
--network solrcloud-test \
--memory=400m \
--platform linux/amd64 \
-v solr-data1:/var/solr \
"$SOLR_BEGIN_IMAGE" solr start -f -c -m 200m --host solr-node1 -p 8983
docker exec solr-node1 solr assert --started http://solr-node1:8983 --timeout 10000

# start next 2 in parallel

echo "Starting solr-node2 connected to first node's ZooKeeper"
docker run --name solr-node2 -d \
--network solrcloud-test \
--memory=400m \
--platform linux/amd64 \
-v solr-data2:/var/solr \
"$SOLR_BEGIN_IMAGE" solr start -f -c -m 200m --host solr-node2 -p 8984 -z solr-node1:9983

echo "Started solr-node3 connected to first node's ZooKeeper"
docker run --name solr-node3 -d \
--network solrcloud-test \
--memory=400m \
--platform linux/amd64 \
-v solr-data3:/var/solr \
"$SOLR_BEGIN_IMAGE" solr start -f -c -m 200m --host solr-node3 -p 8985 -z solr-node1:9983

docker exec solr-node2 solr assert --started http://solr-node2:8984 --timeout 30000
docker exec solr-node3 solr assert --started http://solr-node3:8985 --timeout 30000

echo "Creating a Collection"
docker exec --user=solr solr-node1 solr create -c test-collection -n techproducts --shards 3

echo "Checking collection health"
wait_for 30 1 docker exec solr-node1 solr healthcheck -c test-collection

echo "Add some sample data"
docker exec --user=solr solr-node1 solr post -c test-collection example/exampledocs/mem.xml
assert_success

# Begin rolling upgrade - upgrade node 3 first (reverse order: 3, 2, 1)
echo "Starting rolling upgrade - upgrading node 3"
docker stop solr-node3
docker rm solr-node3
docker run --name solr-node3 -d \
--network solrcloud-test \
--memory=400m \
--platform linux/amd64 \
-v solr-data3:/var/solr \
"$SOLR_END_IMAGE" solr start -f -m 200m --host solr-node3 -p 8985 -z solr-node1:9983
docker exec solr-node3 solr assert --started http://solr-node3:8985 --timeout 30000
assert_success

# Upgrade node 2 second
echo "Upgrading node 2"
docker stop solr-node2
docker rm solr-node2
docker run --name solr-node2 -d \
--network solrcloud-test \
--memory=400m \
--platform linux/amd64 \
-v solr-data2:/var/solr \
"$SOLR_END_IMAGE" solr start -f -m 200m --host solr-node2 -p 8984 -z solr-node1:9983
docker exec solr-node2 solr assert --started http://solr-node2:8984 --timeout 30000
assert_success

echo "Upgrading node 1 (ZK node)"
docker stop solr-node1
docker rm solr-node1
docker run --name solr-node1 -d \
--network solrcloud-test \
--memory=400m \
--platform linux/amd64 \
-v solr-data1:/var/solr \
"$SOLR_END_IMAGE" solr start -f -m 200m --host solr-node1 -p 8983
docker exec solr-node1 solr assert --started http://solr-node1:8983 --timeout 30000
assert_success

# Final collection health check
wait_for 30 1 docker exec solr-node1 solr healthcheck -c test-collection

echo "checking cluster has exactly 3 live nodes"
run docker exec solr-node1 curl -s "http://solr-node1:8983/solr/admin/collections?action=CLUSTERSTATUS"
assert_success

local live_nodes_count=$(echo "$output" | jq -r '.cluster.live_nodes | length')
assert_equal "$live_nodes_count" "3"

}
2 changes: 2 additions & 0 deletions solr/packaging/test/test_start_solr.bats
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ teardown() {
# for start/stop/restart we parse the args separate from picking the command
# which means you don't get an error message for passing a start arg, like --jvm-opts to a stop commmand.

# Pre-check
timeout || skip "timeout utility is not available"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use the new wait_for method but didn't have any luck. So instead, let's just have better handling for when timeout isn't available.

# Set a timeout duration (in seconds)
TIMEOUT_DURATION=2

Expand Down
Loading