api-server: Separate /logs endpoint
This commit is contained in:
parent
fc94212d97
commit
eca2cdddb1
133
api/api.py
133
api/api.py
@ -1,53 +1,110 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from os import system
|
import uuid
|
||||||
from time import sleep
|
from wsgiref.util import request_uri
|
||||||
from typing import Any, Dict
|
|
||||||
from fastapi import FastAPI
|
|
||||||
from fastapi.responses import StreamingResponse
|
|
||||||
from pydantic import BaseModel
|
|
||||||
import ansible_runner
|
import ansible_runner
|
||||||
|
import os
|
||||||
|
from typing import Any, Dict
|
||||||
|
from fastapi import FastAPI, HTTPException, Request
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from ansible_runner import Runner
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
class Link(BaseModel):
|
||||||
|
href: str
|
||||||
|
|
||||||
|
|
||||||
class DeployArgs(BaseModel):
|
class DeployArgs(BaseModel):
|
||||||
extra_vars: Dict[str, Any]
|
service: str
|
||||||
|
inventory: str | None = None
|
||||||
|
vars: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
|
||||||
|
class DeploymentLinks(BaseModel):
|
||||||
|
logs: Link
|
||||||
|
self: Link
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Deployment:
|
||||||
|
id: str
|
||||||
|
runner: Runner | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class DeploymentDTO(BaseModel):
|
||||||
|
id: str
|
||||||
|
status: str
|
||||||
|
links: DeploymentLinks = Field(..., alias="_links")
|
||||||
|
|
||||||
|
def from_deployment(deployment: Deployment, request: Request):
|
||||||
|
return DeploymentDTO(
|
||||||
|
id=deployment.id,
|
||||||
|
status=deployment.runner.status,
|
||||||
|
_links={
|
||||||
|
"self": {"href": request.url_for("deployment", id=deployment.id)},
|
||||||
|
"logs": {"href": request.url_for("deployment_logs", id=deployment.id)},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
deployments: Dict[str, Deployment] = {}
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
@app.post("/deploy/{service}")
|
|
||||||
async def deploy(service: str, args: DeployArgs):
|
|
||||||
finished = False
|
|
||||||
lines = []
|
|
||||||
|
|
||||||
def finish_callback(_):
|
@app.get("/deployment/{id}/logs")
|
||||||
nonlocal finished
|
async def deployment_logs(id: str):
|
||||||
finished = True
|
if id not in deployments:
|
||||||
|
raise HTTPException(status_code=404, detail="Deployment was not found")
|
||||||
|
|
||||||
def event_callback(data: Dict):
|
runner = deployments.get(id).runner
|
||||||
if 'stdout' in data:
|
|
||||||
lines.append(data['stdout'])
|
async def stream():
|
||||||
|
stdout = runner.stdout
|
||||||
|
|
||||||
|
while True:
|
||||||
|
while line := stdout.readline():
|
||||||
|
if line == "":
|
||||||
|
break
|
||||||
|
yield line
|
||||||
|
|
||||||
|
if runner.status != "running":
|
||||||
|
break
|
||||||
|
|
||||||
async def logs():
|
|
||||||
nonlocal lines
|
|
||||||
while not finished:
|
|
||||||
for line in lines:
|
|
||||||
yield line.rstrip() + "\n"
|
|
||||||
lines = []
|
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
ansible_runner.run_async(
|
return StreamingResponse(stream(), media_type="text/plain")
|
||||||
playbook='deploy.yaml',
|
|
||||||
extravars={
|
|
||||||
'services': [service],
|
@app.get("/deployment")
|
||||||
**args.extra_vars
|
async def deployment_list(request: Request):
|
||||||
},
|
return [
|
||||||
private_data_dir='/home/api-server',
|
DeploymentDTO.from_deployment(deployment, request)
|
||||||
project_dir='/var/project',
|
for deployment in deployments.values()
|
||||||
inventory='inventory/m2.ini',
|
]
|
||||||
event_handler=event_callback,
|
|
||||||
finished_callback=finish_callback,
|
|
||||||
settings={
|
@app.get("/deployment/{id}")
|
||||||
'suppress_ansible_output': True
|
async def deployment(id: str, request: Request):
|
||||||
}
|
return DeploymentDTO.from_deployment(deployments[id], request)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/deployment")
|
||||||
|
async def deploy(args: DeployArgs, request: Request):
|
||||||
|
ident = str(uuid.uuid4())
|
||||||
|
|
||||||
|
_, runner = ansible_runner.run_async(
|
||||||
|
playbook="deploy.yaml",
|
||||||
|
ident=ident,
|
||||||
|
extravars={"services": [args.service], **args.vars},
|
||||||
|
private_data_dir="/home/api-server",
|
||||||
|
project_dir=os.environ.get("API_PROJECT_DIR", "/var/project"),
|
||||||
|
inventory=args.inventory or os.environ.get("API_INVENTORY"),
|
||||||
|
settings={"suppress_ansible_output": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
return StreamingResponse(logs(), media_type='text/plain')
|
deployment = Deployment(ident, runner)
|
||||||
|
deployments[ident] = deployment
|
||||||
|
|
||||||
|
return DeploymentDTO.from_deployment(deployment, request)
|
||||||
|
Loading…
Reference in New Issue
Block a user