Files
docker-compose/yaml_convert2.py
2025-11-30 17:04:24 +01:00

154 lines
5.2 KiB
Python

import yaml
import sys
import re
stack_name = sys.argv[1]
INPUT_FILE = f"{stack_name}/docker-compose.yml"
OUTPUT_FILE = f"__swarm/{stack_name}/{stack_name}-swarm.yml"
def fix_env_file(filepath):
"""Convert YAML-style env (KEY: value) into Docker env (KEY=value)."""
fixed_lines = []
changed = False
with open(filepath, "r") as f:
for line in f:
stripped = line.strip()
# Skip empty/comment lines
if not stripped or stripped.startswith("#"):
fixed_lines.append(line)
continue
# Match YAML-style: KEY: value
m = re.match(r"^([A-Za-z0-9_]+):\s*(.*)$", stripped)
if m:
key, value = m.groups()
fixed = f"{key}={value}\n"
fixed_lines.append(fixed)
changed = True
else:
# Validate Docker env format
if " " in stripped:
raise ValueError(f"Invalid env line (contains spaces): {stripped}")
if ":" in stripped:
raise ValueError(f"Invalid env line (contains colon): {stripped}")
fixed_lines.append(line)
# Write back only if changes were needed
if changed:
with open(filepath, "w") as f:
f.writelines(fixed_lines)
print(f"[FIXED] Converted YAML env → Docker env in {filepath}")
else:
print(f"[OK] .env file already valid: {filepath}")
def convert_ports(ports):
"""Convert short port syntax to Swarm long syntax."""
result = []
print(f"Converting ports: {ports}")
for p in ports:
print(f"Port entry: {p}")
if isinstance(p, str):
# format: "8080:80"
pub, tgt = p.split(":")
result.append({
"target": int(tgt),
"published": int(pub),
"protocol": "tcp",
"mode": "ingress"
})
else:
result.append(p)
return result
def to_str_lower(value):
"""Convert value to string. Booleans become lowercase 'true'/'false'."""
if isinstance(value, bool):
return "true" if value else "false"
return str(value)
def env_list_to_dict(env_list):
"""Convert environment from list ['KEY=VAL'] to dict {KEY: VAL} as strings."""
env_dict = {}
for item in env_list:
key, value = item.split("=", 1)
# convert 'true'/'false' strings to lowercase
if value.lower() in ["true", "false"]:
env_dict[key] = value.lower()
else:
env_dict[key] = str(value)
return env_dict
def ensure_labels_as_string(labels):
"""Ensure all label values are strings, lowercase for booleans."""
return {k: to_str_lower(v) for k, v in labels.items()}
def convert_compose_to_swarm(data):
services = data.get("services", {})
#input(services)
for name, svc in services.items():
print(f"Converting service: {name} , svc: {svc}")
if name in ["container_name", "restart", "depends_on"]:
continue
svc.pop('restart', None)
svc.pop('depends_on', None)
svc.pop('container_name', None)
# 1) Convert environment list → dict (strings)
if "environment" in svc and isinstance(svc["environment"], list):
svc["environment"] = env_list_to_dict(svc["environment"])
# 2) Ensure deploy exists
deploy = svc.setdefault("deploy", {})
# 3) Move labels into deploy.labels, all as strings (lowercase booleans)
if "labels" in svc:
deploy.setdefault("labels", {})
if isinstance(svc["labels"], dict):
deploy["labels"].update(ensure_labels_as_string(svc["labels"]))
elif isinstance(svc["labels"], list):
for label in svc["labels"]:
key, value = label.split("=", 1)
deploy["labels"][key] = value.lower() if value.lower() in ["true", "false"] else str(value)
del svc["labels"]
labels = deploy.get("labels", {})
if "homepage.server" in labels and labels["homepage.server"] == "my-docker":
labels["homepage.server"] = "my-docker-swarm"
# 4) Default replicas
deploy.setdefault("replicas", 1)
# 5) Add placement constraint
deploy.setdefault("placement", {})
deploy["placement"].setdefault("constraints", [])
if "node.role == manager" not in deploy["placement"]["constraints"]:
deploy["placement"]["constraints"].append("node.role == manager")
# 6) Convert ports to long format
if "ports" in svc:
#input(svc)
svc["ports"] = convert_ports(svc["ports"])
# 7) Remove container_name (not allowed in Swarm)
svc.pop("container_name", None)
return data
def main():
fix_env_file(f"__swarm/{stack_name}/stack.env") # NEW FIX STEP
with open(INPUT_FILE, "r") as f:
compose = yaml.safe_load(f)
#input(compose)
swarm = convert_compose_to_swarm(compose)
with open(OUTPUT_FILE, "w") as f:
yaml.dump(swarm, f, sort_keys=False)
print(f"Swarm stack file written to {OUTPUT_FILE}")
if __name__ == "__main__":
main()