diff --git a/.github/workflows/build-latest.yaml b/.github/workflows/build-latest.yaml index a328748e..7a3f4b35 100644 --- a/.github/workflows/build-latest.yaml +++ b/.github/workflows/build-latest.yaml @@ -8,6 +8,34 @@ on: branches: - develop jobs: + build-activemq-docker-image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build image for testing activeqm + id: docker_build_testing_image_activeqm + uses: docker/build-push-action@v5 + with: + context: ./clustering/activemq-docker/ + file: ./clustering/activemq-docker/Dockerfile + push: false + load: true + tags: kartoza/activemq-docker:manual-build + outputs: type=docker,dest=/tmp/activemq.tar + cache-from: | + type=gha,scope=test + type=gha,scope=prod + cache-to: type=gha,scope=test + target: activemq-prod + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: kartoza-activemq + path: /tmp/activemq.tar build-docker-image: runs-on: ubuntu-latest strategy: @@ -63,7 +91,7 @@ jobs: run-scenario-tests: runs-on: ubuntu-latest - needs: build-docker-image + needs: [ build-docker-image, build-activemq-docker-image] strategy: matrix: scenario: @@ -83,6 +111,14 @@ jobs: - name: Load image run: | docker load --input /tmp/geoserver.tar + - name: Download ActiveMQ artifact + uses: actions/download-artifact@v3 + with: + name: kartoza-activemq + path: /tmp + - name: Load ActiveMQ image + run: | + docker load --input /tmp/activemq.tar - name: Run scenario test ${{ matrix.scenario }} working-directory: scenario_tests/${{ matrix.scenario }} env: diff --git a/clustering/README.md b/clustering/README.md index 1ed7975a..17d3e6b6 100644 --- a/clustering/README.md +++ b/clustering/README.md @@ -1,6 +1,7 @@ # Clustering using JMS Plugin GeoServer supports clustering using JMS cluster plugin or using the ActiveMQ-broker. +## JMS cluster plugin This setup uses the JMS cluster plugin which uses an embedded broker. A docker-compose.yml is provided in the clustering folder which simulates the replication using a shared data directory. @@ -20,4 +21,18 @@ This value will be different for (Master-Node) * `CLUSTER_CONNECTION_MAX_WAIT=500` - Wait time between connection to broker retry (in milliseconds) * `EXISTING_DATA_DIR` - If you are using an existing data directory, you need to set `CLUSTER_CONFIG_DIR` otherwise the container is will hang and not start. Additionally, it will check if all the files -needed for clustering exists, otherwise it will fail. \ No newline at end of file +needed for clustering exists, otherwise it will fail. + +## ActiveMQ-broker + +You can additionally run the clustering using an external broker. To run this +you will need to build the image locally and run the stack: + +```bash +``` + +or run in a single step + +```bash +docker compose -f docker-compose-external.yml +``` \ No newline at end of file diff --git a/clustering/activemq-docker/Dockerfile b/clustering/activemq-docker/Dockerfile new file mode 100644 index 00000000..bb78c46a --- /dev/null +++ b/clustering/activemq-docker/Dockerfile @@ -0,0 +1,32 @@ +FROM bellsoft/liberica-openjdk-alpine:13 AS activemq-prod + +LABEL maintainer="Alessio Fabiani " + +ENV ACTIVEMQ_VERSION 5.17.1 +ENV ACTIVEMQ apache-activemq-$ACTIVEMQ_VERSION +ENV ACTIVEMQ_HOME /opt/activemq/ +ENV ACTIVEMQ_CONF $ACTIVEMQ_HOME/conf/ +ENV ACTIVEMQ_LIB $ACTIVEMQ_HOME/lib/optional/ + + +RUN apk add --update curl && \ + rm -rf /var/cache/apk/* && \ + mkdir -p /opt && \ + curl -s -S https://archive.apache.org/dist/activemq/$ACTIVEMQ_VERSION/$ACTIVEMQ-bin.tar.gz | tar -xvz -C /opt && \ + mv /opt/$ACTIVEMQ $ACTIVEMQ_HOME && \ + addgroup -S activemq && \ + adduser -S -H -G activemq -h $ACTIVEMQ_HOME activemq && \ + chown -R activemq:activemq $ACTIVEMQ_HOME && \ + chown -h activemq:activemq $ACTIVEMQ_HOME + +ADD setup-jars.sh /setup-jars.sh +RUN chmod +x /*.sh;/setup-jars.sh + +COPY activemq.xml $ACTIVEMQ_CONF/activemq.xml + +# EXPOSE 1883 5672 8161 61613 61614 61616 + +USER activemq +WORKDIR $ACTIVEMQ_HOME + +CMD ["/bin/sh", "-c", "bin/activemq console"] diff --git a/clustering/activemq-docker/activemq.xml b/clustering/activemq-docker/activemq.xml new file mode 100644 index 00000000..dc0e8945 --- /dev/null +++ b/clustering/activemq-docker/activemq.xml @@ -0,0 +1,169 @@ + + + + + + + + file:${activemq.conf}/credentials.properties + + + + + + + + + + + + + + ${POSTGRES_PORT} + ${POSTGRES_DB} + ${HOST} + ${POSTGRES_USER} + ${POSTGRES_PASS} + ${SSL_MODE} + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clustering/activemq-docker/setup-jars.sh b/clustering/activemq-docker/setup-jars.sh new file mode 100644 index 00000000..2d66b7d2 --- /dev/null +++ b/clustering/activemq-docker/setup-jars.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +wget -c --tries=2 https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar \ +-O "${ACTIVEMQ_LIB}"/slf4j-api-1.7.21.jar +wget -c --tries=2 https://repo1.maven.org/maven2/org/postgresql/postgresql/42.4.0/postgresql-42.4.0.jar \ +-O "${ACTIVEMQ_LIB}"/postgresql-42.4.0.jar +wget -c --tries=2 https://repo1.maven.org/maven2/org/osgi/org.osgi.compendium/4.3.1/org.osgi.compendium-4.3.1.jar \ +-O "${ACTIVEMQ_LIB}"/org.osgi.compendium-4.3.1.jar +wget -c --tries=2 https://repo1.maven.org/maven2/org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-dbcp/1.4_3/org.apache.servicemix.bundles.commons-dbcp-1.4_3-sources.jar \ +-O "${ACTIVEMQ_LIB}"/org.apache.servicemix.bundles.commons-dbcp-1.4_3-sources.jar +wget -c --tries=2 https://repo1.maven.org/maven2/com/zaxxer/HikariCP/2.7.2/HikariCP-2.7.2.jar \ +-O "${ACTIVEMQ_LIB}"/HikariCP-2.7.2.jar \ No newline at end of file diff --git a/clustering/docker-compose-external.yml b/clustering/docker-compose-external.yml new file mode 100644 index 00000000..f51f2c53 --- /dev/null +++ b/clustering/docker-compose-external.yml @@ -0,0 +1,152 @@ +version: "3.9" + +# Common template for ActiveMQ services below +x-common-activemq: + &default-common-activemq + image: kartoza/activemq-docker:manual-build + build: activemq-docker + restart: on-failure + +services: + + db: + image: kartoza/postgis:16-3.4 + volumes: + - geo-db-data:/var/lib/postgresql + ports: + - "5432" + environment: + - POSTGRES_DB=gis,data,sample + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ALLOW_IP_RANGE=0.0.0.0/0 + - FORCE_SSL=TRUE + restart: on-failure + healthcheck: + test: "PGPASSWORD=docker pg_isready -h 127.0.0.1 -U docker -d gis" + + broker1: + << : *default-common-activemq + environment: + - JAVA_HOME=/opt/java/openjdk + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=gis + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ACTIVEMQ_SERVER_URI=tcp://broker1:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&jms.useAsyncSend=false&transport.daemon=true + - ACTIVEMQ_SERVER_DISCOVERYURI=multicast://default + - SSL_MODE=ALLOW + depends_on: + db: + condition: service_healthy + healthcheck: + test: netstat -ltn | grep -c ":61616" + interval: 30s + timeout: 10s + retries: 10 + + broker2: + << : *default-common-activemq + environment: + - JAVA_HOME=/opt/java/openjdk + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=data + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ACTIVEMQ_SERVER_URI=tcp://broker2:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&jms.useAsyncSend=false&transport.daemon=true + - ACTIVEMQ_SERVER_DISCOVERYURI=multicast://default + - SSL_MODE=ALLOW + depends_on: + db: + condition: service_healthy + broker1: + condition: service_healthy + healthcheck: + test: netstat -ltn | grep -c ":61616" + interval: 30s + timeout: 10s + retries: 10 + + broker3: + << : *default-common-activemq + environment: + - JAVA_HOME=/opt/java/openjdk + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=sample + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ACTIVEMQ_SERVER_URI=tcp://broker3:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&jms.useAsyncSend=false&transport.daemon=true + - ACTIVEMQ_SERVER_DISCOVERYURI=multicast://default + - SSL_MODE=ALLOW + depends_on: + db: + condition: service_healthy + broker1: + condition: service_healthy + healthcheck: + test: netstat -ltn | grep -c ":61616" + interval: 30s + timeout: 10s + retries: 10 + + master: + image: kartoza/geoserver:2.24.0 + ports: + - "8081:8080" + environment: + - CLUSTERING=true + - CLUSTER_DURABILITY=false + - DB_BACKEND=POSTGRES + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=gis + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - SSL_MODE=ALLOW + - BROKER_URL=failover:(tcp://broker1:61616,tcp://broker2:61616,tcp://broker3:61616) + - READONLY=disabled + - TOGGLE_MASTER=true + - TOGGLE_SLAVE=false + - EMBEDDED_BROKER=disabled + - RUN_AS_ROOT=TRUE + - GEOSERVER_ADMIN_USER=admin + - GEOSERVER_ADMIN_PASSWORD=myawesomegeoserver + - STABLE_EXTENSIONS= + - COMMUNITY_EXTENSIONS=jms-cluster-plugin + volumes: + - geoserver-cluster-data:/opt/geoserver/data_dir + + + node: + image: kartoza/geoserver:2.24.0 + ports: + - "8082:8080" + environment: + - CLUSTERING=true + - CLUSTER_DURABILITY=false + - DB_BACKEND=POSTGRES + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=gis + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - SSL_MODE=ALLOW + - BROKER_URL=failover:(tcp://broker1:61616,tcp://broker2:61616,tcp://broker3:61616) + - READONLY=enabled + - TOGGLE_MASTER=false + - TOGGLE_SLAVE=true + - EMBEDDED_BROKER=disabled + - RUN_AS_ROOT=TRUE + - GEOSERVER_ADMIN_USER=admin + - GEOSERVER_ADMIN_PASSWORD=myawesomegeoserver + - STABLE_EXTENSIONS= + - COMMUNITY_EXTENSIONS=jms-cluster-plugin + volumes: + - geoserver-cluster-data:/opt/geoserver/data_dir + +volumes: + geoserver-cluster-data: + geo-db-data: diff --git a/scenario_tests/clustering/docker-compose-external.yml b/scenario_tests/clustering/docker-compose-external.yml new file mode 100644 index 00000000..b3d99ddc --- /dev/null +++ b/scenario_tests/clustering/docker-compose-external.yml @@ -0,0 +1,163 @@ +version: "3.9" + +# Common template for ActiveMQ services below +x-common-activemq: + &default-common-activemq + image: kartoza/activemq-docker:manual-build + #build: activemq-docker + restart: on-failure + +services: + + db: + image: kartoza/postgis:16-3.4 + volumes: + - geo-db-data:/var/lib/postgresql + - ./tests/init.sql:/docker-entrypoint-initdb.d/init.sql + environment: + - POSTGRES_DB=gis,data,sample + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ALLOW_IP_RANGE=0.0.0.0/0 + - FORCE_SSL=TRUE + restart: on-failure + healthcheck: + test: "PGPASSWORD=docker pg_isready -h 127.0.0.1 -U docker -d gis" + + broker1: + << : *default-common-activemq + environment: + - JAVA_HOME=/opt/java/openjdk + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=gis + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ACTIVEMQ_SERVER_URI=tcp://broker1:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&jms.useAsyncSend=false&transport.daemon=true + - ACTIVEMQ_SERVER_DISCOVERYURI=multicast://default + - SSL_MODE=ALLOW + depends_on: + db: + condition: service_healthy + healthcheck: + test: netstat -ltn | grep -c ":61616" + interval: 30s + timeout: 10s + retries: 10 + + broker2: + << : *default-common-activemq + environment: + - JAVA_HOME=/opt/java/openjdk + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=data + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ACTIVEMQ_SERVER_URI=tcp://broker2:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&jms.useAsyncSend=false&transport.daemon=true + - ACTIVEMQ_SERVER_DISCOVERYURI=multicast://default + - SSL_MODE=ALLOW + depends_on: + db: + condition: service_healthy + broker1: + condition: service_healthy + healthcheck: + test: netstat -ltn | grep -c ":61616" + interval: 30s + timeout: 10s + retries: 10 + + broker3: + << : *default-common-activemq + environment: + - JAVA_HOME=/opt/java/openjdk + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=sample + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + - ACTIVEMQ_SERVER_URI=tcp://broker3:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&jms.useAsyncSend=false&transport.daemon=true + - ACTIVEMQ_SERVER_DISCOVERYURI=multicast://default + - SSL_MODE=ALLOW + depends_on: + db: + condition: service_healthy + broker1: + condition: service_healthy + healthcheck: + test: netstat -ltn | grep -c ":61616" + interval: 30s + timeout: 10s + retries: 10 + + master: + image: kartoza/geoserver:manual-build + environment: + - CLUSTERING=true + - CLUSTER_DURABILITY=false + - DB_BACKEND=POSTGRES + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=gis + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + #- POSTGRES_SCHEMA=gwc + - SSL_MODE=ALLOW + - BROKER_URL=failover:(tcp://broker1:61616,tcp://broker2:61616,tcp://broker3:61616) + - READONLY=disabled + - TOGGLE_MASTER=true + - TOGGLE_SLAVE=false + - EMBEDDED_BROKER=disabled + - RUN_AS_ROOT=TRUE + - GEOSERVER_ADMIN_USER=admin + - GEOSERVER_ADMIN_PASSWORD=myawesomegeoserver + - STABLE_EXTENSIONS= + - COMMUNITY_EXTENSIONS=jms-cluster-plugin + - TEST_CLASS=test_clustering_master.GeoServerClusteringMaster + volumes: + - geoserver-cluster-data:/opt/geoserver/data_dir + - ./tests:/tests + healthcheck: + test: "curl --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null -u admin:myawesomegeoserver http://localhost:8080/geoserver/rest/about/version.xml" + interval: 1m30s + timeout: 10s + retries: 3 + + + node: + image: kartoza/geoserver:manual-build + environment: + - CLUSTERING=true + - CLUSTER_DURABILITY=false + - DB_BACKEND=POSTGRES + - HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_DB=gis + - POSTGRES_USER=docker + - POSTGRES_PASS=docker + #- POSTGRES_SCHEMA=gwc + - SSL_MODE=ALLOW + - BROKER_URL=failover:(tcp://broker1:61616,tcp://broker2:61616,tcp://broker3:61616) + - READONLY=enabled + - TOGGLE_MASTER=false + - TOGGLE_SLAVE=true + - EMBEDDED_BROKER=disabled + - RUN_AS_ROOT=TRUE + - GEOSERVER_ADMIN_USER=admin + - GEOSERVER_ADMIN_PASSWORD=myawesomegeoserver + - STABLE_EXTENSIONS= + - COMMUNITY_EXTENSIONS=jms-cluster-plugin + - TEST_CLASS=test_clustering_node.GeoServerClusteringNode + volumes: + - geoserver-cluster-data:/opt/geoserver/data_dir + - ./tests:/tests + healthcheck: + test: "curl --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null -u admin:myawesomegeoserver http://localhost:8080/geoserver/rest/about/version.xml" + interval: 1m30s + timeout: 10s + retries: 3 + +volumes: + geoserver-cluster-data: + geo-db-data: diff --git a/scenario_tests/clustering/test.sh b/scenario_tests/clustering/test.sh index 6cc5f5d3..61ba4e2d 100755 --- a/scenario_tests/clustering/test.sh +++ b/scenario_tests/clustering/test.sh @@ -12,6 +12,9 @@ if [[ $(dpkg -l | grep "docker-compose") > /dev/null ]];then VERSION='docker compose' fi +################################ +#Test using internal jms cluster +################################ ${VERSION} -f docker-compose.yml up -d if [[ -n "${PRINT_TEST_LOGS}" ]]; then @@ -45,3 +48,41 @@ for service in "${services[@]}"; do done ${VERSION} -f docker-compose.yml down -v + +############################# +#Test using external ActiveMQ +############################# + +${VERSION} -f docker-compose-external.yml up -d + +if [[ -n "${PRINT_TEST_LOGS}" ]]; then + ${VERSION} -f docker-compose-external.yml logs -f & +fi + +sleep 120 + +# Test Master +services=("master") + +for service in "${services[@]}"; do + + # Execute tests + sleep 60 + echo "Execute test for $service" + ${VERSION} -f docker-compose.yml exec "${service}" /bin/bash /tests/test.sh + +done + +# Test Node +services=("node") + +for service in "${services[@]}"; do + + # Execute tests + sleep 60 + echo "Execute test for $service" + ${VERSION} -f docker-compose-external.yml exec "${service}" /bin/bash /tests/test.sh + +done + +${VERSION} -f docker-compose-external.yml down -v \ No newline at end of file diff --git a/scenario_tests/clustering/tests/test_clustering_master.py b/scenario_tests/clustering/tests/test_clustering_master.py index 9da601f2..a1522613 100644 --- a/scenario_tests/clustering/tests/test_clustering_master.py +++ b/scenario_tests/clustering/tests/test_clustering_master.py @@ -52,6 +52,7 @@ def test_publish_store(self): FAST postgis true + ALLOW false