Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
120 changes: 120 additions & 0 deletions .github/workflows/read-replica-integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Read-Replica Integration Tests

on:
pull_request:
types: [labeled, synchronize, opened, reopened, ready_for_review]

# Cancel running tests for previous commits on the same PR to save time/resources
concurrency:
group: read-replica-tests-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
read-replica-integration-test:
# Trigger only if the 'read-replica' label is attached and the PR is not a draft
if: ${{ contains(github.event.pull_request.labels.*.name, 'read-replica') && !github.event.pull_request.draft }}
env:
REPLICA_BASE_DIR: ./dev-support/integration-test/read-replica
defaults:
run:
# Change default working directory from GITHUB_WORKSPACE to this
working-directory: ${{ env.REPLICA_BASE_DIR }}
runs-on: ubuntu-latest
steps:
- name: Checkout HBase Repository with Pull Request's SHA
uses: actions/checkout@v4

- name: Checkout HBase Respoitory Again Inside read-replica Directory
uses: actions/checkout@v4
with:
# Place repo inside dev-support/integration-test/read-replica
# Use this repo to build the hbase-docker image
path: ${{ env.REPLICA_BASE_DIR }}/hbase

- name: Show Working Directory
run: |
echo "The previous default working directory was: GITHUB_WORKSPACE=${GITHUB_WORKSPACE}"
echo "The current and default working directory is now: $(pwd)"
echo "The working directory now has an HBase repo:"
ls -la
echo "Showing the most recent commit in this HBase repo:"
cd hbase && git log --oneline | head

- name: Load .env File into GitHub Actions Environment
run: |
# Turn on auto-export and source the file
set -a
source .env
set +a

# Filter out comments, grab the keys, and write the expanded values to GITHUB_ENV
grep -E "^[A-Za-z0-9_]+=" .env | cut -d= -f1 | while read -r key; do
echo "$key=${!key}"
echo "$key=${!key}" >> $GITHUB_ENV
done

- name: Set up Docker Compose
uses: docker/setup-compose-action@v2

- name: Install Python Requirements
run: |
pip install -r requirements.txt

- name: Set Python Path
run: |
echo "Setting PYTHONPATH=$(pwd) for GITHUB_ENV=${GITHUB_ENV}"
echo "PYTHONPATH=$(pwd)" >> ${GITHUB_ENV}

- name: Compile Protobuf
run: |
echo "Copying ActiveClusterSuffix.proto from HBase repo to have latest version"
cp hbase/hbase-protocol-shaded/src/main/protobuf/server/ActiveClusterSuffix.proto \
python/proto
python3 python/proto/proto_compiler.py

- name: Build Docker Image
run: |
echo "Building docker image"
./build-images.sh
echo "Listing Docker images"
docker image ls

- name: Verify Can't Start Two Active Clusters at Once
run: |
# This script automatically prepares the HBASE_DATA_STORE_ROOT defined in .env.
# This directory and its sub-directories need 777 permissions to prevent HBase
# from failing on startup. The directories are deleted when the containers are stopped
python3 python/scripts/test_dual_active_cluster_startup.py --clean-up-containers

- name: Properly Start HBase Docker Containers
run: |
# This script also prepares HBASE_DATA_STORE_ROOT, similar to the previous step
echo "Starting one active and one replica HBase cluster and waiting for them to initialize..."
python3 python/scripts/verify_hbase_start.py

- name: Test Table Create/Drop Behavior
run: python3 python/scripts/test_create_drop_behavior.py --skip-table-cleanup-on-start

- name: Test Put/Get/Delete Behavior
run: python3 python/scripts/test_put_get_delete_behavior.py --skip-table-cleanup-on-start

- name: Test Read-Only Flag Flipping
run: |
python3 python/scripts/test_read_only_flag_flipping.py --skip-container-start-or-restart

- name: Test Cannot Promote Replica to Second Active Cluster
run: |
python3 python/scripts/test_cannot_promote_second_active_cluster.py --skip-container-start-or-restart

- name: Test Bulkloaded Data and Region Splitting
run: |
python3 python/scripts/test_bulkloaded_data_and_region_splits.py --skip-container-start-or-restart

- name: Clean up
run: docker compose -f docker-compose.yml down

- name: Dump Logs on Failure
if: failure()
run: |
docker logs hbase-docker
docker logs hbase-docker-2
25 changes: 25 additions & 0 deletions dev-support/integration-test/read-replica/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# The name of the HBase Docker image
HBASE_IMAGE=kgeisz/hbase-docker:read-replica-gha
# The name of the HBase docker container
HBASE_CONTAINER_NAME=hbase-docker
# The directory within the docker container that contains the config files
HBASE_CONF_DIR=/opt/hbase/conf
# The directory containing the 'data-store/<hbase.rootdir>' directory.
# This directory is mounted with the HBase Docker container.
HBASE_DATA_STORE_ROOT=${RUNNER_TEMP}/tmp
# 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=${GITHUB_WORKSPACE}/dev-support/integration-test/read-replica/conf1
# Same as above, except for the replica cluster
REPLICA_CLUSTER_CONF_DIR=${GITHUB_WORKSPACE}/dev-support/integration-test/read-replica/conf2
# The path to the docker-compose file
DOCKER_COMPOSE_FILE=${GITHUB_WORKSPACE}/dev-support/integration-test/read-replica/docker-compose.yml
# The location of the utils directory within the docker container
CONTAINER_UTILS_DIR=/opt/utils
# The log level for the Python scripts
LOG_LEVEL=DEBUG
100 changes: 100 additions & 0 deletions dev-support/integration-test/read-replica/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Stage 0: Cache Maven dependencies
ARG BASE_IMAGE=registry.access.redhat.com/ubi8/openjdk-17
FROM ${BASE_IMAGE} AS cache-stage

USER root

# Install necessary packages for building Maven dependencies
RUN INITRD=no DEBIAN_FRONTEND=noninteractive microdnf update -y && microdnf install -y maven git hostname diffutils

# Copy the entire source code to cache dependencies
COPY ./hbase /opt/hbase-src

WORKDIR /opt/hbase-src

# Download and cache all dependencies
RUN mvn clean install -DskipTests -Dskip.license.check=true

# Stage 1: Build the HBase source code
FROM ${BASE_IMAGE} AS build-stage

USER root

# Install necessary build packages
RUN INITRD=no DEBIAN_FRONTEND=noninteractive microdnf update -y && microdnf install -y maven git hostname diffutils

# Copy the cached Maven dependencies
COPY --from=cache-stage /root/.m2 /root/.m2

# Copy the HBase source code
COPY ./hbase /opt/hbase-src

WORKDIR /opt/hbase-src

# Build HBase source code using cached dependencies and enable parallel build
RUN mvn clean package -DskipTests -Dskip.license.check=true assembly:single -T 1C

# Stage 2: Create the final Docker image
FROM ${BASE_IMAGE}

USER root

# Set environment variables
ENV HBASE_HOME=/opt/hbase
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk \
HBASE_USER=hbase \
HBASE_CONF_DIR=${HBASE_HOME}/conf \
HBASE_LIB_DIR=${HBASE_HOME}/lib \
HBASE_LOGS_DIR=${HBASE_HOME}/logs \
DATA_DIR=/data-store

# Install necessary runtime packages
RUN INITRD=no DEBIAN_FRONTEND=noninteractive microdnf update -y && microdnf install -y unzip gzip wget hostname maven git diffutils vim openssh-clients python3 procps

# Copy the built HBase binaries from the build-stage
COPY --from=build-stage /opt/hbase-src/hbase-assembly/target/hbase-4.0.0-alpha-1-SNAPSHOT-bin.tar.gz /opt/

# Extract HBase binaries
RUN tar -xzf /opt/hbase-4.0.0-alpha-1-SNAPSHOT-bin.tar.gz -C /opt \
&& ln -s /opt/hbase-4.0.0-alpha-1-SNAPSHOT /opt/hbase \
&& rm /opt/hbase-4.0.0-alpha-1-SNAPSHOT-bin.tar.gz

COPY --from=build-stage /root/.m2/repository/org/apache/hadoop/hadoop-mapreduce-client-common/*/hadoop-mapreduce-client-common-*.jar \
${HBASE_HOME}/lib/

# Apply custom HBase build steps
RUN sed -i "s,^. export JAVA_HOME.*,export JAVA_HOME=$JAVA_HOME," ${HBASE_CONF_DIR}/hbase-env.sh \
&& sed -E -i 's/(.*)hbase\-daemons\.sh(.*zookeeper)/\1hbase-daemon.sh\2/g' ${HBASE_HOME}/bin/start-hbase.sh \
&& echo -e "JAVA_HOME=$JAVA_HOME\nexport JAVA_HOME\nexport PATH=$JAVA_HOME/jre/bin:$PATH" > /etc/profile.d/defaults.sh \
&& ln -sf ${HBASE_HOME}/bin/* /usr/bin

# Create HBase user and home directory
RUN useradd -u 1000 -m ${HBASE_USER} \
&& mkdir -p /home/${HBASE_USER} \
&& chown -R ${HBASE_USER}:${HBASE_USER} /opt /home/${HBASE_USER} \
&& mkdir "$DATA_DIR" && chown -R ${HBASE_USER}:${HBASE_USER} "$DATA_DIR"

# Set permissions for jboss home directory
RUN chown -R ${HBASE_USER}:${HBASE_USER} /home/jboss

# Copy configuration files - Removed in order to mount config files individually #

# Expose required ports for HBase and related services
EXPOSE 8000 8080 8085 9090 9095 2181 16000 16010 16020 16030

# Set up the utils directory as a volume
VOLUME ["/opt/utils"]

# Switch to the HBase user
USER ${HBASE_USER}

# Create necessary directories for HBase to run
RUN mkdir -p "$DATA_DIR"/hbase "$DATA_DIR"/run "$DATA_DIR"/logs
RUN chmod -R 777 "$DATA_DIR"

# Add the 'ls -l' alias
RUN echo 'alias ll="ls -l"' >> /home/hbase/.bashrc
RUN echo 'alias ll="ls -l"' >> /home/jboss/.bashrc

# Start HBase and keep it running
ENTRYPOINT ["/bin/bash", "-c", "${HBASE_HOME}/bin/start-hbase.sh && tail -f ${HBASE_LOGS_DIR}/*.log"]
47 changes: 47 additions & 0 deletions dev-support/integration-test/read-replica/build-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash

# Load environment variables from .env file
set -a
. ./.env
set +a

# Check if required environment variables are set
if [ -z "$HBASE_IMAGE" ]; then
echo "Error: HBASE_IMAGE is not set in .env file."
exit 1
fi

# HBase source directory
HBASE_SOURCE_DIR="./hbase"

# Check if HBase source directory exists
if [ ! -d "$HBASE_SOURCE_DIR" ]; then
echo "Error: HBase source directory does not exist at $HBASE_SOURCE_DIR."
exit 1
fi

# Run Maven clean to remove previous build artifacts
echo "Running 'mvn clean' in $HBASE_SOURCE_DIR..."
cd "$HBASE_SOURCE_DIR" || exit 1
mvn clean

if [ $? -ne 0 ]; then
echo "Error: 'mvn clean' failed."
exit 1
else
echo "'mvn clean' completed successfully."
fi

# Return to the original directory
cd - || exit 1

# Build HBase Docker image
echo "Building HBase Docker image: ${HBASE_IMAGE}"
docker build -t "${HBASE_IMAGE}" ./

if [ $? -ne 0 ]; then
echo "Error: Failed to build HBase Docker image."
exit 1
else
echo "HBase Docker image built successfully."
fi
54 changes: 54 additions & 0 deletions dev-support/integration-test/read-replica/conf1/hbase-site.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>

<property>
<name>hbase.zookeeper.quorum</name>
<value>hbase-docker</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/data-store/zk</value>
</property>
<property>
<name>hbase.rootdir</name>
<value>file:///data-store/hbase</value>
</property>
<property>
<name>hbase.wal.dir</name>
<value>file:///data-store/wal</value>
</property>
<property>
<name>hbase.store.file-tracker.impl</name>
<value>FILE</value>
</property>

<property>
<name>hbase.master.info.bindAddress</name>
<value>hbase-docker</value>
</property>
<property>
<name>hbase.regionserver.info.bindAddress</name>
<value>hbase-docker</value>
</property>
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<property>
<name>hbase.rest.port</name>
<value>8000</value>
</property>

<property>
<name>hbase.global.readonly.enabled</name>
<value>false</value>
</property>
<property>
<name>hbase.master.hbck.chore.interval</name>
<value>0</value>
</property>
</configuration>
Loading