From 783d077a489d15dc239f82bead6bda5ab44f7142 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Gramain?= <mael@enpls.org>
Date: Mon, 21 Oct 2024 20:38:51 +0200
Subject: [PATCH] ci for traefik labels uniqueness

---
 .ci/traefik_label_watcher.py | 100 +++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 .ci/traefik_label_watcher.py

diff --git a/.ci/traefik_label_watcher.py b/.ci/traefik_label_watcher.py
new file mode 100644
index 0000000..dda93b0
--- /dev/null
+++ b/.ci/traefik_label_watcher.py
@@ -0,0 +1,100 @@
+import os 
+from pathlib import Path
+
+def recursiveSearchForDockerComposeFiles(watchFolder: Path):
+    dockerComposeList = []
+    for root, _, files in os.walk(watchFolder):
+        for file in files:
+            if file == "docker-compose.yaml":
+                dockerComposeList.append(Path(root) / file)
+    return dockerComposeList
+
+def readDockerComposeFile(dockerComposeFile: Path) -> str:
+    with open(dockerComposeFile, "r") as file:
+        data = file.read()
+    return data
+
+def getTraefikLabelsFromDockerComposeFile(dockerComposeFile: str):
+    traefikLabels = []
+    # Find all traefik labels
+    d = readDockerComposeFile(dockerComposeFile)
+    lines = d.split("\n")
+    for line in lines:
+        if "traefik." in line:
+            traefikLabels.append(line)
+    return traefikLabels
+
+def parseTraefikLabels(traefikLabels: list):
+    s = {
+        "routers": [],
+        "services": [],
+    }
+    for label in traefikLabels:
+        if "traefik.http.routers" in label:
+            routerName = label.split(".")[3]
+            s["routers"].append(routerName)
+        elif "traefik.http.services" in label:
+            serviceName = label.split(".")[3]
+            s["services"].append(serviceName)
+    # Make unique
+    s["routers"] = list(set(s["routers"]))
+    s["services"] = list(set(s["services"]))
+    return s
+
+def checkLabelUnique(labels: dict) -> bool:
+    routerNames = []
+    wrong = False
+    for project in labels:
+        for router in labels[project]["routers"]:
+            if router in routerNames:
+                if not wrong:
+                    print("\n\n Seems like some routers are not unique šŸ–\n")
+                print(f"šŸ”“ ERROR : Router *{router}* is not unique")
+                wrong = True
+                for project2 in labels:
+                    if router in labels[project2]["routers"]:
+                        print(f"\t{project} and {project2}")
+            routerNames.append(router)
+    return wrong
+def beautifulDisplayLabels(labels: dict, projectName: str):
+    print(f"\n======\n🧰 Project: {projectName}")
+    print("šŸŽ›ļø  Routers:")
+    for router in labels["routers"]:
+        print(f"\t{router}")
+    if len(labels["services"]) != 0:
+        print("šŸŽ›ļø  Services:")
+        for service in labels["services"]:
+            print(f"\t{service}")
+            
+def main(watchFolder: str):
+    # For each folder, read docker-compose.yaml and look for traefik labels
+    dockerComposeList = recursiveSearchForDockerComposeFiles(watchFolder)
+    labels = {}
+    for dockerComposeFile in dockerComposeList:
+        traefikLabels = getTraefikLabelsFromDockerComposeFile(dockerComposeFile)
+        if len(traefikLabels) == 0:
+            #print(f"No traefik labels found in {dockerComposeFile}")
+            continue
+        parsedTraefikLabels = parseTraefikLabels(traefikLabels)
+        projectName = dockerComposeFile.parent.name
+        labels[projectName] = parsedTraefikLabels
+        beautifulDisplayLabels(parsedTraefikLabels, projectName)
+    # Check if all routers are unique
+    wrong = checkLabelUnique(labels)
+    
+    if wrong:
+        exit(1)
+    else:
+        print("\n\nāœ… All routers are unique\n\n")
+        exit(0)
+
+if __name__ == "__main__":
+    print("Traefik label watcher CI/CD")
+    # Read folder from env
+    watchFolder = os.getenv("WATCH_FOLDER")
+    if watchFolder == None:
+        watchFolder = "."
+        
+    watchFolder = Path(watchFolder)
+    
+    main(watchFolder)
\ No newline at end of file