mirror of
https://github.com/jkocon/hassio-addons.git
synced 2026-02-24 05:14:41 +01:00
Delete minio_backup directory
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
# MinIO Backup Add-on
|
|
||||||
|
|
||||||
This add-on automatically creates Home Assistant backups, stores them on a MinIO
|
|
||||||
(S3-compatible) server, and manages retention for daily and monthly backups.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- Automatic daily and monthly backups
|
|
||||||
- Retention control (default: 3 daily, 12 monthly)
|
|
||||||
- Optional AES-256 encryption
|
|
||||||
- Upload to MinIO / S3-compatible servers
|
|
||||||
- Uses Supervisor API for snapshot creation
|
|
||||||
|
|
||||||
### Decryption (if encryption is enabled)
|
|
||||||
openssl enc -d -aes-256-cbc -pbkdf2 -in backup.tar.enc -out backup.tar -k "PASSWORD"
|
|
||||||
|
|
||||||
### Configuration options
|
|
||||||
- `minio_endpoint` – MinIO server URL
|
|
||||||
- `minio_access_key`
|
|
||||||
- `minio_secret_key`
|
|
||||||
- `minio_bucket`
|
|
||||||
- `daily_to_keep`
|
|
||||||
- `monthly_to_keep`
|
|
||||||
- `encryption_enabled` – true/false
|
|
||||||
- `encryption_password`
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "MinIO Backup",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"slug": "minio_backup",
|
|
||||||
"startup": "application",
|
|
||||||
"description": "Automatic Home Assistant backups to a MinIO S3 server with retention and optional AES-256 encryption.",
|
|
||||||
"arch": ["aarch64", "amd64"],
|
|
||||||
"schema": {
|
|
||||||
"minio_endpoint": "str",
|
|
||||||
"minio_access_key": "str",
|
|
||||||
"minio_secret_key": "str",
|
|
||||||
"minio_bucket": "str",
|
|
||||||
"daily_to_keep": "int",
|
|
||||||
"monthly_to_keep": "int",
|
|
||||||
"daily_time": "str",
|
|
||||||
"encryption_enabled": "bool",
|
|
||||||
"encryption_password": "str"
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"minio_endpoint": "https://your-minio-server.com",
|
|
||||||
"minio_access_key": "",
|
|
||||||
"minio_secret_key": "",
|
|
||||||
"minio_bucket": "ha-backup",
|
|
||||||
"daily_to_keep": 3,
|
|
||||||
"monthly_to_keep": 12,
|
|
||||||
"daily_time": "03:00",
|
|
||||||
"encryption_enabled": true,
|
|
||||||
"encryption_password": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
FROM python:3.11-slim
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y openssl && apt-get clean
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY run.py /app/run.py
|
|
||||||
|
|
||||||
RUN pip install requests boto3
|
|
||||||
|
|
||||||
CMD ["python", "/app/run.py"]
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import os, time, json, requests, boto3, subprocess
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
SUPERVISOR_TOKEN = os.environ.get("SUPERVISOR_TOKEN")
|
|
||||||
HEADERS = {"Authorization": f"Bearer {SUPERVISOR_TOKEN}"}
|
|
||||||
|
|
||||||
def load_cfg():
|
|
||||||
with open("/data/options.json") as f:
|
|
||||||
return json.load(f)
|
|
||||||
|
|
||||||
def create_backup():
|
|
||||||
name = f"auto_backup_{datetime.now().strftime('%Y-%m-%d_%H-%M')}"
|
|
||||||
r = requests.post(
|
|
||||||
"http://supervisor/supervisor/backups",
|
|
||||||
headers=HEADERS,
|
|
||||||
json={"name": name}
|
|
||||||
)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json()["slug"]
|
|
||||||
|
|
||||||
def download_backup(slug, path="/tmp/backup.tar"):
|
|
||||||
dl = requests.get(
|
|
||||||
f"http://supervisor/supervisor/backups/{slug}/download",
|
|
||||||
headers=HEADERS,
|
|
||||||
stream=True
|
|
||||||
)
|
|
||||||
dl.raise_for_status()
|
|
||||||
with open(path, "wb") as f:
|
|
||||||
for chunk in dl.iter_content(1024 * 64):
|
|
||||||
f.write(chunk)
|
|
||||||
return path
|
|
||||||
|
|
||||||
def encrypt_backup(input_path, password):
|
|
||||||
output_path = input_path + ".enc"
|
|
||||||
subprocess.run([
|
|
||||||
"openssl", "enc", "-aes-256-cbc",
|
|
||||||
"-salt", "-pbkdf2",
|
|
||||||
"-k", password,
|
|
||||||
"-in", input_path,
|
|
||||||
"-out", output_path
|
|
||||||
], check=True)
|
|
||||||
return output_path
|
|
||||||
|
|
||||||
def upload_minio(path, cfg, backup_type):
|
|
||||||
s3 = boto3.client(
|
|
||||||
"s3",
|
|
||||||
aws_access_key_id=cfg["minio_access_key"],
|
|
||||||
aws_secret_access_key=cfg["minio_secret_key"],
|
|
||||||
endpoint_url=cfg["minio_endpoint"]
|
|
||||||
)
|
|
||||||
key = f"{backup_type}/backup_{int(time.time())}{os.path.splitext(path)[1]}"
|
|
||||||
s3.upload_file(path, cfg["minio_bucket"], key)
|
|
||||||
print(f"[OK] Uploaded: {key}")
|
|
||||||
cleanup_retention(s3, cfg["minio_bucket"], backup_type, cfg)
|
|
||||||
|
|
||||||
def cleanup_retention(s3, bucket, backup_type, cfg):
|
|
||||||
prefix = f"{backup_type}/"
|
|
||||||
keep = cfg["monthly_to_keep"] if backup_type == "monthly" else cfg["daily_to_keep"]
|
|
||||||
objs = s3.list_objects_v2(Bucket=bucket, Prefix=prefix).get("Contents", [])
|
|
||||||
if len(objs) <= keep:
|
|
||||||
return
|
|
||||||
objs.sort(key=lambda x: x["LastModified"])
|
|
||||||
to_delete = objs[:-keep]
|
|
||||||
for o in to_delete:
|
|
||||||
s3.delete_object(Bucket=bucket, Key=o["Key"])
|
|
||||||
print(f"[CLEAN] Deleted old backup: {o['Key']}")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
cfg = load_cfg()
|
|
||||||
backup_type = "monthly" if datetime.now().day == 1 else "daily"
|
|
||||||
|
|
||||||
print("[INFO] Creating backup…")
|
|
||||||
slug = create_backup()
|
|
||||||
|
|
||||||
print("[INFO] Downloading backup…")
|
|
||||||
path = download_backup(slug)
|
|
||||||
|
|
||||||
# optional encryption
|
|
||||||
if cfg.get("encryption_enabled") and cfg.get("encryption_password"):
|
|
||||||
print("[INFO] Encrypting with AES-256…")
|
|
||||||
path = encrypt_backup(path, cfg["encryption_password"])
|
|
||||||
else:
|
|
||||||
print("[INFO] Encryption disabled.")
|
|
||||||
|
|
||||||
print("[INFO] Uploading to MinIO…")
|
|
||||||
upload_minio(path, cfg, backup_type)
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.remove(path)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
print("[DONE] Backup process finished.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user