105 lines
3.5 KiB
Python
105 lines
3.5 KiB
Python
![]() |
#!/usr/bin/env python3
|
||
|
import requests, subprocess, os, uuid, datetime, csv
|
||
|
import client_config as config
|
||
|
|
||
|
# 获取或生成客户端 ID
|
||
|
def get_client_id():
|
||
|
if os.path.exists(config.CLIENT_ID_FILE):
|
||
|
return open(config.CLIENT_ID_FILE).read().strip()
|
||
|
cid = str(uuid.uuid4())
|
||
|
os.makedirs(os.path.dirname(config.CLIENT_ID_FILE) or ".", exist_ok=True)
|
||
|
open(config.CLIENT_ID_FILE,"w").write(cid)
|
||
|
return cid
|
||
|
|
||
|
# 读取本地 CSV
|
||
|
def read_csv(file_path):
|
||
|
if not os.path.exists(file_path):
|
||
|
return {}
|
||
|
with open(file_path, newline='', encoding='utf-8') as f:
|
||
|
reader = csv.DictReader(f)
|
||
|
return { row['image_name']: row for row in reader }
|
||
|
|
||
|
# 写入 CSV 文件
|
||
|
def write_csv(file_path, rows):
|
||
|
os.makedirs(os.path.dirname(file_path) or ".", exist_ok=True)
|
||
|
with open(file_path,'w',newline='',encoding='utf-8') as f:
|
||
|
writer = csv.DictWriter(f, fieldnames=config.CLIENT_FIELDS)
|
||
|
writer.writeheader()
|
||
|
writer.writerows(rows)
|
||
|
|
||
|
# 获取服务器镜像列表
|
||
|
def get_server_images():
|
||
|
return requests.get(config.SERVER_API).json()
|
||
|
|
||
|
# 获取本地 Docker 镜像
|
||
|
def get_local_images():
|
||
|
output = subprocess.check_output(["docker","images","--format","{{.Repository}}:{{.Tag}}"]).decode()
|
||
|
local_images = {}
|
||
|
for line in output.strip().split("\n"):
|
||
|
if ":" in line:
|
||
|
name, tag = line.split(":",1)
|
||
|
local_images[name] = tag
|
||
|
return local_images
|
||
|
|
||
|
# 下载并加载镜像
|
||
|
def download_and_load(url):
|
||
|
os.makedirs(config.DOWNLOAD_DIR, exist_ok=True)
|
||
|
filename = os.path.join(config.DOWNLOAD_DIR, url.split("/")[-1])
|
||
|
print(f"下载镜像 {url} 到 {filename}")
|
||
|
subprocess.run(["wget","-q","-O",filename,url],check=True)
|
||
|
print(f"加载镜像 {filename} 到 Docker")
|
||
|
subprocess.run(["docker","load","-i",filename],check=True)
|
||
|
|
||
|
# 上报客户端镜像状态
|
||
|
def report_to_server(client_id, images):
|
||
|
requests.post(config.CLIENT_UPDATE_API,json={"client_id":client_id,"images":images})
|
||
|
|
||
|
# 主函数
|
||
|
def main():
|
||
|
cid = get_client_id()
|
||
|
server_images = get_server_images()
|
||
|
local_images = get_local_images()
|
||
|
local_csv = read_csv(config.CLIENT_FILE)
|
||
|
report_data = []
|
||
|
|
||
|
for img in server_images:
|
||
|
name = img["image_name"]
|
||
|
tag = img["image_tag"]
|
||
|
server_time_str = img["last_updated"]
|
||
|
url = img["download_url"]
|
||
|
server_time = datetime.datetime.strptime(server_time_str, "%Y-%m-%d %H:%M:%S")
|
||
|
|
||
|
local_record = local_csv.get(name)
|
||
|
local_tag = local_images.get(name)
|
||
|
local_time = None
|
||
|
if local_record:
|
||
|
local_time = datetime.datetime.strptime(local_record["last_updated"], "%Y-%m-%d %H:%M:%S")
|
||
|
|
||
|
# 判断是否需要下载
|
||
|
need_update = False
|
||
|
if local_tag != tag:
|
||
|
need_update = True
|
||
|
elif local_time is None:
|
||
|
need_update = True
|
||
|
elif local_time < server_time:
|
||
|
need_update = True
|
||
|
|
||
|
if need_update:
|
||
|
print(f"[更新] {name}:{tag}")
|
||
|
download_and_load(url)
|
||
|
now_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
|
else:
|
||
|
print(f"[已最新] {name}:{tag}")
|
||
|
now_str = local_time.strftime("%Y-%m-%d %H:%M:%S") if local_time else server_time_str
|
||
|
|
||
|
report_data.append({"image_name":name,"image_tag":tag,"last_updated":now_str})
|
||
|
|
||
|
# 写本地 CSV
|
||
|
write_csv(config.CLIENT_FILE, report_data)
|
||
|
# 上报服务器
|
||
|
report_to_server(cid, report_data)
|
||
|
print("✅ 客户端镜像状态已上报服务器")
|
||
|
|
||
|
if __name__=="__main__":
|
||
|
main()
|