From ae25c981ae2cb54c16b06cf4b001a3281da0ff87 Mon Sep 17 00:00:00 2001 From: mazano Date: Fri, 3 Jan 2025 20:11:02 +0200 Subject: [PATCH] Fix running backup and restore plugin (#709) * Fix running backup and restore plugin --- .github/workflows/build-latest.yaml | 1 + .../backup_restore/docker-compose.yml | 54 ++++++++++++++ scenario_tests/backup_restore/test.sh | 44 +++++++++++ .../backup_restore/tests/__init__.py | 0 scenario_tests/backup_restore/tests/test.sh | 17 +++++ .../tests/test_geoserver_backup.py | 73 +++++++++++++++++++ .../tests/test_geoserver_restore.py | 67 +++++++++++++++++ scripts/entrypoint.sh | 2 +- scripts/setup.sh | 6 +- scripts/start.sh | 8 +- 10 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 scenario_tests/backup_restore/docker-compose.yml create mode 100755 scenario_tests/backup_restore/test.sh create mode 100644 scenario_tests/backup_restore/tests/__init__.py create mode 100644 scenario_tests/backup_restore/tests/test.sh create mode 100644 scenario_tests/backup_restore/tests/test_geoserver_backup.py create mode 100644 scenario_tests/backup_restore/tests/test_geoserver_restore.py diff --git a/.github/workflows/build-latest.yaml b/.github/workflows/build-latest.yaml index 991b871f..fe52b3a5 100644 --- a/.github/workflows/build-latest.yaml +++ b/.github/workflows/build-latest.yaml @@ -190,6 +190,7 @@ jobs: # - clustering - jdbconfig - libjpeg + - backup_restore steps: - uses: actions/checkout@v4 - name: Download artifact diff --git a/scenario_tests/backup_restore/docker-compose.yml b/scenario_tests/backup_restore/docker-compose.yml new file mode 100644 index 00000000..736bd69d --- /dev/null +++ b/scenario_tests/backup_restore/docker-compose.yml @@ -0,0 +1,54 @@ + +volumes: + geoserver-data-dir: + geoserver-data: + geoserver-backup-dir: + + +services: + + geoserver: + image: 'kartoza/geoserver:${TAG:-manual-build}' + restart: 'always' + volumes: + - geoserver-data-dir:/opt/geoserver/data_dir + - geoserver-backup-dir:/settings + - ./tests:/tests + environment: + GEOSERVER_ADMIN_PASSWORD: myawesomegeoserver + GEOSERVER_ADMIN_USER: admin + SAMPLE_DATA: true + CONSOLE_HANDLER_LEVEL: WARNING + COMMUNITY_EXTENSIONS: backup-restore-plugin + TEST_CLASS: test_geoserver_backup.TestGeoServerBackup + ports: + - "8080:8080" + healthcheck: + test: ["CMD-SHELL", "curl --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null -u $${GEOSERVER_ADMIN_USER}:$${GEOSERVER_ADMIN_PASSWORD} http://localhost:8080/geoserver/rest/about/version.xml"] + interval: 1m30s + timeout: 10s + retries: 3 + + restore: + image: 'kartoza/geoserver:${TAG:-manual-build}' + restart: 'always' + volumes: + - geoserver-data:/opt/geoserver/data_dir + - geoserver-backup-dir:/settings + - ./tests:/tests + environment: + GEOSERVER_ADMIN_PASSWORD: myawesomegeoserver + GEOSERVER_ADMIN_USER: admin + CONSOLE_HANDLER_LEVEL: WARNING + RECREATE_DATADIR: TRUE + COMMUNITY_EXTENSIONS: backup-restore-plugin + TEST_CLASS: test_geoserver_restore.TestGeoServerRestore + ports: + - "8080" + healthcheck: + test: [ "CMD-SHELL", "curl --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null -u $${GEOSERVER_ADMIN_USER}:$${GEOSERVER_ADMIN_PASSWORD} http://localhost:8080/geoserver/rest/about/version.xml" ] + interval: 1m30s + timeout: 10s + retries: 3 + + diff --git a/scenario_tests/backup_restore/test.sh b/scenario_tests/backup_restore/test.sh new file mode 100755 index 00000000..75972bf3 --- /dev/null +++ b/scenario_tests/backup_restore/test.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# exit immediately if test fails +set -e + +source ../test-env.sh + +# Run service +if [[ $(dpkg -l | grep "docker-compose") > /dev/null ]];then + VERSION='docker-compose' + else + VERSION='docker compose' +fi + +${VERSION} -f docker-compose.yml up -d + +if [[ -n "${PRINT_TEST_LOGS}" ]]; then + ${VERSION} -f docker-compose.yml logs -f & +fi + + +services=("geoserver") + +for service in "${services[@]}"; do + + # Execute tests + test_url_availability http://localhost:8080/geoserver/rest/about/version.xml + echo "Execute test for $service" + ${VERSION} -f docker-compose.yml exec $service /bin/bash /tests/test.sh + +done + +services=("restore") + +for service in "${services[@]}"; do + + # Execute tests + test_url_availability http://localhost:8080/geoserver/rest/about/version.xml + echo "Execute test for $service" + ${VERSION} -f docker-compose.yml exec $service /bin/bash /tests/test.sh + +done + +${VERSION} -f docker-compose.yml down -v diff --git a/scenario_tests/backup_restore/tests/__init__.py b/scenario_tests/backup_restore/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scenario_tests/backup_restore/tests/test.sh b/scenario_tests/backup_restore/tests/test.sh new file mode 100644 index 00000000..7337b414 --- /dev/null +++ b/scenario_tests/backup_restore/tests/test.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e + +source /scripts/env-data.sh + +# execute tests +pushd /tests + +cat << EOF +Settings used: + +GEOSERVER_ADMIN_PASSWORD: ${GEOSERVER_ADMIN_PASSWORD} +GEOSERVER_ADMIN_USER: ${GEOSERVER_ADMIN_USER} +EOF + +python3 -m unittest -v ${TEST_CLASS} diff --git a/scenario_tests/backup_restore/tests/test_geoserver_backup.py b/scenario_tests/backup_restore/tests/test_geoserver_backup.py new file mode 100644 index 00000000..5c3fcb4b --- /dev/null +++ b/scenario_tests/backup_restore/tests/test_geoserver_backup.py @@ -0,0 +1,73 @@ +import unittest +import requests +from requests.auth import HTTPBasicAuth +from os import environ, mkdir, chmod +from os.path import join, exists +from shutil import chown +import json +import time + + +class TestGeoServerBackup(unittest.TestCase): + + def setUp(self): + """Set up the base URL, authentication, and create the zip file.""" + self.gs_url = 'http://localhost:8080/geoserver' + self.geo_username = environ.get('GEOSERVER_ADMIN_USER', 'admin') + self.geo_password = environ.get('GEOSERVER_ADMIN_PASSWORD', 'myawesomegeoserver') + self.username = 'geoserveruser' + self.group_name = 'geoserverusers' + self.backup_path = "/settings/backup/" + + def test_create_backup(self): + """Test creating a GeoServer backup using the Backup and Restore plugin.""" + auth = HTTPBasicAuth('%s' % self.geo_username, '%s' % self.geo_password) + base_url = f"{self.gs_url}/rest/br/backup/" + if not exists(self.backup_path): + mkdir(self.backup_path) + backup_file = join(self.backup_path, 'geoserver.zip') + # Create the empty zip file + with open(backup_file, "wb") as f: + pass + + # Change ownership of the zip file + chmod(self.backup_path, 0o777) + chown(backup_file, user=self.username, group=self.group_name) + headers = { + "Content-Type": "application/json" + } + + payload = { + "backup": { + "archiveFile": backup_file, + "overwrite": True, + "options": {} + } + } + + # Send the POST request to trigger the backup + response = requests.post(base_url, json=payload, auth=auth, headers=headers) + response_data = json.loads(response.text) + execution_id = response_data["backup"]["execution"]["id"] + execution_url = f"{self.gs_url}/rest/br/backup/{execution_id}.json" + # wait for backup to complete + time.sleep(40) + response_execution_request = requests.get(execution_url, auth=auth) + if response_execution_request.status_code == 200: + try: + response_execution_json = response_execution_request.json() + response_status = response_execution_json["backup"]["execution"]["status"] + self.assertEqual(response_status, 'COMPLETED', "backup initiated successfully") + except ValueError as e: + print("Error parsing JSON:", e) + print("Raw response content:", response_execution_request.text) + else: + print(f"Request failed with status code {response_execution_request.status_code}") + print("Response content:", response_execution_request.text) + + # Verify the response status code + self.assertEqual(response.status_code, 201, "backup initiated successfully") + + +if __name__ == "__main__": + unittest.main() diff --git a/scenario_tests/backup_restore/tests/test_geoserver_restore.py b/scenario_tests/backup_restore/tests/test_geoserver_restore.py new file mode 100644 index 00000000..67d73413 --- /dev/null +++ b/scenario_tests/backup_restore/tests/test_geoserver_restore.py @@ -0,0 +1,67 @@ +import sys +import unittest +import requests +from requests.auth import HTTPBasicAuth +from os import environ, chmod +from os.path import join, exists +import json +import time + + +class TestGeoServerRestore(unittest.TestCase): + + def setUp(self): + """Set up the base URL, authentication, and create the zip file.""" + self.gs_url = 'http://localhost:8080/geoserver' + self.geo_username = environ.get('GEOSERVER_ADMIN_USER', 'admin') + self.geo_password = environ.get('GEOSERVER_ADMIN_PASSWORD', 'myawesomegeoserver') + self.backup_path = "/settings/backup/" + + def test_restore_backup(self): + """Test restoring an existing backup of a GeoServer instance using the Backup and Restore plugin.""" + auth = HTTPBasicAuth('%s' % self.geo_username, '%s' % self.geo_password) + base_url = f"{self.gs_url}/rest/br/restore/" + backup_file = join(self.backup_path, 'geoserver.zip') + chmod(self.backup_path, 0o777) + if not exists(backup_file): + sys.exit() + + headers = { + "Content-Type": "application/json" + } + + payload = { + "restore": { + "archiveFile": backup_file, + "options": { + "option": ["BK_BEST_EFFORT=true"] + } + } + } + + # Send the POST request to trigger the backup + response = requests.post(base_url, json=payload, auth=auth, headers=headers) + response_data = json.loads(response.text) + execution_id = response_data["restore"]["execution"]["id"] + execution_url = f"{self.gs_url}/rest/br/restore/{execution_id}.json" + # wait for backup to complete + time.sleep(30) + response_execution_request = requests.get(execution_url, auth=auth) + if response_execution_request.status_code == 200: + try: + response_execution_json = response_execution_request.json() + response_status = response_execution_json["restore"]["execution"]["status"] + self.assertEqual(response_status, 'COMPLETED', "backup initiated successfully") + except ValueError as e: + print("Error parsing JSON:", e) + print("Raw response content:", response_execution_request.text) + else: + print(f"Request failed with status code {response_execution_request.status_code}") + print("Response content:", response_execution_request.text) + + # Verify the response status code + self.assertEqual(response.status_code, 201, "backup initiated successfully") + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index 615b1bcc..52715aca 100644 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -106,7 +106,7 @@ export JAVA_OPTS="${JAVA_OPTS} ${GEOSERVER_OPTS}" # Chown again - seems to fix issue with resolving all created directories if [[ ${RUN_AS_ROOT} =~ [Ff][Aa][Ll][Ss][Ee] ]];then dir_ownership=("${CATALINA_HOME}" /home/"${USER_NAME}"/ "${COMMUNITY_PLUGINS_DIR}" - "${STABLE_PLUGINS_DIR}" "${REQUIRED_PLUGINS_DIR}" "${GEOSERVER_HOME}" /usr/share/fonts/ /tomcat_apps.zip + "${STABLE_PLUGINS_DIR}" "${REQUIRED_PLUGINS_DIR}" "${GEOSERVER_HOME}" /usr/share/fonts/ /tmp/ "${FOOTPRINTS_DATA_DIR}" "${CERT_DIR}" "${FONTS_DIR}" /scripts/ "${EXTRA_CONFIG_DIR}" "/docker-entrypoint-geoserver.d" "${MONITOR_AUDIT_PATH}") for directory in "${dir_ownership[@]}"; do diff --git a/scripts/setup.sh b/scripts/setup.sh index a575aea6..48442c66 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -89,7 +89,7 @@ rm -f /tmp/resources/overlays/README.txt && # Package tomcat webapps - useful to activate later if [[ -d "${CATALINA_HOME}"/webapps.dist ]]; then mv "${CATALINA_HOME}"/webapps.dist /tomcat_apps - zip -r /tomcat_apps.zip /tomcat_apps + zip -r "${REQUIRED_PLUGINS_DIR}"/tomcat_apps.zip /tomcat_apps rm -r /tomcat_apps else cp -r "${CATALINA_HOME}"/webapps/ROOT /tomcat_apps @@ -97,8 +97,8 @@ else cp -r "${CATALINA_HOME}"/webapps/examples /tomcat_apps cp -r "${CATALINA_HOME}"/webapps/host-manager /tomcat_apps cp -r "${CATALINA_HOME}"/webapps/manager /tomcat_apps - zip -r /tomcat_apps.zip /tomcat_apps - rm -r /tomcat_apps + zip -r "${REQUIRED_PLUGINS_DIR}"/tomcat_apps.zip /tomcat_apps + rm -rf /tomcat_apps fi pushd ${CATALINA_HOME}/lib || exit diff --git a/scripts/start.sh b/scripts/start.sh index d5351d2d..23461c8e 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -326,8 +326,12 @@ fi if [[ "${TOMCAT_EXTRAS}" =~ [Tt][Rr][Uu][Ee] ]]; then - unzip -qq /tomcat_apps.zip -d /tmp/ && - cp -r /tmp/tomcat_apps/webapps.dist/* "${CATALINA_HOME}"/webapps/ && + unzip -qq "${REQUIRED_PLUGINS_DIR}"/tomcat_apps.zip -d /tmp/ + if [[ -d /tmp/tomcat_apps/webapps.dist ]];then + cp -r /tmp/tomcat_apps/webapps.dist/* "${CATALINA_HOME}"/webapps/ + else + cp -r /tmp/tomcat_apps/* "${CATALINA_HOME}"/webapps/ + fi rm -r /tmp/tomcat_apps if [[ ${POSTGRES_JNDI} =~ [Ff][Aa][Ll][Ss][Ee] ]]; then if [[ -f ${EXTRA_CONFIG_DIR}/context.xml ]]; then