Support library names in configuration

This commit is contained in:
Maks Snegov 2022-01-30 18:27:37 +03:00
parent 681b11bbc1
commit 724166b4a4
9 changed files with 64 additions and 30 deletions

View File

@ -1,4 +1,4 @@
FROM python:3-slim FROM python:3.8.12-slim-buster
RUN apt-get update && apt-get install gnupg curl -y && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install gnupg curl -y && rm -rf /var/lib/apt/lists/*
RUN curl https://linux-clients.seafile.com/seafile.asc | apt-key add - && \ RUN curl https://linux-clients.seafile.com/seafile.asc | apt-key add - && \
@ -11,7 +11,7 @@ WORKDIR /seafile-client
COPY requirements.txt ./ COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY seafile_client ./seafile_client/ COPY dsc ./dsc/
COPY start.py ./start.py COPY start.py ./start.py
RUN chmod +x /seafile-client/start.py && \ RUN chmod +x /seafile-client/start.py && \

View File

@ -34,7 +34,7 @@ Inside container libraries' content will be put in `/data` directory, so map you
Also you could check [docker-compose example](docker-compose.example.yml). Also you could check [docker-compose example](docker-compose.example.yml).
## Environment variables: ## Environment variables:
- `LIBRARY_ID=<your-library-id-here>` ID of library to sync; multiple libraries could be separated by colon `:`. - `LIBRARY_ID=<your-library-id-here>` Library to sync, ID or name; multiple libraries could be separated by colon `:`.
- `SERVER_HOST=<server-host>` Hostname of your seafile server, eg: `seafile.example.com`. If you're using non-standart port, specify it here, eg: `seafile.example.com:8080`. - `SERVER_HOST=<server-host>` Hostname of your seafile server, eg: `seafile.example.com`. If you're using non-standart port, specify it here, eg: `seafile.example.com:8080`.
- `USERNAME=<username>` Seafile account username. - `USERNAME=<username>` Seafile account username.
- `PASSWORD=<password>` Seafile account password. - `PASSWORD=<password>` Seafile account password.

View File

@ -5,7 +5,7 @@ services:
restart: always restart: always
image: snegov/seafile-client image: snegov/seafile-client
environment: environment:
- LIBRARY_ID="79867cbf-2944-488d-9105-859463ecdf9e:8078e3ff-b2a0-450a-b4dd-c1ed9ef18294" - LIBRARY_ID="79867cbf-2944-488d-9105-859463ecdf9e:test_library"
- SERVER_HOST=seafile.example.com - SERVER_HOST=seafile.example.com
- USERNAME=user - USERNAME=user
- PASSWORD=password - PASSWORD=password
@ -15,7 +15,7 @@ services:
volumes: volumes:
- seafile-data:/seafile-client/seafile-data - seafile-data:/seafile-client/seafile-data
- /home/johndow/seafile:/data - /home/johndow/seafile:/data
container_name: seafile-client
volumes: volumes:
seafile-data: seafile-data:

View File

@ -2,16 +2,16 @@ import logging
import os import os
import subprocess import subprocess
import time import time
from typing import Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from cached_property import cached_property_with_ttl
import requests import requests
from .consts import DEFAULT_USERNAME from dsc import consts
from .misc import create_dir from dsc.misc import create_dir
_lg = logging.getLogger(__name__)
logging.basicConfig(format="%(asctime)s %(message)s",
level=logging.INFO)
class SeafileClient: class SeafileClient:
@ -36,16 +36,24 @@ class SeafileClient:
self.__token = r.json()['token'] self.__token = r.json()['token']
return self.__token return self.__token
def get_lib_name(self, lib_id: str) -> str: @cached_property_with_ttl(ttl=60)
url = f"{self.url}/api2/repos/{lib_id}" def remote_libraries(self) -> dict:
url = f"{self.url}/api2/repos/"
auth_header = {"Authorization": f"Token {self.token}"} auth_header = {"Authorization": f"Token {self.token}"}
r = requests.get(url, headers=auth_header) r = requests.get(url, headers=auth_header)
if r.status_code != 200: if r.status_code != 200:
raise RuntimeError(r.text) raise RuntimeError(r.text)
return r.json()['name'] r_libs = {lib["id"]: lib["name"] for lib in r.json()}
return r_libs
def get_library_id(self, library) -> Optional[str]:
for lib_id, lib_name in self.remote_libraries.items():
if library in (lib_id, lib_name):
return lib_id
return None
def sync_lib(self, lib_id: str, data_dir: str): def sync_lib(self, lib_id: str, data_dir: str):
lib_name = self.get_lib_name(lib_id) lib_name = self.remote_libraries[lib_id]
lib_dir = os.path.join(data_dir, lib_name.replace(' ', '_')) lib_dir = os.path.join(data_dir, lib_name.replace(' ', '_'))
create_dir(lib_dir) create_dir(lib_dir)
cmd = ['seaf-cli', 'sync', cmd = ['seaf-cli', 'sync',
@ -55,11 +63,11 @@ class SeafileClient:
'-u', self.user, '-u', self.user,
'-p', self.password] '-p', self.password]
cmd = ' '.join(cmd) cmd = ' '.join(cmd)
subprocess.run(['su', '-', DEFAULT_USERNAME, '-c', cmd]) subprocess.run(['su', '-', consts.DEFAULT_USERNAME, '-c', cmd])
def get_status(self): def get_status(self):
cmd = 'seaf-cli status' cmd = 'seaf-cli status'
out = subprocess.check_output(['su', '-', DEFAULT_USERNAME, '-c', cmd]) out = subprocess.check_output(['su', '-', consts.DEFAULT_USERNAME, '-c', cmd])
out = out.decode().splitlines() out = out.decode().splitlines()
statuses = dict() statuses = dict()
@ -75,15 +83,25 @@ class SeafileClient:
def watch_status(self): def watch_status(self):
prev_status = dict() prev_status = dict()
while True: while True:
time.sleep(5) time.sleep(consts.STATUS_POLL_PERIOD)
cur_status = self.get_status() cur_status = self.get_status()
for folder, state in cur_status.items(): for folder, state in cur_status.items():
if state != prev_status.get(folder): if state != prev_status.get(folder):
logging.info(f"Library {folder}:\t{state}") logging.info("Library %s:\t%s", folder, state)
prev_status[folder] = cur_status[folder] prev_status[folder] = cur_status[folder]
def get_local_libraries(self) -> set:
cmd = 'seaf-cli list'
out = subprocess.check_output(['su', '-', consts.DEFAULT_USERNAME, '-c', cmd])
out = out.decode().splitlines()[1:] # first line is a header
local_libs = set()
for line in out:
lib_name, lib_id, lib_path = line.rsplit(maxsplit=3)
local_libs.add(lib_id)
return local_libs
def start_seaf_daemon(): def start_seaf_daemon():
cmd = 'seaf-cli start' cmd = 'seaf-cli start'
subprocess.run(['su', '-', DEFAULT_USERNAME, '-c', cmd]) subprocess.run(['su', '-', consts.DEFAULT_USERNAME, '-c', cmd])
time.sleep(5)

View File

@ -1 +1,2 @@
DEFAULT_USERNAME = "seafile" DEFAULT_USERNAME = "seafile"
STATUS_POLL_PERIOD = 1

View File

@ -2,7 +2,7 @@ import os
import pwd import pwd
import subprocess import subprocess
from .consts import DEFAULT_USERNAME from dsc.consts import DEFAULT_USERNAME
def setup_uid(uid: int, gid: int): def setup_uid(uid: int, gid: int):
@ -19,7 +19,3 @@ def create_dir(dir_path: str):
else: else:
if not os.path.isdir(dir_path): if not os.path.isdir(dir_path):
raise RuntimeError(f"Data dir {dir_path} is not a directory") raise RuntimeError(f"Data dir {dir_path} is not a directory")
def tail_f(fpath):
os.system(f"tail -f {fpath}")

View File

@ -1 +1,2 @@
requests cached_property==1.5.2
requests==2.27.1

View File

@ -1,15 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import logging
import os import os
import os.path import os.path
import sys import sys
from seafile_client import SeafileClient, start_seaf_daemon from dsc import SeafileClient, start_seaf_daemon
from seafile_client.misc import setup_uid, create_dir from dsc.misc import setup_uid, create_dir
_lg = logging.getLogger('dsc')
def main(): def main():
logging.basicConfig(format="%(asctime)s %(message)s", level=logging.INFO)
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--uid", default=os.getenv("SEAFILE_UID", default=1000), type=int) parser.add_argument("--uid", default=os.getenv("SEAFILE_UID", default=1000), type=int)
parser.add_argument("--gid", default=os.getenv("SEAFILE_GID", default=100), type=int) parser.add_argument("--gid", default=os.getenv("SEAFILE_GID", default=100), type=int)
@ -21,10 +26,23 @@ def main():
args = parser.parse_args() args = parser.parse_args()
setup_uid(args.uid, args.gid) setup_uid(args.uid, args.gid)
start_seaf_daemon()
create_dir(args.data_dir) create_dir(args.data_dir)
start_seaf_daemon()
libs_to_sync = set()
client = SeafileClient(args.host, args.username, args.password) client = SeafileClient(args.host, args.username, args.password)
for lib_id in args.libs.split(sep=":"): for arg_lib in args.libs.split(sep=":"):
lib_id = client.get_library_id(arg_lib)
if lib_id:
libs_to_sync.add(lib_id)
else:
_lg.warning("Library %s is not found on server %s", arg_lib, args.host)
# don't start to sync libraries already in sync
libs_to_sync -= client.get_local_libraries()
for lib_id in libs_to_sync:
client.sync_lib(lib_id, args.data_dir) client.sync_lib(lib_id, args.data_dir)
client.watch_status() client.watch_status()