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
|
||||
from os import system
|
||||
from time import sleep
|
||||
from typing import Any, Dict
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
import uuid
|
||||
from wsgiref.util import request_uri
|
||||
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):
|
||||
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.post("/deploy/{service}")
|
||||
async def deploy(service: str, args: DeployArgs):
|
||||
finished = False
|
||||
lines = []
|
||||
|
||||
def finish_callback(_):
|
||||
nonlocal finished
|
||||
finished = True
|
||||
@app.get("/deployment/{id}/logs")
|
||||
async def deployment_logs(id: str):
|
||||
if id not in deployments:
|
||||
raise HTTPException(status_code=404, detail="Deployment was not found")
|
||||
|
||||
def event_callback(data: Dict):
|
||||
if 'stdout' in data:
|
||||
lines.append(data['stdout'])
|
||||
runner = deployments.get(id).runner
|
||||
|
||||
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)
|
||||
|
||||
ansible_runner.run_async(
|
||||
playbook='deploy.yaml',
|
||||
extravars={
|
||||
'services': [service],
|
||||
**args.extra_vars
|
||||
},
|
||||
private_data_dir='/home/api-server',
|
||||
project_dir='/var/project',
|
||||
inventory='inventory/m2.ini',
|
||||
event_handler=event_callback,
|
||||
finished_callback=finish_callback,
|
||||
settings={
|
||||
'suppress_ansible_output': True
|
||||
}
|
||||
return StreamingResponse(stream(), media_type="text/plain")
|
||||
|
||||
|
||||
@app.get("/deployment")
|
||||
async def deployment_list(request: Request):
|
||||
return [
|
||||
DeploymentDTO.from_deployment(deployment, request)
|
||||
for deployment in deployments.values()
|
||||
]
|
||||
|
||||
|
||||
@app.get("/deployment/{id}")
|
||||
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