From c18187f40715b2b5462e8f32e4f119b112b4394f Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 16 Aug 2025 07:37:53 +0200 Subject: [PATCH] fixes --- alpine/config | 2 +- alpine/hosts | 9 -- alpine/known_hosts | 4 + app/sbin/api | 193 +++++++++++++++++++++---------- app/sbin/startContainer | 57 ++++++++-- app/test/api.json | 233 ++++++++++++++++++++++++++++++++++++++ app/test/create_container | 8 -- app/test/runner.sh | 53 +++++++++ app/test/start_container | 5 - app/test/test.sh | 141 +++++++++++++++++++++++ docker-compose.yaml | 2 + 11 files changed, 615 insertions(+), 92 deletions(-) delete mode 100644 alpine/hosts create mode 100644 app/test/api.json delete mode 100644 app/test/create_container create mode 100644 app/test/runner.sh delete mode 100644 app/test/start_container create mode 100644 app/test/test.sh diff --git a/alpine/config b/alpine/config index 1b40fe8..459352f 100644 --- a/alpine/config +++ b/alpine/config @@ -1,5 +1,5 @@ Host dev - Hostname 192.168.111.209 + Hostname dev User oliver Port 2222 diff --git a/alpine/hosts b/alpine/hosts deleted file mode 100644 index 24b66d5..0000000 --- a/alpine/hosts +++ /dev/null @@ -1,9 +0,0 @@ -127.0.0.1 dev -::1 dev - -192.168.111.209 dev n8n - -192.168.9.11 saopaulo -192.168.9.17 mumbai -192.168.9.15 london -192.168.9.16 boston diff --git a/alpine/known_hosts b/alpine/known_hosts index 824678d..6ac134a 100644 --- a/alpine/known_hosts +++ b/alpine/known_hosts @@ -11,3 +11,7 @@ 192.168.9.16 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCLzyfVrbCNJW9tNTkxMy8IDLGyQoqkxlGN+TirLoNyw5GD1XpJgvHIBy//6tDEIuAfL5ouqp0T+PM00nSj6gEjGeTQtjrzAeSXJ5D3Q3+Vl92j1uSPPhnChRnidI2mrjvNuaukaDxBMwIvr9sagTvtZwCupKbuYeW0kQqCwnSIjTS7OwcrCRm3RtzC8bOPpOIHYaLxV6OnyoRXKLv9Lu987nuNoTcZu61jyCHhFTzzN3Y3KRpioshUoHiukgJbJQVnRuVkxQRknWa5gS4ATLLUG70taTs0Ld4Canyrym4aVV9pw5rDwH0rkidvkz8OLX049mIeGhv/QPRH8x5bgtPezKkiOcPW22HG0+z+7zHgbkTKlcvGr1JdhkTN6jqL4HyYdhMj04v7miv8LnxjaBNb3iNFQiAs5aV5LmPevYP+/94TGBIlTVCh5oTyeDJ1OKmZKL9ZUUfu0Ozz+v3O3en4hNMOI8P+uujfIxfvn/Pasxh4r7pTGYQV9nseCmrsl0k= 192.168.9.16 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLtNCuNwiidC4IdZci3J0s/vbcTtFZod47BKd8GKyGrucOS402fuZ03F1elrEKjA0PcFNZQR7MbEvF6zDjEfoPY= +[dev]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILf8g96UGmWCLf5C5qfGgir3ned83s5HXNpM231A0rUu +[dev]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC45Aw7jQXh3/QOMOCkM4wvz4/yXxpjQU5C2ZMJ/76lTPB/DsvP9C+DGKqO3zzznOhdaYzTb5lipMguWRyH0RYlwYtR7TnYKTZgioXC/+kSdVzgAUeqcUCUIoNH5CSp/lgsAp1XxQTPk3ooZJc57cTnHo78Y+oOj7bF8u8Nin/q+uTTrgXVkbMCZYSX+CA/fHtmatH9vMdAbPv/Bx9M72vijMMZWOP/6hwRhn3C0s2o1rgz/nXe5u3HYIodwzItqdceF3k6hLuZP8Bb+kKz42wXstke5SXivnUm3uDdkLNKjKWnSWoD7TAIA9w6MuFbLBLL6QRBmfcx74F6aeuV55ItzRPVAz0XEqQU55r3nS9r7zLyT0HGvu4Wt+Zf97nHKfWucaf/5UFWoTC9pijYEYbNyhYockyzM+QAx3/n6cfwIjlc/wFkU8nd7fUjfkymTlIX6UiT/12b+TrFuOLBk4mylDGkPge7zfslvH/LOyokzCRsz75Sh3bc7bxB1ppPrjc= +[dev]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK+WCqjyl6CKaDnMAcfqGYty8KoWqh95T0lQK3RFcNfHom4rsZljS2tPlicfRSHT/2zWH7HfQ9T2t/2PYjWjxrc= + diff --git a/app/sbin/api b/app/sbin/api index 23208d8..983e75c 100755 --- a/app/sbin/api +++ b/app/sbin/api @@ -1,5 +1,4 @@ #!/usr/bin/env python3 - from fastapi import FastAPI, HTTPException, Depends, Response from fastapi.security.api_key import APIKeyHeader from fastapi.responses import RedirectResponse @@ -8,9 +7,9 @@ import psutil import sqlite3 import subprocess import os -import sys import uvicorn from typing import Optional +from datetime import datetime # Constants DB_PATH = "/4server/data/contracts.db" @@ -20,8 +19,6 @@ VERSION = "API: 0.0.5" # FastAPI app app = FastAPI() - -# Security api_key_header = APIKeyHeader(name="X-API-Key") @@ -29,7 +26,16 @@ def verify_api_key(key: str = Depends(api_key_header)): if key != API_KEY: raise HTTPException(status_code=403, detail="Unauthorized") -# ---------------------- Database ---------------------- + +# ---------------------- Helpers ---------------------- +def run_command(cmd: list[str]) -> str: + """Run a shell command and return stdout or raise HTTPException on error.""" + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + raise HTTPException(status_code=500, detail=result.stderr.strip() or "Unknown error") + return result.stdout.strip() + + def init_db(): """Initialize the database with containers table.""" os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) @@ -43,12 +49,12 @@ def init_db(): expires DATE, tags TEXT, env TEXT, - affiliate char(30), - image char(50), - history text, - comment text, - domains text, - status char (20). + affiliate CHAR(30), + image CHAR(50), + history TEXT, + comment TEXT, + domains TEXT, + status CHAR(20), created DATE, bump DATE ) @@ -57,6 +63,16 @@ def init_db(): conn.close() +def execute_db(query: str, params: tuple = (), fetch: bool = False): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute(query, params) + conn.commit() + data = cursor.fetchall() if fetch else None + conn.close() + return data + + # ---------------------- Models ---------------------- class ContainerModel(BaseModel): UUID: str @@ -65,95 +81,150 @@ class ContainerModel(BaseModel): tags: Optional[str] = None env: Optional[str] = None affiliate: Optional[str] = None - image: Optional[str} = None + image: Optional[str] = None history: Optional[str] = None comment: Optional[str] = None - domains:Optional[str] = None + domains: Optional[str] = None status: str created: str + bump: Optional[str] = None -class StartContainerRequest(BaseModel): - uuid: str - email: str +class ContainerIDRequest(BaseModel): + container_id: str -# ---------------------- CONTAINER Routes ---------------------- +class UpdateContainerRequest(ContainerModel): + pass + + +class InfoContainerRequest(BaseModel): + container_id: Optional[str] = None + + +# ---------------------- Routes ---------------------- @app.get("/", include_in_schema=False) def redirect_to_odoo(): return RedirectResponse(url="https://ODOO4PROJECTS.com") + @app.post("/container/update", dependencies=[Depends(verify_api_key)]) def update_container(request: UpdateContainerRequest): ---> Insert the new container into the database. Create, if container does not exist + existing = execute_db("SELECT * FROM containers WHERE UUID = ?", (request.UUID,), fetch=True) + if existing: + execute_db(""" + UPDATE containers SET email=?, expires=?, tags=?, env=?, affiliate=?, image=?, + history=?, comment=?, domains=?, status=?, created=?, bump=? + WHERE UUID=? + """, ( + request.email, request.expires, request.tags, request.env, request.affiliate, + request.image, request.history, request.comment, request.domains, request.status, + request.created, request.bump, request.UUID + )) + else: + execute_db(""" + INSERT INTO containers (UUID, email, expires, tags, env, affiliate, image, history, + comment, domains, status, created, bump) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, ( + request.UUID, request.email, request.expires, request.tags, request.env, + request.affiliate, request.image, request.history, request.comment, + request.domains, request.status, request.created, request.bump + )) + return {"message": "Container updated or created"} + @app.post("/container/start", dependencies=[Depends(verify_api_key)]) -def start_container(request: StartContainerRequest): ---> refactor, so that only the container is is given in the request. the shell script BIN_PATH/startContainer is called withtge containerid as parameter +def start_container(request: ContainerIDRequest): + return {"message": run_command([f"{BIN_PATH}/startContainer", request.container_id])} + @app.post("/container/stop", dependencies=[Depends(verify_api_key)]) -def stop_container(request: StopContainerRequest): ---> refactor, so that only the container is is given in the request. the shell script BIN_PATH/stopContainer is called withtge containerid as parameter +def stop_container(request: ContainerIDRequest): + return {"message": run_command([f"{BIN_PATH}/stopContainer", request.container_id])} + @app.post("/container/nuke", dependencies=[Depends(verify_api_key)]) -def nuke_container(request: StopContainerRequest): ---> refactor, so that only the container is is given in the request. -when the status of the database is "nuke" then -the shell script BIN_PATH/nukeContainer is called withtge containerid as parameter +def nuke_container(request: ContainerIDRequest): + status = execute_db("SELECT status FROM containers WHERE UUID=?", (request.container_id,), fetch=True) + if not status or status[0][0] != "nuke": + raise HTTPException(400, "Container status is not 'nuke'") + return {"message": run_command([f"{BIN_PATH}/nukeContainer", request.container_id])} + @app.post("/container/info", dependencies=[Depends(verify_api_key)]) def info_container(request: InfoContainerRequest): - --> refactor: When no container id is given, query all containers from the database. when a containeris is given, just select that one. Return the result - ---> create an endpoint that changes the container image to the latest version of that image /container/bump besides updating the container image, also update the SQL for this container and put the current date into "bump" - ---> add /container/quota return the disk and ram usage of this container. I think you can obtain this from docker + if request.container_id: + rows = execute_db("SELECT * FROM containers WHERE UUID=?", (request.container_id,), fetch=True) + else: + rows = execute_db("SELECT * FROM containers", fetch=True) + return {"containers": rows} - -# ------------------------ SYSTEM Routes +@app.post("/container/bump", dependencies=[Depends(verify_api_key)]) +def bump_container(request: ContainerIDRequest): + today = datetime.utcnow().strftime("%Y-%m-%d") + execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.container_id)) + msg = run_command([f"{BIN_PATH}/bumpContainer", request.container_id]) + return {"message": msg, "bump_date": today} +@app.post("/container/quota", dependencies=[Depends(verify_api_key)]) +def container_quota(request: ContainerIDRequest): + output = run_command([ + "docker", "stats", request.container_id, "--no-stream", + "--format", "{{.MemUsage}},{{.BlockIO}}" + ]) + mem_usage, disk_usage = output.split(",") + return {"memory_usage": mem_usage, "disk_io": disk_usage} + + +# ---------------------- SYSTEM ---------------------- @app.get("/system/containers", dependencies=[Depends(verify_api_key)]) def get_containers(): - result = subprocess.run([BIN_PATH+'/getContainers'], capture_output=True, text=True) - if result.returncode != 0: - return Response(content='{"error": "Script failed"}', media_type="application/json", status_code=500) - return Response(content=result.stdout, media_type="application/json") + return Response(content=run_command([f"{BIN_PATH}/getContainers"]), media_type="application/json") + + +@app.get("/system/images", dependencies=[Depends(verify_api_key)]) +def list_images(): + images = run_command(["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"]) + return {"images": images.split("\n")} ---> create an endpoint that lkists all docker images available on the system /system/images @app.get("/system/info", dependencies=[Depends(verify_api_key)]) def get_system_info(): -return all INFOas JSON -in this function add last_update and return the content of /4server/data/update -if this file does not exist return NONE -also add the latest bump date of all images in the database -return the VERSION as well. try: - with open("/etc/alpine-release") as f: - version = f.read().strip() - return {"alpine_version": version} - except FileNotFoundError: - raise HTTPException(status_code=404, detail="File not found. Press play on tape") + alpine_version = None + last_update = None + bump_dates = execute_db("SELECT MAX(bump) FROM containers", fetch=True)[0][0] + if os.path.exists("/4server/data/update"): + with open("/4server/data/update") as f: + last_update = f.read().strip() + if os.path.exists("/etc/alpine-release"): + with open("/etc/alpine-release") as f: + alpine_version = f.read().strip() + mem = psutil.virtual_memory() + disk = psutil.disk_usage("/") + cpu_count = psutil.cpu_count(logical=True) + return { + "alpine_version": alpine_version, + "last_update": last_update, + "latest_bump": bump_dates, + "version": VERSION, + "resources": { + "memory": {"total": mem.total, "available": mem.available, "used": mem.used}, + "disk": {"total": disk.total, "used": disk.used, "free": disk.free}, + "cpu_count": cpu_count + } + } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) -@app.get("/system/resources", dependencies=[Depends(verify_api_key)]) -def get_resources(): +@app.post("/system/pull", dependencies=[Depends(verify_api_key)]) +def pull_all_images(): + return {"message": run_command([f"{BIN_PATH}/pullAllContainers"])} ---> consolidate this API into /system/info - - mem = psutil.virtual_memory() - disk = psutil.disk_usage("/") - return { - "memory": {"total": mem.total, "available": mem.available, "used": mem.used}, - "disk": {"total": disk.total, "used": disk.used, "free": disk.free}, - "cpu_count": psutil.cpu_count(logical=True), - } - ---> create an endpoint that docker pulls all containers /system/pull # ---------------------- Entry Point ---------------------- if __name__ == "__main__": diff --git a/app/sbin/startContainer b/app/sbin/startContainer index abaa384..4d57560 100644 --- a/app/sbin/startContainer +++ b/app/sbin/startContainer @@ -1,14 +1,55 @@ -this script gets a container UUID +#!/bin/bash +# Usage: ./start_by_uuid.sh +# Example: ./start_by_uuid.sh abc-001-xxxx-xxxx-xxxx -Wtrite a bash script, that gets all info out off the Sqlite3 Database usind the UUID and stores the values in environment variables +DB_PATH="/4server/data/contracts.db" +BIN_PATH="/4server/sbin" -setr the BIN_BATH to /4srver/sbin/ +UUID="$1" -the uuid looks like xxx-xxx-xxxx-xxxx-xxxx-.... +if [[ -z "$UUID" ]]; then + echo "Usage: $0 " + exit 1 +fi -get the second number +# Fetch all info from SQLite and export as environment variables +eval $(sqlite3 -separator "=" "$DB_PATH" \ + "SELECT 'UUID=' || UUID, + 'EMAIL=' || email, + 'EXPIRES=' || expires, + 'TAGS=' || tags, + 'ENV=' || env, + 'AFFILIATE=' || affiliate, + 'IMAGE=' || image, + 'HISTORY=' || history, + 'COMMENT=' || comment, + 'DOMAINS=' || domains, + 'STATUS=' || status, + 'CREATED=' || created, + 'BUMP=' || bump + FROM containers WHERE UUID='$UUID';" | sed 's/ /\\ /g') + +# Debug: print loaded environment variables +# env | grep -E 'UUID|EMAIL|EXPIRES|TAGS|ENV|AFFILIATE|IMAGE|HISTORY|COMMENT|DOMAINS|STATUS|CREATED|BUMP' + +# Extract the second part of UUID (split by "-") +SECOND_PART=$(echo "$UUID" | cut -d'-' -f2) + +# Decide which script to run +case "$SECOND_PART" in + 001) + "$BIN_PATH/start/n8n" + ;; + 002) + "$BIN_PATH/start/ODOO18" + ;; + 003) + "$BIN_PATH/start/ODOO19" + ;; + *) + echo "Unknown UUID type: $SECOND_PART" + exit 2 + ;; +esac -When the number is 001 call the script BIN_PATH/start/n8n -When the number is 002 call the sctipr BIN_ÜATH/start/ODOO18 -WHen the number is 003 call the script BIN_PATH/start/ODOO19 diff --git a/app/test/api.json b/app/test/api.json new file mode 100644 index 0000000..f6c5f78 --- /dev/null +++ b/app/test/api.json @@ -0,0 +1,233 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Container Management API", + "version": "0.0.5", + "description": "API for managing containers and system resources." + }, + "servers": [ + { "url": "http://localhost:8888" } + ], + "components": { + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key" + } + }, + "schemas": { + "ContainerModel": { + "type": "object", + "properties": { + "UUID": { "type": "string" }, + "email": { "type": "string" }, + "expires": { "type": "string", "format": "date" }, + "tags": { "type": "string" }, + "env": { "type": "string" }, + "affiliate": { "type": "string" }, + "image": { "type": "string" }, + "history": { "type": "string" }, + "comment": { "type": "string" }, + "domains": { "type": "string" }, + "status": { "type": "string" }, + "created": { "type": "string", "format": "date" }, + "bump": { "type": "string", "format": "date" } + }, + "required": ["UUID", "email", "expires", "status", "created"] + }, + "ContainerIDRequest": { + "type": "object", + "properties": { + "container_id": { "type": "string" } + }, + "required": ["container_id"] + }, + "InfoContainerRequest": { + "type": "object", + "properties": { + "container_id": { "type": "string" } + } + } + } + }, + "security": [{ "ApiKeyAuth": [] }], + "paths": { + "/": { + "get": { + "summary": "Root redirect", + "responses": { + "302": { "description": "Redirect to ODOO4PROJECTS.com" } + } + } + }, + "/container/update": { + "post": { + "summary": "Create or update container", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ContainerModel" } + } + } + }, + "responses": { + "200": { + "description": "Container updated or created", + "content": { "application/json": { "example": { "message": "Container updated or created" } } } + } + } + } + }, + "/container/start": { + "post": { + "summary": "Start a container", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContainerIDRequest" } } } + }, + "responses": { "200": { "description": "Container started" } } + } + }, + "/container/stop": { + "post": { + "summary": "Stop a container", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContainerIDRequest" } } } + }, + "responses": { "200": { "description": "Container stopped" } } + } + }, + "/container/nuke": { + "post": { + "summary": "Nuke a container", + "description": "Permanently deletes container if its status is 'nuke'.", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContainerIDRequest" } } } + }, + "responses": { + "200": { "description": "Container nuked" }, + "400": { "description": "Container status is not 'nuke'" } + } + } + }, + "/container/info": { + "post": { + "summary": "Get container info", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InfoContainerRequest" } } } + }, + "responses": { "200": { "description": "Container info list" } } + } + }, + "/container/bump": { + "post": { + "summary": "Bump a container", + "description": "Updates bump date and runs bump script.", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContainerIDRequest" } } } + }, + "responses": { + "200": { "description": "Bump successful", "content": { "application/json": {} } } + } + } + }, + "/container/quota": { + "post": { + "summary": "Get container quota", + "security": [{ "ApiKeyAuth": [] }], + "requestBody": { + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContainerIDRequest" } } } + }, + "responses": { + "200": { + "description": "Quota usage", + "content": { + "application/json": { + "example": { + "memory_usage": "50MiB / 2GiB", + "disk_io": "10MB / 200MB" + } + } + } + } + } + } + }, + "/system/containers": { + "get": { + "summary": "List running containers", + "security": [{ "ApiKeyAuth": [] }], + "responses": { + "200": { + "description": "Containers in system", + "content": { "application/json": {} } + } + } + } + }, + "/system/images": { + "get": { + "summary": "List system images", + "security": [{ "ApiKeyAuth": [] }], + "responses": { + "200": { + "description": "List of docker images", + "content": { + "application/json": { + "example": { + "images": ["repo1:tag", "repo2:tag"] + } + } + } + } + } + } + }, + "/system/info": { + "get": { + "summary": "Get system info", + "security": [{ "ApiKeyAuth": [] }], + "responses": { + "200": { + "description": "System resource info", + "content": { + "application/json": { + "example": { + "alpine_version": "3.18.2", + "last_update": "2025-08-01", + "latest_bump": "2025-08-10", + "version": "API: 0.0.5", + "resources": { + "memory": { "total": 16777216, "available": 8388608, "used": 8388608 }, + "disk": { "total": 100000000, "used": 50000000, "free": 50000000 }, + "cpu_count": 8 + } + } + } + } + } + } + } + }, + "/system/pull": { + "post": { + "summary": "Pull all images", + "security": [{ "ApiKeyAuth": [] }], + "responses": { + "200": { + "description": "All images pulled", + "content": { "application/json": { "example": { "message": "All images pulled" } } } + } + } + } + } + } +} + diff --git a/app/test/create_container b/app/test/create_container deleted file mode 100644 index 9c3681f..0000000 --- a/app/test/create_container +++ /dev/null @@ -1,8 +0,0 @@ -write a curl request that creatrs a container with -a uuid starting with 000-001-.... -email o.arnold@projektbox.de -domain n8n.local -and the rest with sane info - - - diff --git a/app/test/runner.sh b/app/test/runner.sh new file mode 100644 index 0000000..22365c9 --- /dev/null +++ b/app/test/runner.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# run_all_tests.sh — simple runner for test_api.sh +# + +API_SCRIPT="./test_api.sh" + +# Make sure test_api.sh is executable +if [ ! -x "$API_SCRIPT" ]; then + echo "Error: $API_SCRIPT not found or not executable" + exit 1 +fi + +# List of tests to run (must match functions in test_api.sh) +TESTS=( + root_redirect + update_container + start_container + stop_container + nuke_container + info_all + info_one + bump_container + container_quota + system_containers + system_images + system_info + system_pull +) + +ok_count=0 +fail_count=0 + +echo "Running API tests..." +echo "====================" + +for test in "${TESTS[@]}"; do + # Run test, capture HTTP status code only + status=$($API_SCRIPT $test -s -o /dev/null -w "%{http_code}") + + if [[ "$status" == "200" || "$status" == "302" ]]; then + echo "[OK ] $test" + ((ok_count++)) + else + echo "[NOK] $test (HTTP $status)" + ((fail_count++)) + fi +done + +echo "====================" +echo "Summary: $ok_count OK, $fail_count NOK" +exit $fail_count + diff --git a/app/test/start_container b/app/test/start_container deleted file mode 100644 index 059ce0c..0000000 --- a/app/test/start_container +++ /dev/null @@ -1,5 +0,0 @@ -start the container - -000-001- - -add a random uuid diff --git a/app/test/test.sh b/app/test/test.sh new file mode 100644 index 0000000..8d00624 --- /dev/null +++ b/app/test/test.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# +# test_api.sh — cURL test suite for Container Management API +# + +# ========= CONFIG ========= +API_KEY="your-secret-api-key" +BASE_URL="http://localhost:8888" +CONTAINER_ID="123e4567-e89b-12d3-a456-426614174000" # sample UUID +# ========================== + +# --- Functions for each endpoint --- + +root_redirect() { + curl -i "$BASE_URL/" +} + +update_container() { + curl -X POST "$BASE_URL/container/update" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ + "UUID": "'"$CONTAINER_ID"'", + "email": "user@example.com", + "expires": "2025-12-31", + "status": "active", + "created": "2025-08-16" + }' +} + +start_container() { + curl -X POST "$BASE_URL/container/start" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ "container_id": "'"$CONTAINER_ID"'" }' +} + +stop_container() { + curl -X POST "$BASE_URL/container/stop" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ "container_id": "'"$CONTAINER_ID"'" }' +} + +nuke_container() { + curl -X POST "$BASE_URL/container/nuke" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ "container_id": "'"$CONTAINER_ID"'" }' +} + +info_all() { + curl -X POST "$BASE_URL/container/info" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{}' +} + +info_one() { + curl -X POST "$BASE_URL/container/info" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ "container_id": "'"$CONTAINER_ID"'" }' +} + +bump_container() { + curl -X POST "$BASE_URL/container/bump" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ "container_id": "'"$CONTAINER_ID"'" }' +} + +container_quota() { + curl -X POST "$BASE_URL/container/quota" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $API_KEY" \ + -d '{ "container_id": "'"$CONTAINER_ID"'" }' +} + +system_containers() { + curl -X GET "$BASE_URL/system/containers" \ + -H "X-API-Key: $API_KEY" +} + +system_images() { + curl -X GET "$BASE_URL/system/images" \ + -H "X-API-Key: $API_KEY" +} + +system_info() { + curl -X GET "$BASE_URL/system/info" \ + -H "X-API-Key: $API_KEY" +} + +system_pull() { + curl -X POST "$BASE_URL/system/pull" \ + -H "X-API-Key: $API_KEY" +} + +# --- Help message --- +show_help() { + cat < + +Available commands: + root_redirect - Test root redirect + update_container - Create or update container + start_container - Start container + stop_container - Stop container + nuke_container - Nuke container (status must be "nuke") + info_all - Get info for all containers + info_one - Get info for one container + bump_container - Bump container + container_quota - Show container quota + system_containers - List running containers + system_images - List docker images + system_info - Show system information + system_pull - Pull all container images + +Examples: + $0 update_container + $0 system_info +EOF +} + +# --- Main --- +if [ $# -eq 0 ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then + show_help + exit 0 +fi + +cmd="$1" +shift +if declare -f "$cmd" >/dev/null 2>&1; then + "$cmd" "$@" +else + echo "Unknown command: $cmd" + show_help + exit 1 +fi + diff --git a/docker-compose.yaml b/docker-compose.yaml index 49602b0..2ab41c0 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,4 +6,6 @@ services: - ./app:/app tty: true privileged: true + extra_hosts: + - "dev:192.168.9.221"