diff --git a/.gitignore b/.gitignore index bce9047..d5ba8c1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ go.work go.work.sum tmp/ +__pycache__ \ No newline at end of file diff --git a/contribute/Makefile b/contribute/Makefile new file mode 100644 index 0000000..3ed6a9f --- /dev/null +++ b/contribute/Makefile @@ -0,0 +1,31 @@ +MIDAAS_IMAGE := api-midaas:v1 +WEBHOOK_IMAGE := webhook:v1 +ENV_MIDAAS_KEYNAME := test +ENV_MIDAAS_KEYVALUE := test +ENV_MIDAAS_ZONES := dev.local +CACHE_CONFIG := /tmp/cluster-name.txt +CLUSTER_NAME := go + +build-midaas: + docker build -t ${MIDAAS_IMAGE} midaas-ws/ + +push-midaas: + kind load docker-image ${MIDAAS_IMAGE} --name ${CLUSTER_NAME} + +delete-midaas: + @kubectl delete pod midaas --ignore-not-found + @kubectl delete svc midaas --ignore-not-found + +deploy-midaas: delete-midaas + @kubectl run --image ${MIDAAS_IMAGE} --expose=true --port 8080 \ + --env "MIDAAS_KEYNAME=${ENV_MIDAAS_KEYNAME}" \ + --env "MIDAAS_KEYVALUE=${ENV_MIDAAS_KEYVALUE}" \ + --env "MIDAAS_ZONES=${ENV_MIDAAS_ZONES}" midaas + @echo "\n\n-------------------" + @echo "Kubernetes midaas service is listening on port 8080" + @echo "Use 'TSIG_${ENV_MIDAAS_KEYNAME}=${ENV_MIDAAS_KEYVALUE}' on environment variable in webhook" + @echo "For manual actions, OpenAPI schema is available at path /docs" + + +start-midaas: build-midaas push-midaas deploy-midaas + diff --git a/contribute/midaas-ws/Dockerfile b/contribute/midaas-ws/Dockerfile new file mode 100644 index 0000000..a55e887 --- /dev/null +++ b/contribute/midaas-ws/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9 +WORKDIR /app +COPY ./requirements.txt . +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt +COPY main.py . +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080", "--reload"] + diff --git a/contribute/midaas-ws/main.py b/contribute/midaas-ws/main.py new file mode 100644 index 0000000..c6acf89 --- /dev/null +++ b/contribute/midaas-ws/main.py @@ -0,0 +1,114 @@ +import os +import pathlib +import json +from fastapi import FastAPI, Response, Request, Body +from pydantic import BaseModel + + +KEYNAME = os.environ.get("MIDAAS_KEYNAME", "test") +KEYVALUE = os.environ.get("MIDAAS_KEYVALUE", "test") +ZONES = os.environ.get("MIDAAS_ZONE", "dev.local,test.local") +ALL_ZONES = ZONES.split(",") + +INFO = f""" +Informations about current midaas instance: +- keyname: {KEYNAME} - (MIDAAS_KEYNAME env var) +- keyvalue: {KEYVALUE} - (MIDAAS_KEYVALUE env var) + +Availables zones are comma separated: {ZONES} (MIDAAS_ZONE env var) +""" +app = FastAPI() + + +class TTLDelete(BaseModel): + keyname: str + keyvalue: str + + +class TTLCreate(BaseModel): + ttl: int + keyname: str + keyvalue: str + + +def check_TSIG(keyname, keyvalue): + return keyname == KEYNAME and KEYVALUE == keyvalue + + +def create_zone(file): + print(f"Creating zone on {file}") + with open(file, "w+") as f: + json.dump({}, f) + + +def create_zone_if_not_exist(file): + p = pathlib.Path(file) + if not p.exists(): + create_zone(file) + else: + # check if zone is not empty + with open(file, "r") as f: + content = f.read() + if not content: + create_zone(file) + + +def read_zone(file): + with open(file, "r") as f: + data = json.loads(f.read()) + return data + + +@app.get("/ws/{domaine}") +async def list_domain(request: Request, domaine): + records = {} + for zone in ALL_ZONES: + if zone in domaine.strip().lower(): + file = f"/tmp/{zone}" + create_zone_if_not_exist(file) + records = read_zone(file) + return records + return records + + +@app.get("/healthz") +async def health(): + return {"status": "OK"} + + +@app.put("/ws/{domaine}/{type}/{valeur}") +def create(response: Response, request: Request, domaine: str, type: str, valeur: str, TTL: TTLCreate) -> dict: + if not check_TSIG(keyname=TTL.keyname, keyvalue=TTL.keyvalue): + return {"status": "ERROR", "message": "wrong credentials"} + + for zone in ALL_ZONES: + if zone in domaine: + file = f"/tmp/{zone}" + create_zone_if_not_exist(file=file) + data = read_zone(file=file) + with open(file, "w+") as f: + updated_data = data | {domaine: {"type": type, + "valeur": valeur, + "ttl": TTL.ttl}} + json.dump(updated_data, f) + return {"status": "OK"} + return {"status": "ERROR", "message": "zone not available"} + + +@app.delete("/ws/{domaine}/{type}/{valeur}") +def delete(response: Response, request: Request, domaine: str, type: str, valeur: str, TTL: TTLDelete) -> dict: + if not check_TSIG(keyname=TTL.keyname, keyvalue=TTL.keyvalue): + return {"status": "ERROR", "message": "wrong credentials"} + + for zone in ALL_ZONES: + if zone in domaine: + file = f"/tmp/{zone}" + create_zone_if_not_exist(file=file) + data = read_zone(file=file) + print(domaine, data) + with open(file, "w") as f: + if domaine in data: + data.pop(domaine) + json.dump(data, f) + return {"status": "OK"} + return {"status": "ERROR", "message": "no domain"} diff --git a/contribute/midaas-ws/requirements.txt b/contribute/midaas-ws/requirements.txt new file mode 100644 index 0000000..42612ea --- /dev/null +++ b/contribute/midaas-ws/requirements.txt @@ -0,0 +1,3 @@ +fastapi +requests +uvicorn diff --git a/midaas/midaas.go b/midaas/midaas.go index 523ce1c..479df2a 100644 --- a/midaas/midaas.go +++ b/midaas/midaas.go @@ -229,9 +229,9 @@ func RequestUrl(method string, url string, body Body) error { bodyResponse, err := io.ReadAll(response.Body) defer response.Body.Close() - if err != nil { - return err - } + if err != nil { + return err + } if response.StatusCode > 200 { return fmt.Errorf(string(bodyResponse)) }