current version seems to work
This commit is contained in:
parent
7553962f2b
commit
7f5e63e397
@ -1,21 +1,14 @@
|
||||
# load config so everything else can work
|
||||
from pillar_tool.schemas import HealthCheckError
|
||||
from pillar_tool.util import load_config, config
|
||||
from pillar_tool.util.validation import validate_and_split_path_and_domain_name
|
||||
|
||||
load_config()
|
||||
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
|
||||
from pillar_tool.db.base_model import as_dict
|
||||
from pillar_tool.middleware.logging import request_logging_middleware
|
||||
from pillar_tool.schemas import HostCreateParams
|
||||
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from pillar_tool.db.database import get_connection
|
||||
from pillar_tool.db.queries.auth_queries import create_user
|
||||
from pillar_tool.db.queries.host_queries import *
|
||||
|
||||
from pillar_tool.db.queries.auth_queries import create_user
|
||||
|
||||
from fastapi import FastAPI
|
||||
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||
@ -27,6 +20,8 @@ from pillar_tool.middleware.basicauth_backend import BasicAuthBackend
|
||||
from pillar_tool.middleware.db_connection import db_connection_middleware
|
||||
from pillar_tool.db.database import run_db_migrations
|
||||
|
||||
# import all the routers
|
||||
from pillar_tool.routers.host import router as host_router
|
||||
|
||||
# run any pending migrations
|
||||
run_db_migrations()
|
||||
@ -59,6 +54,7 @@ def on_db_error(request: Request, exc: Exception):
|
||||
|
||||
def on_general_error(request: Request, exc: Exception):
|
||||
print("wtf?")
|
||||
|
||||
response = PlainTextResponse(str(exc), status_code=500)
|
||||
return response
|
||||
|
||||
@ -68,17 +64,27 @@ app.add_middleware(BaseHTTPMiddleware, dispatch=db_connection_middleware)
|
||||
app.add_middleware(BaseHTTPMiddleware, dispatch=request_logging_middleware)
|
||||
app.exception_handler(Exception)(on_general_error)
|
||||
|
||||
# Setup the api router
|
||||
app.include_router(host_router)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Hello World"}
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
# TODO: improve health check
|
||||
return {"message": "Healthy"}
|
||||
# Check database connection
|
||||
try:
|
||||
db = get_connection()
|
||||
db.execute("SELECT 1")
|
||||
db.close()
|
||||
except Exception as e:
|
||||
return HealthCheckError(500, f"Database connection error:\n{e}").response()
|
||||
|
||||
return HealthCheckSuccess().response()
|
||||
|
||||
"""
|
||||
@app.get("/pillar/{host}")
|
||||
async def pillar_get(req: Request, host: str):
|
||||
print(req.headers)
|
||||
@ -96,19 +102,59 @@ async def pillar_set(request: Request, host: str, value: str):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
# TODO: list, create update and delete hosts
|
||||
@app.get("/hosts")
|
||||
async def host_list(request: Request):
|
||||
all_hosts = list_all_hosts(request.state.db)
|
||||
return JSONResponse([x.name for x in all_hosts if x.parent_id is None])
|
||||
return JSONResponse([x.name for x in all_hosts if not x.is_hostgroup])
|
||||
|
||||
|
||||
# TODO: list, create, update and delete hostgroups
|
||||
@app.get("/hostgroups")
|
||||
async def hostgroup_list(request: Request):
|
||||
all_hosts = list_all_hosts(request.state.db)
|
||||
return JSONResponse([x.name for x in all_hosts if x.parent_id is not None])
|
||||
return JSONResponse([x.name for x in all_hosts if x.is_hostgroup])
|
||||
|
||||
|
||||
# TODO: list, create, update and delete states
|
||||
# TODO: list, create, update and delete environments
|
||||
|
||||
# TODO: top files generated on a per host basis
|
||||
@app.get("/top/{fqdn}")
|
||||
async def host_top(request: Request, fqdn: str):
|
||||
async def host_top(req: Request, fqdn: str):
|
||||
db: Session = req.state.db
|
||||
|
||||
if not validate_fqdn(fqdn):
|
||||
return JSONResponse(status_code=400, content={
|
||||
'message': f"Invalid FQDN: {fqdn}"
|
||||
})
|
||||
|
||||
environment_stmt = select(Environment)
|
||||
result = db.execute(environment_stmt).fetchall()
|
||||
|
||||
if len(result) == 0:
|
||||
return JSONResponse(status_code=400, content={
|
||||
'message': "There are no environments defined"
|
||||
})
|
||||
|
||||
environments: list[Environment] = list(map(lambda x: x[0], result))
|
||||
|
||||
|
||||
stmt_host = select(Host).where(Host.name == fqdn)
|
||||
result = db.execute(stmt_host).fetchall()
|
||||
if len(result) < 1:
|
||||
return JSONResponse(status_code=404, content={
|
||||
'message': f"No such Host is known: {fqdn}"
|
||||
})
|
||||
|
||||
# this should be enforced by the database
|
||||
assert len(result) == 1
|
||||
|
||||
host: Host = result[0][0]
|
||||
|
||||
stmt_top = select(Environment, Host, State).where(Environment).join
|
||||
# TODO: implement
|
||||
return JSONResponse({})
|
||||
|
||||
"""
|
||||
@ -3,9 +3,10 @@ import base64
|
||||
import click
|
||||
|
||||
import requests
|
||||
from click import Context
|
||||
|
||||
from pillar_tool.schemas import HostCreateParams
|
||||
from pillar_tool.util import config, load_config, Config
|
||||
from pillar_tool.util.validation import split_and_validate_path, validate_fqdn
|
||||
|
||||
cfg: Config | None = None
|
||||
base_url: str | None = None
|
||||
@ -39,6 +40,14 @@ def pillar():
|
||||
def host():
|
||||
pass
|
||||
|
||||
@main.group("hostgroup")
|
||||
def hostgroup():
|
||||
pass
|
||||
|
||||
@main.group("environment")
|
||||
def environment():
|
||||
pass
|
||||
|
||||
@main.group("query")
|
||||
def query():
|
||||
pass
|
||||
@ -55,7 +64,7 @@ def pillar_list():
|
||||
def host_list():
|
||||
click.echo("Listing known hosts...")
|
||||
try:
|
||||
response = requests.get(f"{base_url}/hosts", headers=auth_header)
|
||||
response = requests.get(f"{base_url}/host", headers=auth_header)
|
||||
response.raise_for_status()
|
||||
|
||||
print(response.json())
|
||||
@ -68,16 +77,48 @@ def host_list():
|
||||
def host_create(fqdn: str, parent: str | None):
|
||||
click.echo("Creating host...")
|
||||
try:
|
||||
response = requests.post(f"{base_url}/host/{fqdn}", json={'parent': parent}, headers=auth_header)
|
||||
params = HostCreateParams(
|
||||
parent=parent
|
||||
)
|
||||
response = requests.post(f"{base_url}/host/{fqdn}", json=params.model_dump(), headers=auth_header)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise click.ClickException(f"Failed to create host:\n{e}")
|
||||
|
||||
|
||||
click.echo(f"Host '{fqdn}' created!")
|
||||
|
||||
|
||||
@host.command("delete")
|
||||
@click.argument("full_path")
|
||||
def host_delete(full_path: str):
|
||||
click.confirm(f"Are you sure you want to delete")
|
||||
@click.argument("fqdn")
|
||||
def host_delete(fqdn: str):
|
||||
if not validate_fqdn(fqdn):
|
||||
click.echo("Invalid FQDN")
|
||||
return
|
||||
|
||||
if click.confirm(f"Are you sure you want to delete '{fqdn}'?"):
|
||||
click.echo("Deleting host...")
|
||||
|
||||
try:
|
||||
response = requests.delete(f'{base_url}/host/{fqdn}', headers=auth_header)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise click.ClickException(f"Failed to delete host:\n{e}")
|
||||
|
||||
|
||||
@hostgroup.command("list")
|
||||
def hostgroup_list():
|
||||
click.echo("Listing known hostgroups...")
|
||||
click.echo("TODO: implement")
|
||||
|
||||
|
||||
@hostgroup.command("create")
|
||||
@click.argument("path")
|
||||
def hostgroup_create(path: str):
|
||||
click.echo("TODO: implement")
|
||||
|
||||
|
||||
@hostgroup.command("delete")
|
||||
@click.argument("path")
|
||||
def hostgroup_delete(path: str):
|
||||
click.echo("TODO: implement")
|
||||
|
||||
|
||||
@ -23,11 +23,47 @@ router = APIRouter(
|
||||
def hosts_get(req: Request):
|
||||
db: Session = req.state.db
|
||||
|
||||
result = db.execute(select(Host)).fetchall()
|
||||
hosts: list[Host] = list(map(lambda x: x[0], result))
|
||||
|
||||
return JSONResponse(status_code=200, content=list(map(lambda x: x.name, hosts)))
|
||||
|
||||
|
||||
|
||||
@router.get("/{fqdn}")
|
||||
def host_get(req: Request, fqdn: str):
|
||||
db: Session = req.state.db
|
||||
|
||||
if not validate_fqdn(fqdn):
|
||||
raise HTTPException(status_code=400, detail="Provided host is not an FQDN")
|
||||
|
||||
host_stmt = select(Host).where(Host.name == fqdn)
|
||||
result = db.execute(host_stmt).fetchall()
|
||||
|
||||
if len(result) != 1:
|
||||
raise HTTPException(status_code=404, detail=f"No such host found (length of result was {len(result)})")
|
||||
|
||||
host: Host = result[0][0]
|
||||
|
||||
last_parent = host
|
||||
path = []
|
||||
parent_stmt = select(Host).where(Host.id == bindparam('parent_id'))
|
||||
while host.parent_id is not None:
|
||||
result = db.execute(parent_stmt, { 'parent_id': last_parent.parent_id }).fetchall()
|
||||
|
||||
# Note: this assertion should be enforced by the database
|
||||
assert len(result) == 1
|
||||
|
||||
parent = result[0][0]
|
||||
path.append(parent)
|
||||
last_parent = parent
|
||||
|
||||
path.reverse()
|
||||
return JSONResponse(status_code=200, content={
|
||||
"host": host.name,
|
||||
"path": '/'.join(map(lambda x: x.name, path))
|
||||
})
|
||||
|
||||
|
||||
@router.post("/{fqdn}")
|
||||
async def host_add(request: Request, fqdn: str, params: HostCreateParams):
|
||||
|
||||
@ -1,7 +1,45 @@
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
# Error returns
|
||||
class ErrorDetails(BaseModel):
|
||||
status: str
|
||||
message: str
|
||||
|
||||
class ErrorReturn(BaseModel):
|
||||
status_code: int
|
||||
content: ErrorDetails
|
||||
|
||||
def response(self):
|
||||
return JSONResponse(self.model_dump())
|
||||
|
||||
class HealthCheckError(ErrorReturn):
|
||||
def __init__(self, code: int, reason: str):
|
||||
super().__init__(**{
|
||||
'status_code': code,
|
||||
'content': ErrorDetails(
|
||||
status="error",
|
||||
message=f"Host is not healthy: {reason}"
|
||||
)
|
||||
})
|
||||
|
||||
class HealthCheckSuccess(ErrorReturn):
|
||||
def __init__(self):
|
||||
super().__init__(**{
|
||||
'status_code': 200,
|
||||
'content': ErrorDetails(
|
||||
status='ok',
|
||||
message='API is healthy'
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
# Host operations
|
||||
class HostCreateParams(BaseModel):
|
||||
parent: str | None
|
||||
|
||||
# Hostgroup operations
|
||||
class HostgroupCreateParams(BaseModel):
|
||||
parent: str | None
|
||||
parent: str | None
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user