From 29b59915e2713fc2ee8fe488d84db094c2221078 Mon Sep 17 00:00:00 2001 From: N Space <1257310+nourspace@users.noreply.github.com> Date: Mon, 21 Nov 2022 02:14:47 +0100 Subject: [PATCH] wip: dynamic operations --- remote_operator/README.md | 8 +++ remote_operator/main.py | 95 +++++++++++++++++++++++++++------ remote_operator/operations.json | 60 +++++++++++++++++++++ 3 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 remote_operator/operations.json diff --git a/remote_operator/README.md b/remote_operator/README.md index 393a249..e048fa3 100644 --- a/remote_operator/README.md +++ b/remote_operator/README.md @@ -58,3 +58,11 @@ Allows 0L node operators to perform critical operations remotely. - [ ] Add basic auth - [ ] Publish initial version with restart feature - [ ] Implement logging and backups + +## Dynamic Operations + +Using operations.json node owners can define arbitrary allowed operations that suit their needs without having to update +the service code. + +The main reason behind this is that not everyone is running the docker setup, and it would not be as useful to hardcode +docker-related operations only. diff --git a/remote_operator/main.py b/remote_operator/main.py index c76d52e..5836c02 100644 --- a/remote_operator/main.py +++ b/remote_operator/main.py @@ -1,6 +1,10 @@ +import json +from enum import Enum + from fastapi import FastAPI +from pydantic import BaseModel, create_model -app = FastAPI() +app = FastAPI(title="OL Remote Operator", version="0.0.1") @app.get("/") @@ -8,26 +12,85 @@ def root(): return {"Hello": "0L Operator!"} -@app.post("/restart") -def restart(): - return {"not": "implemented"} +class OperationType(str, Enum): + """ + Operation types + + cmd: run a command and return its output + patch: given a filename, update part of its content + + """ + CMD = 'cmd' + PATCH = 'patch' + + +class BaseOperation(BaseModel): + name: str + description: str + method: str + type: OperationType + checks: list[str] = [] + + +class CmdOperation(BaseOperation): + cmd: list[str] = None + filename: str = None + payload: str = None + + +class PatchOperation(BaseOperation): + cmd: list[str] = None + filename: str = None + payload: str = None + + +class BaseRequest(BaseModel): + reason: str + + +# Load operations schema +with open("operations.json") as f: + operations = json.load(f) + +for op_dict in operations: + op: BaseOperation = BaseOperation.parse_obj(op_dict) + + # Create request model + request_model_attrs = { + '__base__': BaseRequest, + } + # Create response model + if op.type == OperationType.CMD: + op: CmdOperation = CmdOperation.parse_obj(op) -@app.post("/cron/on") -def cron_on(): - return {"not": "implemented"} + elif op.type == OperationType.PATCH: + op: PatchOperation = PatchOperation.parse_obj(op) + # create payload model based on schema from op.payload + OperationRequestModel = create_model(f"Request{op.name.capitalize()}", **request_model_attrs) -@app.post("/cron/off") -def cron_off(): - return {"not": "implemented"} + # Create endpoint + def endpoint(data: OperationRequestModel): + if op.type == OperationType.CMD: + output = 1 + elif op.type == OperationType.PATCH: + output = 2 -@app.patch("/ol-toml") -def patch_ol_toml(): - return {"not": "implemented"} + return { + "operation": op.name, + "description": op.description, + "not": "implemented", + "output": output + } -@app.patch("/validator-yaml") -def patch_validator_yaml(): - return {"not": "implemented"} + # Add endpoint to api + app.add_api_route( + path=f"/{op.name}", + methods=[op.method], + endpoint=endpoint, + name=f"{op.name.capitalize()} Endpoint", + description=op.description + ) diff --git a/remote_operator/operations.json b/remote_operator/operations.json new file mode 100644 index 0000000..441b091 --- /dev/null +++ b/remote_operator/operations.json @@ -0,0 +1,60 @@ +[ + { + "name": "restart", + "description": "Restarts node", + "method": "post", + "type": "cmd", + "checks": [ + "echo checking..." + ], + "cmd": [ + "echo restarting..." + ] + }, + { + "name": "cron_on", + "description": "Setup cron", + "method": "post", + "type": "cmd", + "checks": [ + "echo checking..." + ], + "cmd": [ + "echo setting cron..." + ] + }, + { + "name": "cron_off", + "description": "Disable cron", + "method": "post", + "type": "cmd", + "checks": [ + "echo checking..." + ], + "cmd": [ + "echo disabling cron..." + ] + }, + { + "name": "patch_validator_yaml", + "description": "Patch validator.node.yaml", + "method": "patch", + "type": "patch", + "checks": [ + "echo checking..." + ], + "filename": "validator.node.yaml", + "payload": "str" + }, + { + "name": "patch_ol_toml", + "description": "Patch 0L.toml", + "method": "patch", + "type": "patch", + "checks": [ + "echo checking..." + ], + "filename": "0L.toml", + "payload": "str" + } +] \ No newline at end of file