Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
noxowl committed Sep 18, 2020
0 parents commit 3e8e846
Show file tree
Hide file tree
Showing 11 changed files with 989 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
__pycache__/
*.py[cod]
*$py.class

.DS_Store
.idea
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM python:3.8-slim

RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
&& apt-get clean && \
rm -rf /var/lib/apt/lists/*

ENV APP_DIR /app

WORKDIR ${APP_DIR}

COPY Pipfile Pipfile.lock ${APP_DIR}/

RUN pip install pipenv --no-cache-dir && \
pipenv install --system --deploy && \
pip uninstall -y pipenv virtualenv-clone virtualenv && \
mkdir ${APP_DIR}/logs && \
mkdir /srv/origin

COPY . ${APP_DIR}/

ENV DROPBOX_BACKUP_ACCESS_TOKEN ""
ENV DROPBOX_BACKUP_WRITE_MODE "overwrite"
ENV DROPBOX_BACKUP_FROM /srv/origin
ENV DROPBOX_BACKUP_TO /backup
ENV DROPBOX_BACKUP_DEBUG_MODE false
ENV DROPBOX_BACKUP_TEST_MODE false

CMD ["python", "-c", "from dropbox_backup import app; app.execute();"]
661 changes: 661 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
dropbox = "*"

[requires]
python_version = "3.8"
76 changes: 76 additions & 0 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Dropbox Backup

Create tar.gz archive and upload to Dropbox.

## Usage

### Build
<code>
docker build -t noxowl/dropbox-backup:latest .
</code>

### Run
<code>
docker run -it --name dropbox-backup -e DROPBOX_BACKUP_ACCESS_TOKEN="YOUR_TOKEN" -e DROPBOX_BACKUP_TO="YOUR_DROPBOX_APP_FOLDER_PATH" -v [ORIGIN_PATH]:/srv/origin noxowl/dropbox-backup:latest
</code>
8 changes: 8 additions & 0 deletions dropbox_backup/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
"""
dropbox_backup
"""

__version__ = '1.0'

from .dropbox_backup import app
62 changes: 62 additions & 0 deletions dropbox_backup/dropbox_backup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
import sys
import logging
from .packer import BackupPacker
from .transporter import DropboxTransporter
from .logger import get_logger


def is_true(value) -> bool:
return value.lower() == 'true'


class DropboxBackup:
def __init__(self):
self.backup = None
self.dropbox = None
self.is_debug = is_true(os.environ['DROPBOX_BACKUP_DEBUG_MODE'])
logging.basicConfig(
filename='logs/backup.log',
level=logging.DEBUG if self.is_debug else logging.INFO)
self.logger = get_logger(__name__)
self.is_test = is_true(os.environ['DROPBOX_BACKUP_TEST_MODE'])
self.access_token = os.environ['DROPBOX_BACKUP_ACCESS_TOKEN']
self.dropbox_write_mode = os.environ['DROPBOX_BACKUP_WRITE_MODE']
self.backup_from = os.environ['DROPBOX_BACKUP_FROM']
self.backup_to = os.environ['DROPBOX_BACKUP_TO']
self.backup_basename = os.path.basename(self.backup_to)
self.pack_to = '/tmp/{0}.tar.gz'.format(self.backup_basename)

def initialize(self):
self.logger.info('Dropbox Backup Initialize Started.')
self.backup = BackupPacker(
pack_from=self.backup_from,
pack_to=self.pack_to,
basename=self.backup_basename
)
self.dropbox = DropboxTransporter(
access_token=self.access_token,
write_mode=self.dropbox_write_mode,
autorename=False
)
self.dropbox.connection_check()
self.logger.info('Dropbox Backup Initialized.')

def dry_run(self):
self.backup.run()

def run(self):
self.backup.run()
self.dropbox.upload(self.pack_to, self.backup_to + '.tar.gz')

def execute(self):
self.initialize()
if self.is_test:
self.dry_run()
else:
self.run()
self.logger.info('Job finished. Exit.')
sys.exit()


app = DropboxBackup()
8 changes: 8 additions & 0 deletions dropbox_backup/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import logging


def get_logger(module_name):
logger = logging.getLogger(module_name)
stream = logging.StreamHandler()
logger.addHandler(stream)
return logger
70 changes: 70 additions & 0 deletions dropbox_backup/packer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import os
import tarfile
import shutil
from .logger import get_logger


def dir_size(root_path):
total = 0
for path, dirs, files in os.walk(root_path):
for file in files:
filepath = os.path.join(path, file)
total += os.path.getsize(filepath)
return total


class BackupPacker:
def __init__(self, pack_from: str, pack_to: str, basename):
self.logger = get_logger(__name__)
self.pack_from = pack_from
self.pack_to = pack_to
self.backup_basename = basename
self.temp_path = '/tmp/origin'

def copy_origin(self) -> None:
self.logger.info('coping original file...')
shutil.copytree(self.pack_from, self.temp_path)
for file in os.listdir(self.temp_path):
os.chown(os.path.join(self.temp_path, file), uid=1000, gid=1000)
self.logger.info('original file copied.')
self.logger.debug(os.listdir(self.temp_path))

def make_tar(self) -> None:
self.logger.info('initiate packing.')
with tarfile.open(self.pack_to, "w:gz") as tar:
for file in os.listdir(self.temp_path):
tar.add(
'{0}/{1}'.format(self.temp_path, file),
arcname='{0}/{1}'.format(self.backup_basename, os.path.basename(file)),
recursive=False,
filter=self._filter
)
if not os.path.exists(self.pack_to):
self.logger.error('Packed file not exist. abort.')
raise FileNotFoundError
else:
self.logger.info('Packed file {0} is successfully created in {1} → {2} byte'.format(
self.backup_basename,
self.pack_to,
os.path.getsize(self.pack_to)
))

def _filter(self, tarinfo: tarfile.TarInfo):
if tarinfo.isdir():
self.logger.info(
'pack {0} → {1} byte'.format(
tarinfo.name,
dir_size(os.path.join(self.temp_path, os.path.basename(tarinfo.name)))
)
)
else:
self.logger.info(
'pack {0} → {1} byte'.format(
tarinfo.name,
tarinfo.size)
)
return tarinfo

def run(self):
self.copy_origin()
self.make_tar()
41 changes: 41 additions & 0 deletions dropbox_backup/transporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import dropbox
from .logger import get_logger

mode = {
'add': 'add',
'overwrite': 'overwrite',
'update': 'update'
}


class DropboxTransporter:
def __init__(self, access_token: str, write_mode: str, autorename: bool):
self.logger = get_logger(__name__)
self.access_token = access_token
self.dropbox_session = dropbox.Dropbox(self.access_token)
self.autorename = autorename
try:
self.write_mode = dropbox.files.WriteMode(mode[write_mode])
except KeyError:
self.logger.error('Please input the correct Write Mode. [add/overwrite/update]')
raise KeyError

def upload(self, file, file_to) -> None:
self.logger.info('initiate dropbox upload.')
with open(file, 'rb') as f:
self.dropbox_session.files_upload(
f.read(),
file_to,
mode=self.write_mode,
autorename=self.autorename)
self.logger.info('dropbox upload finished.')

def connection_check(self) -> None:
payload = 'ping'
echo = self.dropbox_session.check_user(payload)
self.logger.debug(echo)
if echo.result != payload:
self.logger.error('Dropbox connection failed.')
raise ConnectionError
else:
self.logger.error('Dropbox connection success.')

0 comments on commit 3e8e846

Please sign in to comment.