import yaml import sys stack_name = sys.argv[1] INPUT_FILE = f"{stack_name}/docker-compose.yml" OUTPUT_FILE = f"__swarm/{stack_name}/{stack_name}-swarm.yml" 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"] # 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(): 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()