From 706a734ecf947edfa1dabef5e40851ff45931665 Mon Sep 17 00:00:00 2001 From: Kevin Geiszler Date: Thu, 17 Sep 2026 16:27:39 -0400 Subject: [PATCH 1/6] Move dev-support/hbase_nightly_read_replica_test.sh -> dev-support/read-replica/hbase_nightly_read_replica_test.sh Change-Id: I39075ee08ed7c07fac4432015037b35411e5f50b --- .../{ => read-replica}/hbase_nightly_read_replica_test.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) rename dev-support/{ => read-replica}/hbase_nightly_read_replica_test.sh (95%) diff --git a/dev-support/hbase_nightly_read_replica_test.sh b/dev-support/read-replica/hbase_nightly_read_replica_test.sh similarity index 95% rename from dev-support/hbase_nightly_read_replica_test.sh rename to dev-support/read-replica/hbase_nightly_read_replica_test.sh index 528f8fa8e514..62860ee32b4b 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" From ec7826998d97ef38be2503c0992cdea818c12356 Mon Sep 17 00:00:00 2001 From: Kevin Geiszler Date: Thu, 17 Sep 2026 16:28:51 -0400 Subject: [PATCH 2/6] Remove read-replica stage from dev-support/Jenkinsfile; Create new dev-support/read-replica/Jenkinsfile Change-Id: Ie2b915eb663c90f64db2fc7a5eec213e59ac525d --- dev-support/Jenkinsfile | 89 +---------------- dev-support/read-replica/Jenkinsfile | 144 +++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 84 deletions(-) create mode 100644 dev-support/read-replica/Jenkinsfile diff --git a/dev-support/Jenkinsfile b/dev-support/Jenkinsfile index 9200f5aa5593..8b4e05f47d92 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') { @@ -323,7 +321,7 @@ pipeline { else echo "No archiver directory, skipping compressing." fi - ''' +''' sshPublisher(publishers: [ sshPublisherDesc(configName: 'Nightlies', transfers: [ @@ -342,7 +340,7 @@ pipeline { else echo "No test_logs.zip, skipping" fi - ''' +''' // Has to be relative to WORKSPACE. archiveArtifacts artifacts: "${env.OUTPUT_DIR_RELATIVE}/*" archiveArtifacts artifacts: "${env.OUTPUT_DIR_RELATIVE}/**/*" @@ -436,7 +434,7 @@ pipeline { else echo "No archiver directory, skipping compressing." fi - ''' +''' sshPublisher(publishers: [ sshPublisherDesc(configName: 'Nightlies', transfers: [ @@ -455,7 +453,7 @@ pipeline { else echo "No test_logs.zip, skipping" fi - ''' +''' // Has to be relative to WORKSPACE. archiveArtifacts artifacts: "${env.OUTPUT_DIR_RELATIVE}/*" archiveArtifacts artifacts: "${env.OUTPUT_DIR_RELATIVE}/**/*" @@ -584,6 +582,7 @@ pipeline { } } } + stage ('yetus jdk17 hadoop3 checks') { agent { node { @@ -804,81 +803,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 +820,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 +827,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 +834,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/read-replica/Jenkinsfile b/dev-support/read-replica/Jenkinsfile new file mode 100644 index 000000000000..fa39bbca3086 --- /dev/null +++ b/dev-support/read-replica/Jenkinsfile @@ -0,0 +1,144 @@ +// 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 any + options { + buildDiscarder(logRotator(numToKeepStr: '10')) + timeout (time: 2, unit: 'HOURS') + timestamps() + skipDefaultCheckout() + disableConcurrentBuilds() + } + environment { + OUTPUT_DIR_RELATIVE_READ_REPLICA = 'output-read-replica' + } + 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') { + 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 { + // 1. Publish JUnit XML metrics for Jenkins trend charts + junit testResults: "${env.OUTPUT_DIR_RELATIVE}/read-replica-nightly-test-results.xml", allowEmptyResults: true + + // 2. Render interactive HTML test report in the Jenkins UI navigation bar + 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] Final aggregated comment (would be posted to JIRA in production):" + echo comment + } catch (Exception exception) { + echo "Got exception: ${exception}" + echo " ${exception.getStackTrace()}" + } + } + } + } +} From 65103b5aee06a04eff3f1b582d83b3b0fd543a18 Mon Sep 17 00:00:00 2001 From: Kevin Geiszler Date: Thu, 17 Sep 2026 16:29:19 -0400 Subject: [PATCH 3/6] Update dev-support/README.md and dev-support/read-replica/README.md Change-Id: I942e63f85f314661bf5052abee227d96063b6210 --- dev-support/README.md | 9 +++------ dev-support/read-replica/README.md | 12 ++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) 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/README.md b/dev-support/read-replica/README.md index e4b5c3b436a8..f446b1fab96d 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 @@ -208,7 +208,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 +220,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 From 48770bdc289ceaf24ff1c5d48fcdc831b421376f Mon Sep 17 00:00:00 2001 From: Kevin Geiszler Date: Thu, 17 Sep 2026 17:41:36 -0400 Subject: [PATCH 4/6] Change HBASE_HOST from localhost to host.docker.internal in dev-support/read-replica/.env file Change-Id: Ib71188aab47e2e42f254125e271b31201a4f1756 --- dev-support/read-replica/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-support/read-replica/.env b/dev-support/read-replica/.env index d6edb145d58f..d738d324bcb9 100644 --- a/dev-support/read-replica/.env +++ b/dev-support/read-replica/.env @@ -22,7 +22,7 @@ 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} +HBASE_HOST=${HBASE_HOST:-host.docker.internal} # The directory within the docker container that contains the config files HBASE_CONF_DIR=/opt/hbase/conf # The directory containing the 'data-store/' directory. From 158ce4d40b447ce24b011653b6a2b6e5c8d0eea5 Mon Sep 17 00:00:00 2001 From: Kevin Geiszler Date: Fri, 18 Sep 2026 15:40:13 -0400 Subject: [PATCH 5/6] Add hbase node tabel, trigger, when condition; Match with dev-support/Jenkinsfile aside from what stages are run Change-Id: I4150f69249d748ba0d687e170bd4b70d881df62a --- dev-support/read-replica/Jenkinsfile | 77 ++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/dev-support/read-replica/Jenkinsfile b/dev-support/read-replica/Jenkinsfile index fa39bbca3086..feea5f7f03fa 100644 --- a/dev-support/read-replica/Jenkinsfile +++ b/dev-support/read-replica/Jenkinsfile @@ -15,9 +15,16 @@ // specific language governing permissions and limitations // under the License. pipeline { - agent any + agent { + node { + label 'hbase' + } + } + triggers { + pollSCM(getCronParams(env.BRANCH_NAME)) + } options { - buildDiscarder(logRotator(numToKeepStr: '10')) + buildDiscarder(logRotator(numToKeepStr: '20')) timeout (time: 2, unit: 'HOURS') timestamps() skipDefaultCheckout() @@ -26,6 +33,9 @@ pipeline { 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 { @@ -40,6 +50,12 @@ pipeline { } } 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}" @@ -86,10 +102,8 @@ pipeline { } post { always { - // 1. Publish JUnit XML metrics for Jenkins trend charts junit testResults: "${env.OUTPUT_DIR_RELATIVE}/read-replica-nightly-test-results.xml", allowEmptyResults: true - // 2. Render interactive HTML test report in the Jenkins UI navigation bar publishHTML target: [ allowMissing : true, keepAll : true, @@ -132,8 +146,19 @@ pipeline { echo "" echo "[DEBUG] trying to aggregate step-wise results" comment += results.collect { fileExists(file: it) ? readFile(file: it) : "" }.join("\n\n") - echo "[INFO] Final aggregated comment (would be posted to JIRA in production):" + 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()}" @@ -142,3 +167,45 @@ pipeline { } } } +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 * *' + } +} From d5c0d7a277e9d9bc47eaba5ebd5458eab7ee434f Mon Sep 17 00:00:00 2001 From: Kevin Geiszler Date: Mon, 21 Sep 2026 16:46:48 -0400 Subject: [PATCH 6/6] Remove HBASE_HOST and HBASE_UI ports; Check HBase UI readiness using curl within docker container Change-Id: I6d99a00334c94474173f7e926cddc421b3da5b2c --- dev-support/read-replica/.env | 8 ---- dev-support/read-replica/docker-compose.yml | 8 ---- .../python/src/hbase_docker_client.py | 40 +++++++++---------- dev-support/read-replica/python/src/utils.py | 6 +-- 4 files changed, 20 insertions(+), 42 deletions(-) diff --git a/dev-support/read-replica/.env b/dev-support/read-replica/.env index d738d324bcb9..069b8010f2b5 100644 --- a/dev-support/read-replica/.env +++ b/dev-support/read-replica/.env @@ -19,19 +19,11 @@ HBASE_IMAGE=${HBASE_IMAGE:-kgeisz/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:-host.docker.internal} # 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). diff --git a/dev-support/read-replica/docker-compose.yml b/dev-support/read-replica/docker-compose.yml index 2fd3143f8bd5..b3953cfe52a9 100644 --- a/dev-support/read-replica/docker-compose.yml +++ b/dev-support/read-replica/docker-compose.yml @@ -23,10 +23,6 @@ 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 @@ -39,10 +35,6 @@ services: 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 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..2203495d3660 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,22 +50,20 @@ 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 def name(self) -> str: return self._cluster_name - def run_docker_exec_command(self, bash_cmd: str, timeout: int | None = None) -> str: + def run_docker_exec_command(self, bash_cmd: str, timeout: int | None = None) -> str | None: """ Uses the Docker SDK to exec a Bash command in the object's Docker container. Equivalent to: docker exec bash -c @@ -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