Skip to content

Commit ae25c98

Browse files
authored
Fix running backup and restore plugin (#709)
* Fix running backup and restore plugin
1 parent b2031b8 commit ae25c98

File tree

10 files changed

+266
-6
lines changed

10 files changed

+266
-6
lines changed

.github/workflows/build-latest.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ jobs:
190190
# - clustering
191191
- jdbconfig
192192
- libjpeg
193+
- backup_restore
193194
steps:
194195
- uses: actions/checkout@v4
195196
- name: Download artifact
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
volumes:
3+
geoserver-data-dir:
4+
geoserver-data:
5+
geoserver-backup-dir:
6+
7+
8+
services:
9+
10+
geoserver:
11+
image: 'kartoza/geoserver:${TAG:-manual-build}'
12+
restart: 'always'
13+
volumes:
14+
- geoserver-data-dir:/opt/geoserver/data_dir
15+
- geoserver-backup-dir:/settings
16+
- ./tests:/tests
17+
environment:
18+
GEOSERVER_ADMIN_PASSWORD: myawesomegeoserver
19+
GEOSERVER_ADMIN_USER: admin
20+
SAMPLE_DATA: true
21+
CONSOLE_HANDLER_LEVEL: WARNING
22+
COMMUNITY_EXTENSIONS: backup-restore-plugin
23+
TEST_CLASS: test_geoserver_backup.TestGeoServerBackup
24+
ports:
25+
- "8080:8080"
26+
healthcheck:
27+
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"]
28+
interval: 1m30s
29+
timeout: 10s
30+
retries: 3
31+
32+
restore:
33+
image: 'kartoza/geoserver:${TAG:-manual-build}'
34+
restart: 'always'
35+
volumes:
36+
- geoserver-data:/opt/geoserver/data_dir
37+
- geoserver-backup-dir:/settings
38+
- ./tests:/tests
39+
environment:
40+
GEOSERVER_ADMIN_PASSWORD: myawesomegeoserver
41+
GEOSERVER_ADMIN_USER: admin
42+
CONSOLE_HANDLER_LEVEL: WARNING
43+
RECREATE_DATADIR: TRUE
44+
COMMUNITY_EXTENSIONS: backup-restore-plugin
45+
TEST_CLASS: test_geoserver_restore.TestGeoServerRestore
46+
ports:
47+
- "8080"
48+
healthcheck:
49+
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" ]
50+
interval: 1m30s
51+
timeout: 10s
52+
retries: 3
53+
54+

scenario_tests/backup_restore/test.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env bash
2+
3+
# exit immediately if test fails
4+
set -e
5+
6+
source ../test-env.sh
7+
8+
# Run service
9+
if [[ $(dpkg -l | grep "docker-compose") > /dev/null ]];then
10+
VERSION='docker-compose'
11+
else
12+
VERSION='docker compose'
13+
fi
14+
15+
${VERSION} -f docker-compose.yml up -d
16+
17+
if [[ -n "${PRINT_TEST_LOGS}" ]]; then
18+
${VERSION} -f docker-compose.yml logs -f &
19+
fi
20+
21+
22+
services=("geoserver")
23+
24+
for service in "${services[@]}"; do
25+
26+
# Execute tests
27+
test_url_availability http://localhost:8080/geoserver/rest/about/version.xml
28+
echo "Execute test for $service"
29+
${VERSION} -f docker-compose.yml exec $service /bin/bash /tests/test.sh
30+
31+
done
32+
33+
services=("restore")
34+
35+
for service in "${services[@]}"; do
36+
37+
# Execute tests
38+
test_url_availability http://localhost:8080/geoserver/rest/about/version.xml
39+
echo "Execute test for $service"
40+
${VERSION} -f docker-compose.yml exec $service /bin/bash /tests/test.sh
41+
42+
done
43+
44+
${VERSION} -f docker-compose.yml down -v

scenario_tests/backup_restore/tests/__init__.py

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
source /scripts/env-data.sh
6+
7+
# execute tests
8+
pushd /tests
9+
10+
cat << EOF
11+
Settings used:
12+
13+
GEOSERVER_ADMIN_PASSWORD: ${GEOSERVER_ADMIN_PASSWORD}
14+
GEOSERVER_ADMIN_USER: ${GEOSERVER_ADMIN_USER}
15+
EOF
16+
17+
python3 -m unittest -v ${TEST_CLASS}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import unittest
2+
import requests
3+
from requests.auth import HTTPBasicAuth
4+
from os import environ, mkdir, chmod
5+
from os.path import join, exists
6+
from shutil import chown
7+
import json
8+
import time
9+
10+
11+
class TestGeoServerBackup(unittest.TestCase):
12+
13+
def setUp(self):
14+
"""Set up the base URL, authentication, and create the zip file."""
15+
self.gs_url = 'http://localhost:8080/geoserver'
16+
self.geo_username = environ.get('GEOSERVER_ADMIN_USER', 'admin')
17+
self.geo_password = environ.get('GEOSERVER_ADMIN_PASSWORD', 'myawesomegeoserver')
18+
self.username = 'geoserveruser'
19+
self.group_name = 'geoserverusers'
20+
self.backup_path = "/settings/backup/"
21+
22+
def test_create_backup(self):
23+
"""Test creating a GeoServer backup using the Backup and Restore plugin."""
24+
auth = HTTPBasicAuth('%s' % self.geo_username, '%s' % self.geo_password)
25+
base_url = f"{self.gs_url}/rest/br/backup/"
26+
if not exists(self.backup_path):
27+
mkdir(self.backup_path)
28+
backup_file = join(self.backup_path, 'geoserver.zip')
29+
# Create the empty zip file
30+
with open(backup_file, "wb") as f:
31+
pass
32+
33+
# Change ownership of the zip file
34+
chmod(self.backup_path, 0o777)
35+
chown(backup_file, user=self.username, group=self.group_name)
36+
headers = {
37+
"Content-Type": "application/json"
38+
}
39+
40+
payload = {
41+
"backup": {
42+
"archiveFile": backup_file,
43+
"overwrite": True,
44+
"options": {}
45+
}
46+
}
47+
48+
# Send the POST request to trigger the backup
49+
response = requests.post(base_url, json=payload, auth=auth, headers=headers)
50+
response_data = json.loads(response.text)
51+
execution_id = response_data["backup"]["execution"]["id"]
52+
execution_url = f"{self.gs_url}/rest/br/backup/{execution_id}.json"
53+
# wait for backup to complete
54+
time.sleep(40)
55+
response_execution_request = requests.get(execution_url, auth=auth)
56+
if response_execution_request.status_code == 200:
57+
try:
58+
response_execution_json = response_execution_request.json()
59+
response_status = response_execution_json["backup"]["execution"]["status"]
60+
self.assertEqual(response_status, 'COMPLETED', "backup initiated successfully")
61+
except ValueError as e:
62+
print("Error parsing JSON:", e)
63+
print("Raw response content:", response_execution_request.text)
64+
else:
65+
print(f"Request failed with status code {response_execution_request.status_code}")
66+
print("Response content:", response_execution_request.text)
67+
68+
# Verify the response status code
69+
self.assertEqual(response.status_code, 201, "backup initiated successfully")
70+
71+
72+
if __name__ == "__main__":
73+
unittest.main()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import sys
2+
import unittest
3+
import requests
4+
from requests.auth import HTTPBasicAuth
5+
from os import environ, chmod
6+
from os.path import join, exists
7+
import json
8+
import time
9+
10+
11+
class TestGeoServerRestore(unittest.TestCase):
12+
13+
def setUp(self):
14+
"""Set up the base URL, authentication, and create the zip file."""
15+
self.gs_url = 'http://localhost:8080/geoserver'
16+
self.geo_username = environ.get('GEOSERVER_ADMIN_USER', 'admin')
17+
self.geo_password = environ.get('GEOSERVER_ADMIN_PASSWORD', 'myawesomegeoserver')
18+
self.backup_path = "/settings/backup/"
19+
20+
def test_restore_backup(self):
21+
"""Test restoring an existing backup of a GeoServer instance using the Backup and Restore plugin."""
22+
auth = HTTPBasicAuth('%s' % self.geo_username, '%s' % self.geo_password)
23+
base_url = f"{self.gs_url}/rest/br/restore/"
24+
backup_file = join(self.backup_path, 'geoserver.zip')
25+
chmod(self.backup_path, 0o777)
26+
if not exists(backup_file):
27+
sys.exit()
28+
29+
headers = {
30+
"Content-Type": "application/json"
31+
}
32+
33+
payload = {
34+
"restore": {
35+
"archiveFile": backup_file,
36+
"options": {
37+
"option": ["BK_BEST_EFFORT=true"]
38+
}
39+
}
40+
}
41+
42+
# Send the POST request to trigger the backup
43+
response = requests.post(base_url, json=payload, auth=auth, headers=headers)
44+
response_data = json.loads(response.text)
45+
execution_id = response_data["restore"]["execution"]["id"]
46+
execution_url = f"{self.gs_url}/rest/br/restore/{execution_id}.json"
47+
# wait for backup to complete
48+
time.sleep(30)
49+
response_execution_request = requests.get(execution_url, auth=auth)
50+
if response_execution_request.status_code == 200:
51+
try:
52+
response_execution_json = response_execution_request.json()
53+
response_status = response_execution_json["restore"]["execution"]["status"]
54+
self.assertEqual(response_status, 'COMPLETED', "backup initiated successfully")
55+
except ValueError as e:
56+
print("Error parsing JSON:", e)
57+
print("Raw response content:", response_execution_request.text)
58+
else:
59+
print(f"Request failed with status code {response_execution_request.status_code}")
60+
print("Response content:", response_execution_request.text)
61+
62+
# Verify the response status code
63+
self.assertEqual(response.status_code, 201, "backup initiated successfully")
64+
65+
66+
if __name__ == "__main__":
67+
unittest.main()

scripts/entrypoint.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export JAVA_OPTS="${JAVA_OPTS} ${GEOSERVER_OPTS}"
106106
# Chown again - seems to fix issue with resolving all created directories
107107
if [[ ${RUN_AS_ROOT} =~ [Ff][Aa][Ll][Ss][Ee] ]];then
108108
dir_ownership=("${CATALINA_HOME}" /home/"${USER_NAME}"/ "${COMMUNITY_PLUGINS_DIR}"
109-
"${STABLE_PLUGINS_DIR}" "${REQUIRED_PLUGINS_DIR}" "${GEOSERVER_HOME}" /usr/share/fonts/ /tomcat_apps.zip
109+
"${STABLE_PLUGINS_DIR}" "${REQUIRED_PLUGINS_DIR}" "${GEOSERVER_HOME}" /usr/share/fonts/
110110
/tmp/ "${FOOTPRINTS_DATA_DIR}" "${CERT_DIR}" "${FONTS_DIR}" /scripts/
111111
"${EXTRA_CONFIG_DIR}" "/docker-entrypoint-geoserver.d" "${MONITOR_AUDIT_PATH}")
112112
for directory in "${dir_ownership[@]}"; do

scripts/setup.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,16 @@ rm -f /tmp/resources/overlays/README.txt &&
8989
# Package tomcat webapps - useful to activate later
9090
if [[ -d "${CATALINA_HOME}"/webapps.dist ]]; then
9191
mv "${CATALINA_HOME}"/webapps.dist /tomcat_apps
92-
zip -r /tomcat_apps.zip /tomcat_apps
92+
zip -r "${REQUIRED_PLUGINS_DIR}"/tomcat_apps.zip /tomcat_apps
9393
rm -r /tomcat_apps
9494
else
9595
cp -r "${CATALINA_HOME}"/webapps/ROOT /tomcat_apps
9696
cp -r "${CATALINA_HOME}"/webapps/docs /tomcat_apps
9797
cp -r "${CATALINA_HOME}"/webapps/examples /tomcat_apps
9898
cp -r "${CATALINA_HOME}"/webapps/host-manager /tomcat_apps
9999
cp -r "${CATALINA_HOME}"/webapps/manager /tomcat_apps
100-
zip -r /tomcat_apps.zip /tomcat_apps
101-
rm -r /tomcat_apps
100+
zip -r "${REQUIRED_PLUGINS_DIR}"/tomcat_apps.zip /tomcat_apps
101+
rm -rf /tomcat_apps
102102
fi
103103

104104
pushd ${CATALINA_HOME}/lib || exit

scripts/start.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,12 @@ fi
326326

327327

328328
if [[ "${TOMCAT_EXTRAS}" =~ [Tt][Rr][Uu][Ee] ]]; then
329-
unzip -qq /tomcat_apps.zip -d /tmp/ &&
330-
cp -r /tmp/tomcat_apps/webapps.dist/* "${CATALINA_HOME}"/webapps/ &&
329+
unzip -qq "${REQUIRED_PLUGINS_DIR}"/tomcat_apps.zip -d /tmp/
330+
if [[ -d /tmp/tomcat_apps/webapps.dist ]];then
331+
cp -r /tmp/tomcat_apps/webapps.dist/* "${CATALINA_HOME}"/webapps/
332+
else
333+
cp -r /tmp/tomcat_apps/* "${CATALINA_HOME}"/webapps/
334+
fi
331335
rm -r /tmp/tomcat_apps
332336
if [[ ${POSTGRES_JNDI} =~ [Ff][Aa][Ll][Ss][Ee] ]]; then
333337
if [[ -f ${EXTRA_CONFIG_DIR}/context.xml ]]; then

0 commit comments

Comments
 (0)