Files
portainer-export/porta_dump.py
2025-07-23 00:36:13 +01:00

77 lines
2.1 KiB
Python
Executable File

#!/usr/bin/env -S uv run --script --python >=3.10
# /// script
# dependencies = [
# "requests",
# "rich"
# ]
# ///
import configparser
import requests
import sys
from datetime import datetime
from rich import print
from rich.progress import track, Progress
from rich.traceback import install
install(show_locals=True)
c = configparser.RawConfigParser()
# trick to not need [DEFAULT] header in config file
config_str = '[DEFAULT]\n' + open(sys.path[0] + '/CONFIG', 'r').read()
c.read_string(config_str)
PORTA_URL = c.get("DEFAULT", "PORTAINER_URL")
API_KEY = c.get("DEFAULT", "PORTAINER_API_KEY")
headers = {
"X-API-Key": API_KEY
}
s = requests.Session()
s.headers.update(headers)
# Get environments / endpoints
with Progress(transient=True) as prog:
task_getep = prog.add_task("Querying endpoints", start=False)
r = s.get(PORTA_URL + "/api/endpoints")
environs = r.json()
#print(repr(environs))
endpoints = {}
for i in environs:
endpoints[i["Id"]] = i
# Get stacks
with Progress(transient=True) as prog:
task_getst = prog.add_task("Querying stacks", start=False)
r = s.get(PORTA_URL + "/api/stacks")
stacks = r.json()
for i in track(stacks, description="Exporting stacks", transient=True):
#print(repr(i))
st = i["Status"] # 1 = Started / 2 = Stopped
ep = endpoints[i["EndpointId"]]["Name"]
stamp = i["UpdateDate"] if i["UpdateDate"]>0 else i["CreationDate"]
stamp_dt = datetime.fromtimestamp(stamp)
stamp_filename = stamp_dt.strftime("%Y-%m-%d_%H%M%S")
print(f"{i['Id']} {st} - {i['Name']} (endpoint: {ep}) - {stamp_dt}")
r = s.get(PORTA_URL + f"/api/stacks/{i['Id']}/file")
sf = r.json()
stackfile = sf["StackFileContent"]
sf_name = f"stack_{ep}_{i['Name']}_{stamp_filename}"
if st == 2:
sf_name += "_STOPPED"
# Dump Yaml
with open(sf_name + ".yaml", "wt") as f:
f.write(stackfile)
# Dump ENV if any
if len(i["Env"]) > 0:
with open(sf_name + ".env", "wt") as f:
for j in i["Env"]:
f.write(f"{j['name']}={j['value']}\n")