diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..ca46cf9 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,30 @@ +# A guide to effective docker deployment for Open Cloud + +We need a reliable process using the existing tools and the creation of new one to have a consistant deployment of Open Cloud when each service is running in a docker container. + +This document aims at addressing : +- The existing tools used +- The functionning of said tools +- The needed improvement (bugs/new features) +- The required configuration for each service + +## Steps + +- Downloading the repos : `oc-deploy/download_oc.py` uses the interactivity offered by python's library to select and follow the cloning of the repos on the forge, `oc-deploy/clone_opencloud_microservices.sh` is more straifhtforward using bash. + +- Selecting the services to launch : `build_containers.sh` asks the user for the services that need to be launched. The user can choose non essential services (in front, monitord and shared) to be added to tthe list of minimum service to run open cloud (auth, catalog, datacenter, peer, workspace, worflow, scheduler, schedulerd) + +- Verify if the service really need a `docker build` : this operation is time and resource consumming, so we need to check : + - is a container already runs + - does an image already exist + + and prompt the user if he wants to proceed with the build, or just start a container with the existing image or let the the current container run. + +- Fill the configuration file for each service selected to be built. + +## Todo + +- Implement a script that interacts with the user to fill the configuration json file + +- Remove the filed json file from the forge to prevent that data from other dev are stored and used during build, which would lead the services to be missconfigured + - We could let some generic value, like ports, container addresses... diff --git a/docker/build_containers.sh b/docker/build_containers.sh new file mode 100755 index 0000000..73d8d56 --- /dev/null +++ b/docker/build_containers.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +# List of services to build + +MINIMUM_REPOS=( + "oc-auth" + "oc-catalog" + "oc-datacenter" + "oc-peer" + "oc-scheduler" + "oc-schedulerd" + "oc-workflow" + "oc-workspace" +) + +EXTRA_REPOS=( + "oc-front" + "oc-shared" + "oc-monitord" +) + +REPOS=("${MINIMUM_REPOS[@]}") # Start with minimum repos + +OC_ROOT="$(realpath ../..)" +DOCKER_BUILD="$(pwd)" +LOG_DIR="$DOCKER_BUILD/build_logs" +mkdir -p "$LOG_DIR" + +cd "$OC_ROOT" || exit 1 + +# Function to build a service +build_service() { + local service=$1 + local logfile="$LOG_DIR/$service.log" + + echo "[START] Building $service..." + + docker build -t "$service" "$OC_ROOT/$service" > "$logfile" 2>&1 & + echo $! # Return PID +} + +# Track running builds +declare -A pids +declare -a active_services=() + + +# Select services to build +echo "🔧 Optional extra services:" +for i in "${!EXTRA_REPOS[@]}"; do + echo " [$((i+1))] ${EXTRA_REPOS[$i]}" +done + +read -p "🟡 Do you want to add any extra services? Enter numbers separated by space (e.g., 1 3), or press Enter to skip: " -a selected + +for index in "${selected[@]}"; do + if [[ "$index" =~ ^[0-9]+$ ]] && (( index >= 1 && index <= ${#EXTRA_REPOS[@]} )); then + REPOS+=("${EXTRA_REPOS[$((index-1))]}") + else + echo "⚠️ Invalid selection: $index" + fi +done + +echo "✅ Selected services:" +for repo in "${REPOS[@]}"; do + echo " - $repo" +done + +# Launch builds +for service in "${REPOS[@]}"; do + IMAGE_NAME="$service" + + # Check if the image exists locally + if docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then + read -p "🟡 Image '$IMAGE_NAME' already exists. Rebuild? (y/N): " rebuild + if [[ "$rebuild" =~ ^[Yy]$ ]]; then + echo "🔄 Rebuilding image for '$IMAGE_NAME'..." + else + echo "⏭️ Skipping build for '$IMAGE_NAME'." + continue + fi + fi + + # Check if a container is already running from this image + if docker ps --filter "ancestor=$IMAGE_NAME" --format '{{.ID}}' | grep -q .; then + echo "✅ A container from image '$IMAGE_NAME' is already running. Skipping build." + else + SERVICE_PATH="$OC_ROOT/$service" + if [ -d "$SERVICE_PATH" ]; then + build_service "$service" & + pids["$service"]=$! + active_services+=("$service") + else + echo "⚠️ Directory not found for $service. Skipping." + fi + fi +done + + +echo "========================" +echo "Building: ${active_services[*]}" +echo "========================" + +# Monitor logs for each build in parallel +for service in "${active_services[@]}"; do + logfile="$LOG_DIR/$service.log" + + ( + tail -n 0 -f "$logfile" | while IFS= read -r line; do + # Highlight docker build steps + if [[ "$line" =~ Step\ ([0-9]+/[0-9]+) ]]; then + echo -e "[$service] 🚧 ${BASH_REMATCH[0]}: $line" + else + echo "[$service] $line" + fi + done + ) & +done + +# Wait for all builds to complete +for pid in "${pids[@]}"; do + wait "$pid" +done + +for service in "${active_services[@]}"; do + cd $OC_ROOT/service + docker compose up -d +done + +echo "✅ All builds completed." diff --git a/download_oc.py b/download_oc.py new file mode 100755 index 0000000..ceccdd9 --- /dev/null +++ b/download_oc.py @@ -0,0 +1,78 @@ +#/bin/python3 + +import requests +from bs4 import BeautifulSoup +import inquirer +import os +import subprocess +from concurrent.futures import ThreadPoolExecutor, as_completed +from rich.console import Console +from rich.table import Table +from rich.live import Live + +# URLs des pages à analyser +urls = [ + 'https://cloud.o-forge.io/explore/repos?page=1', + 'https://cloud.o-forge.io/explore/repos?page=2' +] + +def get_all_repo(urls): + repositories = [] + + for url in urls: + response = requests.get(url) + response.raise_for_status() # Vérifie si la requête a réussi + + soup = BeautifulSoup(response.text, 'html.parser') + + titles = soup.find_all(class_='flex-item-title') + + for title in titles: + repo_name = title.get_text(strip=True) + if repo_name.startswith('core/'): + repositories.append(repo_name.split("core/")[1]) + return repositories + +def git_clone_repo(repo: str, dir: str, status: dict): + status[repo] = "⏳ Cloning..." + + try: + if os.path.exists(f"{dir}/{repo}") : + subprocess.run(["git", "-C", f"{dir}/{repo}","pull"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + else: + base_url = f"https://cloud.o-forge.io/core/{repo}.git" + subprocess.run(["git", "clone", base_url, f"{dir}/{repo}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + status[repo] = "✅ Done" + except Exception as e: + status[repo] = f"❌ Failed: {e}" + +def display_status(status): + table = Table(title="Repository Cloning Status") + table.add_column("Repository", justify="left") + table.add_column("Status", justify="right") + + for repo, state in status.items(): + table.add_row(repo, state) + + return table + +repositories = get_all_repo(urls) +cwd = os.getcwd() + +questions = [ + inquirer.Checkbox('repo_choice', + message=f"Which Open Cloud repo do you want to download ? (Will be download in {cwd})?", + choices=repositories, + ), +] + +selected_repo = inquirer.prompt(questions) +status = {repo: "Waiting" for repo in selected_repo["repo_choice"]} + +with ThreadPoolExecutor() as executor: + futures = {executor.submit(git_clone_repo, repo, cwd, status): repo for repo in selected_repo["repo_choice"]} + + with Live(display_status(status), refresh_per_second=2) as live: + for future in as_completed(futures): + live.update(display_status(status))