diff --git a/dev-support/Jenkinsfile b/dev-support/Jenkinsfile index 9200f5aa5593..2bed8ec51c42 100644 --- a/dev-support/Jenkinsfile +++ b/dev-support/Jenkinsfile @@ -39,7 +39,6 @@ pipeline { OUTPUT_DIR_RELATIVE_JDK11_HADOOP3 = 'output-jdk11-hadoop3' OUTPUT_DIR_RELATIVE_JDK17_HADOOP3 = 'output-jdk17-hadoop3' OUTPUT_DIR_RELATIVE_JDK21_HADOOP3 = 'output-jdk21-hadoop3' - OUTPUT_DIR_RELATIVE_READ_REPLICA = 'output-read-replica' PROJECT = 'hbase' PROJECT_PERSONALITY = 'https://raw.githubusercontent.com/apache/hbase/master/dev-support/hbase-personality.sh' @@ -138,7 +137,6 @@ pipeline { stash name: 'jdk11-hadoop3-result', allowEmpty: true, includes: "${OUTPUT_DIR_RELATIVE_JDK11_HADOOP3}/doesn't-match" stash name: 'jdk17-hadoop3-result', allowEmpty: true, includes: "${OUTPUT_DIR_RELATIVE_JDK17_HADOOP3}/doesn't-match" stash name: 'jdk21-hadoop3-result', allowEmpty: true, includes: "${OUTPUT_DIR_RELATIVE_JDK21_HADOOP3}/doesn't-match" - stash name: 'read-replica-result', allowEmpty: true, includes: "${OUTPUT_DIR_RELATIVE_READ_REPLICA}/doesn't-match" } } stage ('health checks') { @@ -804,81 +802,6 @@ pipeline { } } } - stage ('hbase read-replica feature checks') { - agent { - node { - label 'hbase' - } - } - when { - anyOf { - branch 'master' - branch 'branch-3' - } - } - environment { - BASEDIR = "${env.WORKSPACE}/component" - OUTPUT_DIR_RELATIVE = "${env.OUTPUT_DIR_RELATIVE_READ_REPLICA}" - OUTPUT_DIR = "${env.WORKSPACE}/${env.OUTPUT_DIR_RELATIVE_READ_REPLICA}" - } - steps { - sh '''#!/usr/bin/env bash - set -e - rm -rf "${OUTPUT_DIR}" && mkdir -p "${OUTPUT_DIR}" - echo '(x) {color:red}-1 read-replica checks{color}' >"${OUTPUT_DIR}/commentfile" - echo "-- Something went wrong running this stage, please [check relevant console output|${BUILD_URL}/console]." >> "${OUTPUT_DIR}/commentfile" - ''' - dir('component') { - checkout scm - } - sh '''#!/usr/bin/env bash - set -e - rm -rf "${OUTPUT_DIR}/machine" && mkdir -p "${OUTPUT_DIR}/machine" - "${BASEDIR}/dev-support/gather_machine_environment.sh" "${OUTPUT_DIR_RELATIVE}/machine" - echo "got the following saved stats in '${OUTPUT_DIR_RELATIVE}/machine'" - ls -lh "${OUTPUT_DIR_RELATIVE}/machine" - ''' - script { - def ret = sh( - returnStatus: true, - script: '''#!/usr/bin/env bash - set -e - declare -i status=0 - if "${BASEDIR}/dev-support/hbase_nightly_read_replica_test.sh" ; then - echo '(/) {color:green}+1 read-replica checks{color}' > "${OUTPUT_DIR}/commentfile" - else - echo '(x) {color:red}-1 read-replica checks{color}' > "${OUTPUT_DIR}/commentfile" - status=1 - fi - echo "-- For more information [see read-replica test report|${BUILD_URL}Read_20Replica_20Nightly_20Test_20Report/]" >> "${OUTPUT_DIR}/commentfile" - exit "${status}" - ''' - ) - if (ret != 0) { - // mark the build as UNSTABLE instead of FAILURE, to avoid skipping the later publish of - // test output. See HBASE-26339 for more details. - currentBuild.result = 'UNSTABLE' - } - } - } - post { - always { - junit testResults: "${env.OUTPUT_DIR_RELATIVE}/read-replica-nightly-test-results.xml", allowEmptyResults: true - - publishHTML target: [ - allowMissing : true, - keepAll : true, - alwaysLinkToLastBuild: true, - reportDir : "${env.OUTPUT_DIR_RELATIVE}", - reportFiles : 'read-replica-nightly-test-report.html', - reportName : 'Read Replica Nightly Test Report' - ] - - stash name: 'read-replica-result', includes: "${OUTPUT_DIR_RELATIVE}/commentfile" - archiveArtifacts artifacts: "${env.OUTPUT_DIR_RELATIVE}/**/*", allowEmptyArchive: true - } - } - } } // parallel } //stage:_health checks } //stages @@ -896,7 +819,6 @@ pipeline { rm -rf ${OUTPUT_DIR_RELATIVE_JDK11_HADOOP3} rm -rf ${OUTPUT_DIR_RELATIVE_JDK17_HADOOP3} rm -rf ${OUTPUT_DIR_RELATIVE_JDK21_HADOOP3} - rm -rf ${OUTPUT_DIR_RELATIVE_READ_REPLICA} ''' unstash 'general-result' unstash 'jdk8-hadoop2-result' @@ -904,7 +826,6 @@ pipeline { unstash 'jdk11-hadoop3-result' unstash 'jdk17-hadoop3-result' unstash 'jdk21-hadoop3-result' - unstash 'read-replica-result' def results = ["${env.OUTPUT_DIR_RELATIVE_GENERAL}/commentfile", "${env.OUTPUT_DIR_RELATIVE_JDK8_HADOOP2}/commentfile", @@ -912,7 +833,6 @@ pipeline { "${env.OUTPUT_DIR_RELATIVE_JDK11_HADOOP3}/commentfile", "${env.OUTPUT_DIR_RELATIVE_JDK17_HADOOP3}/commentfile", "${env.OUTPUT_DIR_RELATIVE_JDK21_HADOOP3}/commentfile"] - "${env.OUTPUT_DIR_RELATIVE_READ_REPLICA}/commentfile"] echo env.BRANCH_NAME echo env.BUILD_URL echo currentBuild.result diff --git a/dev-support/README.md b/dev-support/README.md index eb6cec921b43..d47780b0c2c3 100644 --- a/dev-support/README.md +++ b/dev-support/README.md @@ -60,12 +60,9 @@ configurations in this directory: - `docker/` -- Dockerfile for CI build environment - `hbase_docker/`, `hbase_docker.sh` -- Docker-based local test cluster -- `hbase_nightly_read_replica_test.sh` -- Builds a Docker image and runs two - HBase clusters (primary + read-replica) to execute end-to-end replication - integration tests via pytest. Optional `--keep-containers` and `--keep-image` - flags skip cleanup on exit. -- `read-replica/` -- Python test suite, Docker Compose configuration, and - cluster config used by `hbase_nightly_read_replica_test.sh` +- `read-replica/` -- Standalone Jenkins pipeline, test driver script + (`hbase_nightly_read_replica_test.sh`), Python test suite, Docker Compose + configuration, and cluster config for read-replica integration tests - `adhoc_run_tests/` -- Scripts for running test suites outside CI - `integration-test/` -- Integration test support diff --git a/dev-support/read-replica/.env b/dev-support/read-replica/.env index d6edb145d58f..1b36c57226b8 100644 --- a/dev-support/read-replica/.env +++ b/dev-support/read-replica/.env @@ -16,32 +16,20 @@ # under the License. # The name of the HBase Docker image -HBASE_IMAGE=${HBASE_IMAGE:-kgeisz/hbase-docker:read-replica-jenkins} +HBASE_IMAGE=${HBASE_IMAGE:-default/hbase-docker:read-replica-jenkins} # The name of the HBase docker container HBASE_CONTAINER_NAME=hbase-docker -# This is the host running the hbase-docker containers. Use localhost if the containers -# are running locally. If they are started by another container, such as a Jenkins -# container in a Docker-out-of-Docker setup, then try setting this to host.docker.internal. -HBASE_HOST=${HBASE_HOST:-localhost} # The directory within the docker container that contains the config files HBASE_CONF_DIR=/opt/hbase/conf # The directory containing the 'data-store/' directory. # This directory is mounted with the HBase Docker container. HBASE_DATA_STORE_ROOT=./tmp-read-replica-data -# The port for the active cluster's HBase UI (used for cluster readiness) -ACTIVE_CLUSTER_PORT=16010 -# The port for the replica cluster's HBase UI (used for cluster readiness) -REPLICA_CLUSTER_PORT=26010 # Local path to the active cluster's HBase config file. # This file is part of a mounted volume, so modifying it # locally also modifies it within the container (and vise-versa). ACTIVE_CLUSTER_CONF_DIR=${HBASE_ROOT}/dev-support/read-replica/cluster1/conf -# Local path to docker volume where HBase sends active cluster logs -ACTIVE_CLUSTER_LOGS_DIR=${HBASE_ROOT}/dev-support/read-replica/cluster1/logs # Same as above, except for the replica cluster REPLICA_CLUSTER_CONF_DIR=${HBASE_ROOT}/dev-support/read-replica/cluster2/conf -# Local path to docker volume where HBase sends replica cluster logs -REPLICA_CLUSTER_LOGS_DIR=${HBASE_ROOT}/dev-support/read-replica/cluster2/logs # The path to the docker-compose file DOCKER_COMPOSE_FILE=${HBASE_ROOT}/dev-support/read-replica/docker-compose.yml # The location of the utils directory within the docker container diff --git a/dev-support/read-replica/Jenkinsfile b/dev-support/read-replica/Jenkinsfile new file mode 100644 index 000000000000..feea5f7f03fa --- /dev/null +++ b/dev-support/read-replica/Jenkinsfile @@ -0,0 +1,211 @@ +// 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. +pipeline { + agent { + node { + label 'hbase' + } + } + triggers { + pollSCM(getCronParams(env.BRANCH_NAME)) + } + options { + buildDiscarder(logRotator(numToKeepStr: '20')) + timeout (time: 2, unit: 'HOURS') + timestamps() + skipDefaultCheckout() + disableConcurrentBuilds() + } + environment { + OUTPUT_DIR_RELATIVE_READ_REPLICA = 'output-read-replica' + } + parameters { + booleanParam(name: 'DEBUG', defaultValue: false, description: 'Produce a lot more meta-information.') + } + stages { + stage ('scm-checkout') { + steps { + dir('component') { + checkout scm + } + } + } + stage ('init health results') { + steps { + stash name: 'read-replica-result', allowEmpty: true, includes: "${OUTPUT_DIR_RELATIVE_READ_REPLICA}/doesn't-match" + } + } + stage ('hbase read-replica feature checks') { + when { + anyOf { + branch 'master' + branch 'branch-3*' + } + } + environment { + BASEDIR = "${env.WORKSPACE}/component" + OUTPUT_DIR_RELATIVE = "${env.OUTPUT_DIR_RELATIVE_READ_REPLICA}" + OUTPUT_DIR = "${env.WORKSPACE}/${env.OUTPUT_DIR_RELATIVE_READ_REPLICA}" + } + steps { + sh '''#!/usr/bin/env bash + set -e + rm -rf "${OUTPUT_DIR}" && mkdir -p "${OUTPUT_DIR}" + echo '(x) {color:red}-1 read-replica checks{color}' >"${OUTPUT_DIR}/commentfile" + echo "-- Something went wrong running this stage, please [check relevant console output|${BUILD_URL}/console]." >> "${OUTPUT_DIR}/commentfile" + ''' + dir('component') { + checkout scm + } + sh '''#!/usr/bin/env bash + set -e + rm -rf "${OUTPUT_DIR}/machine" && mkdir -p "${OUTPUT_DIR}/machine" + "${BASEDIR}/dev-support/gather_machine_environment.sh" "${OUTPUT_DIR_RELATIVE}/machine" + echo "got the following saved stats in '${OUTPUT_DIR_RELATIVE}/machine'" + ls -lh "${OUTPUT_DIR_RELATIVE}/machine" + ''' + script { + def ret = sh( + returnStatus: true, + script: '''#!/usr/bin/env bash + set -e + declare -i status=0 + if "${BASEDIR}/dev-support/read-replica/hbase_nightly_read_replica_test.sh" ; then + echo '(/) {color:green}+1 read-replica checks{color}' > "${OUTPUT_DIR}/commentfile" + else + echo '(x) {color:red}-1 read-replica checks{color}' > "${OUTPUT_DIR}/commentfile" + status=1 + fi + echo "-- For more information [see read-replica test report|${BUILD_URL}Read_20Replica_20Nightly_20Test_20Report/]" >> "${OUTPUT_DIR}/commentfile" + exit "${status}" + ''' + ) + if (ret != 0) { + // mark the build as UNSTABLE instead of FAILURE to allow post processing of test reports (HBASE-26339) + currentBuild.result = 'UNSTABLE' + } + } + } + post { + always { + junit testResults: "${env.OUTPUT_DIR_RELATIVE}/read-replica-nightly-test-results.xml", allowEmptyResults: true + + publishHTML target: [ + allowMissing : true, + keepAll : true, + alwaysLinkToLastBuild: true, + reportDir : "${env.OUTPUT_DIR_RELATIVE}", + reportFiles : 'read-replica-nightly-test-report.html', + reportName : 'Read Replica Nightly Test Report' + ] + + stash name: 'read-replica-result', includes: "${OUTPUT_DIR_RELATIVE}/commentfile" + archiveArtifacts artifacts: "${env.OUTPUT_DIR_RELATIVE}/**/*", allowEmptyArchive: true + } + } + } + } + post { + always { + script { + try { + sh "printenv" + sh ''' + echo "Clean up result directories" + rm -rf ${OUTPUT_DIR_RELATIVE_READ_REPLICA} + ''' + unstash 'read-replica-result' + + def results = ["${env.OUTPUT_DIR_RELATIVE_READ_REPLICA}/commentfile"] + echo env.BRANCH_NAME + echo env.BUILD_URL + echo currentBuild.result + echo currentBuild.durationString + def comment = "Results for branch ${env.BRANCH_NAME}\n" + comment += "\t[build ${currentBuild.displayName} on builds.a.o|${env.BUILD_URL}]: " + if (currentBuild.result == null || currentBuild.result == "SUCCESS") { + comment += "(/) *{color:green}+1 overall{color}*\n" + } else { + comment += "(x) *{color:red}-1 overall{color}*\n" + } + comment += "----\ndetails (if available):\n\n" + echo "" + echo "[DEBUG] trying to aggregate step-wise results" + comment += results.collect { fileExists(file: it) ? readFile(file: it) : "" }.join("\n\n") + echo "[INFO] Comment:" + echo comment + echo "" + echo "[DEBUG] checking to see if feature branch" + def jiras = getJirasToComment(env.BRANCH_NAME, []) + if (jiras.isEmpty()) { + echo "[DEBUG] non-feature branch, checking change messages for jira keys." + echo "[INFO] There are ${currentBuild.changeSets.size()} change sets." + jiras = getJirasToCommentFromChangesets(currentBuild) + } + jiras.each { currentIssue -> + jiraComment issueKey: currentIssue, body: comment + } + } catch (Exception exception) { + echo "Got exception: ${exception}" + echo " ${exception.getStackTrace()}" + } + } + } + } +} +import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper +@NonCPS +List getJirasToCommentFromChangesets(RunWrapper thisBuild) { + def seenJiras = [] + thisBuild.changeSets.each { cs -> + cs.getItems().each { change -> + CharSequence msg = change.msg + echo "change: ${change}" + echo " ${msg}" + echo " ${change.commitId}" + echo " ${change.author}" + echo "" + seenJiras = getJirasToComment(msg, seenJiras) + } + } + return seenJiras +} +@NonCPS +List getJirasToComment(CharSequence source, List seen) { + source.eachMatch("HBASE-[0-9]+") { currentIssue -> + echo "[DEBUG] found jira key: ${currentIssue}" + if (currentIssue in seen) { + echo "[DEBUG] already commented on ${currentIssue}." + } else { + echo "[INFO] commenting on ${currentIssue}." + seen << currentIssue + } + } + return seen +} +@NonCPS +String getCronParams(String branchName) { + if (branchName == 'master') { + return 'H H 1-31/3 * *' + } else if (branchName == 'branch-3') { + return 'H H 1-31/3 * *' + } else if (branchName == 'branch-2') { + return 'H H 2-31/3 * *' + } else { + return 'H H 3-31/3 * *' + } +} diff --git a/dev-support/read-replica/README.md b/dev-support/read-replica/README.md index e4b5c3b436a8..0b6821021c2b 100644 --- a/dev-support/read-replica/README.md +++ b/dev-support/read-replica/README.md @@ -113,13 +113,13 @@ read-replica/ ## CI: Jenkins Nightly Pipeline **Files:** -- `dev-support/Jenkinsfile` — stage definition (`hbase read-replica feature checks`) -- `dev-support/hbase_nightly_read_replica_test.sh` — test driver script +- `dev-support/read-replica/Jenkinsfile` — pipeline definition (`hbase read-replica feature checks`) +- `dev-support/read-replica/hbase_nightly_read_replica_test.sh` — test driver script ### When It Runs -The read-replica stage runs as part of the HBase nightly build on the `master` and `branch-3` -branches. It executes in parallel alongside the other nightly check stages (Yetus, JDK8/11/17). +The read-replica tests run as their own standalone nightly pipeline on the `master` and `branch-3` +branches, separate from the main HBase nightly build. ### What the Test Driver Does @@ -128,7 +128,7 @@ branches. It executes in parallel alongside the other nightly check stages (Yetu | # | Step | Description | |---|------|-------------| | 1 | Clone HBase source | `git clone --local` into `read-replica/hbase/` for the Docker build context (Docker COPY can't follow symlinks) | -| 2 | Source `.env` and clean old logs | Loads environment variables and removes log directories from prior runs | +| 2 | Source `.env` | Loads environment variables | | 3 | Register cleanup trap | On exit: runs `docker compose down` (unless `--keep-containers`), removes the Docker image (unless `--keep-image`), and deletes the cloned source | | 4 | Copy Protobuf | Copies the latest `ActiveClusterSuffix.proto` from the source tree into `python/proto/` | | 5 | Set up Python environment | Creates a venv, installs dependencies from `requirements.txt` | @@ -191,7 +191,6 @@ Defines environment variables consumed by Docker Compose, the build script, and - `HBASE_IMAGE` — Docker image tag - `HBASE_DATA_STORE_ROOT` — Host path for the shared data store -- `ACTIVE_CLUSTER_PORT` / `REPLICA_CLUSTER_PORT` — Mapped HBase Master UI ports (16010 / 26010) - `DOCKER_COMPOSE_FILE` — Absolute path to `docker-compose.yml` ### Protobuf Verification @@ -208,7 +207,7 @@ useful for reproducing test failures seen in CI. ```bash # From the repo root — run the full suite -dev-support/hbase_nightly_read_replica_test.sh +dev-support/read-replica/hbase_nightly_read_replica_test.sh ``` The script accepts two flags for local debugging: @@ -220,7 +219,7 @@ The script accepts two flags for local debugging: ```bash # Keep the image and containers for debugging -dev-support/hbase_nightly_read_replica_test.sh --keep-image --keep-containers +dev-support/read-replica/hbase_nightly_read_replica_test.sh --keep-image --keep-containers ``` ### Running Manually @@ -264,17 +263,14 @@ docker compose -f docker-compose.yml down ### Mounted Volumes -The data-store directory (`tmp-read-replica-data/`) and log directories (`cluster1/logs/`, -`cluster2/logs/`) are mounted into the containers. Between `docker compose down` and -`docker compose up`, consider removing these directories to start with a clean state: +The data-store directory (`tmp-read-replica-data/`) is mounted into the containers. Between +`docker compose down` and `docker compose up`, consider removing this directory to start with +a clean state: ```bash -rm -rf tmp-read-replica-data cluster1/logs cluster2/logs +rm -rf tmp-read-replica-data ``` -The shell script automatically cleans the log directories on each run, but the data-store -directory persists across runs. - **Prerequisites:** Docker, Docker Compose, Python 3, Maven, JDK 17. ## Related diff --git a/dev-support/read-replica/docker-compose.yml b/dev-support/read-replica/docker-compose.yml index 2fd3143f8bd5..bb0544b841ff 100644 --- a/dev-support/read-replica/docker-compose.yml +++ b/dev-support/read-replica/docker-compose.yml @@ -23,30 +23,20 @@ services: image: ${HBASE_IMAGE} container_name: ${HBASE_CONTAINER_NAME} hostname: ${HBASE_CONTAINER_NAME} - extra_hosts: - - "host.docker.internal:host-gateway" - ports: - - ${ACTIVE_CLUSTER_PORT}:16010 # Master UI volumes: - ./utils:${CONTAINER_UTILS_DIR} - ${HBASE_DATA_STORE_ROOT}/data-store/hbase:/data-store/hbase - ${ACTIVE_CLUSTER_CONF_DIR}/hbase-site.xml:${HBASE_CONF_DIR}/hbase-site.xml - ${ACTIVE_CLUSTER_CONF_DIR}/zoo.cfg:${HBASE_CONF_DIR}/zoo.cfg - ${ACTIVE_CLUSTER_CONF_DIR}/log4j2.properties:${HBASE_CONF_DIR}/log4j2.properties - - ${ACTIVE_CLUSTER_LOGS_DIR}:/opt/hbase/logs hbase2: image: ${HBASE_IMAGE} container_name: ${HBASE_CONTAINER_NAME}-2 hostname: ${HBASE_CONTAINER_NAME}-2 - extra_hosts: - - "host.docker.internal:host-gateway" - ports: - - ${REPLICA_CLUSTER_PORT}:16010 # Master UI volumes: - ./utils:${CONTAINER_UTILS_DIR} - ${HBASE_DATA_STORE_ROOT}/data-store/hbase:/data-store/hbase - ${REPLICA_CLUSTER_CONF_DIR}/hbase-site.xml:${HBASE_CONF_DIR}/hbase-site.xml - ${REPLICA_CLUSTER_CONF_DIR}/zoo.cfg:${HBASE_CONF_DIR}/zoo.cfg - ${REPLICA_CLUSTER_CONF_DIR}/log4j2.properties:${HBASE_CONF_DIR}/log4j2.properties - - ${REPLICA_CLUSTER_LOGS_DIR}:/opt/hbase/logs diff --git a/dev-support/hbase_nightly_read_replica_test.sh b/dev-support/read-replica/hbase_nightly_read_replica_test.sh similarity index 86% rename from dev-support/hbase_nightly_read_replica_test.sh rename to dev-support/read-replica/hbase_nightly_read_replica_test.sh index 528f8fa8e514..b6bec403ced0 100755 --- a/dev-support/hbase_nightly_read_replica_test.sh +++ b/dev-support/read-replica/hbase_nightly_read_replica_test.sh @@ -20,10 +20,9 @@ set -e -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPLICA_DIR="${SCRIPT_DIR}/read-replica" +REPLICA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" OUTPUT_DIR="${OUTPUT_DIR:-${REPLICA_DIR}/output}" -export HBASE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +export HBASE_ROOT="$(cd "${REPLICA_DIR}/../.." && pwd)" export HBASE_IMAGE="hbase-read-replica:${BUILD_NUMBER:-local}" @@ -48,12 +47,11 @@ while [[ $# -gt 0 ]]; do esac done -echo "Script dir: ${SCRIPT_DIR}" echo "Replica dir: ${REPLICA_DIR}" echo "Output dir: ${OUTPUT_DIR}" echo "HBase root: ${HBASE_ROOT}" -echo "Changing to replica dir: REPLICA_DIR" +echo "Changing to replica dir: ${REPLICA_DIR}" cd "${REPLICA_DIR}" echo "Sourcing environment file: $(pwd)/.env" @@ -68,13 +66,6 @@ echo "DOCKER_COMPOSE_FILE=${DOCKER_COMPOSE_FILE}" echo "HBASE_DATA_STORE_ROOT=${HBASE_DATA_STORE_ROOT}" echo "realpath of HBASE_DATA_STORE_ROOT=$(realpath ${HBASE_DATA_STORE_ROOT})" -echo "Removing HBase log directories from mounted volumes that may exist from a previous test run:" -echo "ACTIVE_CLUSTER_LOGS_DIR=${ACTIVE_CLUSTER_LOGS_DIR}" -echo "REPLICA_CLUSTER_LOGS_DIR=${REPLICA_CLUSTER_LOGS_DIR}" -rm -rf "${ACTIVE_CLUSTER_LOGS_DIR}" "${REPLICA_CLUSTER_LOGS_DIR}" -mkdir -p "${ACTIVE_CLUSTER_LOGS_DIR}" "${REPLICA_CLUSTER_LOGS_DIR}" -chmod 777 "${ACTIVE_CLUSTER_LOGS_DIR}" "${REPLICA_CLUSTER_LOGS_DIR}" - # Clone HBase source for Docker build context (Docker COPY doesn't follow symlinks) echo "Cloning HBase source into ${REPLICA_DIR}/hbase for Docker build context..." rm -rf "${REPLICA_DIR}/hbase" diff --git a/dev-support/read-replica/python/src/hbase_docker_client.py b/dev-support/read-replica/python/src/hbase_docker_client.py index 8ded58080195..6982470150e6 100644 --- a/dev-support/read-replica/python/src/hbase_docker_client.py +++ b/dev-support/read-replica/python/src/hbase_docker_client.py @@ -23,7 +23,6 @@ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError import docker -import requests import subprocess import time import xml.etree.ElementTree as ET @@ -51,15 +50,13 @@ class HBaseInitializationError(Exception): class HBaseDockerClient: def __init__(self, container_name: str, local_conf: str, hbase_ui_port: int = 16010, - cluster_name: str = "HBase Cluster", max_retries: int = 12, sleep_time: int = 5, - hbase_host: str = "localhost") -> None: + cluster_name: str = "HBase Cluster", max_retries: int = 12, sleep_time: int = 5) -> None: self._container_name = container_name self._local_conf = local_conf self._hbase_ui_port = hbase_ui_port self._cluster_name = cluster_name self._max_retries = max_retries self._sleep_time = sleep_time - self._hbase_host = hbase_host self._docker_client = docker.from_env() @property @@ -93,8 +90,6 @@ def run_docker_exec_command(self, bash_cmd: str, timeout: int | None = None) -> pool.shutdown(wait=False, cancel_futures=True) else: result = container.exec_run(cmd, demux=True) - except DockerExecCommandError: - raise except docker.errors.DockerException as e: raise DockerExecCommandError( f"The following command failed on {self._cluster_name} ({self._container_name}): {bash_cmd}\n" @@ -141,26 +136,27 @@ def _get_pid_from_jps(self, process_name: str) -> int | None: return None def wait_for_hbase_ui(self) -> bool: - """Checks for a 200 OK on the HBase Master UI.""" - # Read HBASE_HOST from environment, falling back to 'localhost' for host-native execution - url = f"http://{self._hbase_host}:{self._hbase_ui_port}" - logger.info(f"Waiting for HBase UI: {self._cluster_name} on {url}") - last_exception = None + """ + Checks for a 200 OK on the HBase Master UI inside the container using curl. + """ + check_cmd = f"curl -s -f --max-time 3 http://{self._container_name}:{self._hbase_ui_port} > /dev/null" + + logger.info(f"Waiting for HBase UI inside container: {self._cluster_name} ({self._container_name})") + last_error = None for attempt in range(1, self._max_retries + 1): try: - response = requests.get(url, timeout=self._sleep_time) - if response.status_code == 200: - logger.info(f"SUCCESS: {self._cluster_name} UI is up.") - return True - except requests.exceptions.RequestException as e: - last_exception = e - logging.info(f"Waiting {self._sleep_time} seconds before requesting HBase UI again") + self.run_docker_exec_command(check_cmd, timeout=self._sleep_time) + logger.info(f"SUCCESS: {self._cluster_name} HBase UI is up.") + return True + except DockerExecCommandError as e: + last_error = e + + logger.info(f"Waiting {self._sleep_time} seconds before checking HBase UI inside container again") time.sleep(self._sleep_time) raise HBaseInitializationError( - f"\nTIMEOUT: {self._cluster_name} UI failed to respond after " - f"{self._max_retries} attempts. " - f"Last raised exception was: {last_exception}" + f"\nTIMEOUT: {self._cluster_name} UI failed to respond inside container after " + f"{self._max_retries} attempts.\nLast error: {last_error}" ) def wait_for_master_initialization(self) -> bool: diff --git a/dev-support/read-replica/python/src/utils.py b/dev-support/read-replica/python/src/utils.py index 5a7635c78089..a7f410a8b602 100644 --- a/dev-support/read-replica/python/src/utils.py +++ b/dev-support/read-replica/python/src/utils.py @@ -67,12 +67,10 @@ def load_env_and_set_up_clients(cluster1_name: str = "Cluster 1", active_cluster = HBaseDockerClient(container_name=container_name, local_conf=f"{get_env('ACTIVE_CLUSTER_CONF_DIR')}/hbase-site.xml", - hbase_ui_port=get_env('ACTIVE_CLUSTER_PORT'), - cluster_name=cluster1_name, hbase_host=get_env('HBASE_HOST')) + cluster_name=cluster1_name) replica_cluster = HBaseDockerClient(container_name=f'{container_name}-2', local_conf=f"{get_env('REPLICA_CLUSTER_CONF_DIR')}/hbase-site.xml", - hbase_ui_port=get_env('REPLICA_CLUSTER_PORT'), - cluster_name=cluster2_name, hbase_host=get_env('HBASE_HOST')) + cluster_name=cluster2_name) return active_cluster, replica_cluster