Skip to content

Commit

Permalink
feat: add minecraft backup method
Browse files Browse the repository at this point in the history
Minecraft server can now be backed up using the restic-compose-backup.minecraft flag
  • Loading branch information
Silthus committed Nov 25, 2020
1 parent 02ae4ca commit 9eb0501
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 195 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ venv
restic_data/
restic_cache/
alerts.env
minecraft/

# build
build/
Expand Down
11 changes: 11 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ services:
volumes:
- pgdata:/var/lib/postgresql/data

minecraft:
image: itzg/minecraft-server
labels:
restic-compose-backup.minecraft: true
restic-compose-backup.volumes.include: "minecraft"
environment:
- RCON_PASSWORD=minecraft
- EULA=TRUE
volumes:
- ./minecraft:/data

volumes:
mysqldata:
mariadbdata:
Expand Down
3 changes: 0 additions & 3 deletions src/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,5 @@ ENV XDG_CACHE_HOME=/cache

# end install

ADD backup.sh /backup.sh
RUN chmod +x /backup.sh

ENTRYPOINT []
CMD ["./entrypoint.sh"]
158 changes: 0 additions & 158 deletions src/backup.sh

This file was deleted.

3 changes: 2 additions & 1 deletion src/crontab
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
10 2 * * * . /env.sh && /backup.sh > /proc/1/fd/1 2>&1
10 2 * * * source /env.sh && rcb backup > /proc/1/fd/1

17 changes: 16 additions & 1 deletion src/restic_compose_backup/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
restic,
)
from restic_compose_backup.config import Config
from restic_compose_backup.containers import RunningContainers
from restic_compose_backup.containers import RunningContainers, Container
from restic_compose_backup import cron, utils

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -94,6 +94,21 @@ def status(config, containers):
for container in backup_containers:
logger.info('service: %s', container.service_name)

if container.minecraft_backup_enabled:
instance = container.instance
ping = instance.ping()
logger.info(
' - %s (is_ready=%s):',
instance.container_type,
ping == 0
)
for mount in container.filter_mounts():
logger.info(
' - volume: %s -> %s',
mount.source,
container.get_volume_backup_destination(mount, '/minecraft'),
)

if container.volume_backup_enabled:
for mount in container.filter_mounts():
logger.info(
Expand Down
4 changes: 2 additions & 2 deletions src/restic_compose_backup/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@


class Config:
default_backup_command = ". /env.sh && /backup.sh > /proc/1/fd/1 2>&1"
default_crontab_schedule = "0 */4 * * *"
default_backup_command = "source /env.sh && rcb backup > /proc/1/fd/1"
default_crontab_schedule = "10 2 * * *"

"""Bag for config values"""
def __init__(self, check=True):
Expand Down
3 changes: 2 additions & 1 deletion src/restic_compose_backup/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def backup_enabled(self) -> bool:
return any([
self.volume_backup_enabled,
self.database_backup_enabled,
self.minecraft_backup_enabled
])

@property
Expand Down Expand Up @@ -206,7 +207,7 @@ def filter_mounts(self):
exclude_bind_mounts = utils.is_true(config.exclude_bind_mounts)
mounts = list(filter(lambda m: not exclude_bind_mounts or m.type == "volume", self._mounts))

if not self.volume_backup_enabled:
if not self.volume_backup_enabled and not self.minecraft_backup_enabled:
return filtered

if self._include:
Expand Down
61 changes: 36 additions & 25 deletions src/restic_compose_backup/containers_minecraft.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import os
import logging

from pathlib import Path

from restic_compose_backup.containers import Container
Expand All @@ -9,6 +12,8 @@
)
from restic_compose_backup import utils

logger = logging.getLogger(__name__)


class MinecraftContainer(Container):
container_type = 'minecraft'
Expand All @@ -21,16 +26,30 @@ def get_credentials(self) -> dict:
'port': self.get_config_env('RCON_PORT'),
}

def prepare_mc_backup(self) -> bool:
creds = self.get_credentials()

with utils.environment('RCON_PASSWORD', creds['password']):
rcon.save_off(creds['host'], creds['port'])
rcon.save_all(creds['host'], creds['port'])
rcon.sync(creds['host'], creds['port'])
return True

def ping(self) -> bool:
"""Check the availability of the service"""
creds = self.get_credentials()

logger.debug("[rcon-cli] checking if minecraft server %s is online...", self.service_name)
with utils.environment('RCON_PASSWORD', creds['password']):
return rcon.is_online(
creds['host'],
creds['port']
)
try:
logger.debug("[rcon-cli] checking if minecraft server %s is online...", self.service_name)
with utils.environment('RCON_PASSWORD', creds['password']):
return rcon.is_online(
creds['host'],
creds['port']
)
except Exception as ex:
logger.error('[rcon-cli] unable to contact minecraft server %s', self.service_name)
logger.exception(ex)
return 1

def backup(self) -> bool:
config = Config()
Expand All @@ -40,15 +59,16 @@ def backup(self) -> bool:
with utils.environment('RCON_PASSWORD', creds['password']):
try:
# turn off auto-save and sync all data to the disk before backing up worlds
prepare_mc_backup()
for mount in container.filter_mounts():
backup_data = container.get_volume_backup_destination(mount, '/volumes')
logger.info('Backing up %s', mount.source)
vol_result = restic.backup_files(config.repository, source=backup_data)
logger.debug('Minecraft backup exit code: %s', vol_result)
if vol_result != 0:
logger.error('Minecraft backup exited with non-zero code: %s', vol_result)
errors = True
self.prepare_mc_backup()

for mount in self.filter_mounts():
backup_data = self.get_volume_backup_destination(mount, '/minecraft')
logger.info('Backing up %s', mount.source)
vol_result = restic.backup_files(config.repository, source=backup_data)
logger.debug('Minecraft backup exit code: %s', vol_result)
if vol_result != 0:
logger.error('Minecraft backup exited with non-zero code: %s', vol_result)
errors = True
except Exception as ex:
logger.error('Exception raised during minecraft backup')
logger.exception(ex)
Expand All @@ -57,13 +77,4 @@ def backup(self) -> bool:
# always always turn saving back on
rcon.save_on(creds['host'], creds['port'])

return errors


def prepare_mc_backup():
creds = self.get_credentials()

with utils.environment('RCON_PASSWORD', creds['password']):
rcon.save_off(creds['host'], creds['port'])
rcon.save_all(creds['host'], creds['port'])
rcon.sync(creds['host'], creds['port'])
return errors
9 changes: 5 additions & 4 deletions src/restic_compose_backup/rcon.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@
commands,
containers
)
from restic_compose_backup import utils

logger = logging.getLogger(__name__)

def rcon_cli(host, port, cmd: str) -> int:
exit_code = commands.run([
"rcon-cli",
f"--host {host}",
f"--port {port}",
f"--host={host}",
f"--port={port}",
cmd
])

if exit_code != 0:
raise RconException("rcon-cli %s exited with a non-zero exit code: %s", cmd, exit_code)
raise RconException(f"rcon-cli {cmd} exited with a non-zero exit code: {exit_code}")

return exit_code

def is_online(host, port) -> int:
"""Check if rcon can be reached"""
return rcon_cli(host, port, "version")
return rcon_cli(host, port, "help")

def save_off(host, port) -> int:
"""Turn saving off"""
Expand Down

0 comments on commit 9eb0501

Please sign in to comment.