Skip to content

Commit

Permalink
kraken: main: Change main to use new API module
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaoMario109 authored and patrickelectric committed May 20, 2024
1 parent 638f984 commit 9a0a1b6
Show file tree
Hide file tree
Showing 5 changed files with 9 additions and 160 deletions.
1 change: 1 addition & 0 deletions core/services/kraken/api/v1/routers/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
responses={status.HTTP_404_NOT_FOUND: {"description": "Not found"}},
)


@extension_router_v1.post("/install", status_code=status.HTTP_201_CREATED)
async def install_extension(extension: Extension) -> Any:
if not extension.is_valid():
Expand Down
1 change: 1 addition & 0 deletions core/services/kraken/api/v1/routers/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
responses={status.HTTP_404_NOT_FOUND: {"description": "Not found"}},
)


@index_router_v1.get("/extensions_manifest", status_code=status.HTTP_200_OK)
async def fetch_manifest() -> Any:
return await kraken_instance.fetch_manifest()
Expand Down
1 change: 1 addition & 0 deletions core/services/kraken/api/v2/routers/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
responses={status.HTTP_404_NOT_FOUND: {"description": "Not found"}},
)


@manifest_router_v2.get("/", status_code=status.HTTP_200_OK)
async def fetch(_data: bool = True) -> Response:
"""
Expand Down
2 changes: 0 additions & 2 deletions core/services/kraken/kraken.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,4 @@ async def stop(self) -> None:
self.should_run = False


# Global Kraken control instance

kraken_instance = Kraken()
164 changes: 6 additions & 158 deletions core/services/kraken/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,170 +2,18 @@
import argparse
import asyncio
import logging
from typing import Any, Iterable

from commonwealth.utils.apis import GenericErrorHandlingRoute
from commonwealth.utils.logs import InterceptHandler, init_logger
from commonwealth.utils.streaming import streamer, timeout_streamer
from fastapi import FastAPI, HTTPException, status
from fastapi.responses import HTMLResponse, PlainTextResponse, StreamingResponse
from fastapi_versioning import VersionedFastAPI, version
from loguru import logger
from pydantic import BaseModel
from uvicorn import Config, Server

from kraken import Kraken


class Extension(BaseModel):
name: str
docker: str
tag: str
permissions: str
enabled: bool
identifier: str
user_permissions: str

def is_valid(self) -> bool:
return all([self.name, self.docker, self.tag, any([self.permissions, self.user_permissions]), self.identifier])


SERVICE_NAME = "kraken"
from api import application
from config import SERVICE_NAME

logging.basicConfig(handlers=[InterceptHandler()], level=0)
init_logger(SERVICE_NAME)

kraken = Kraken()

app = FastAPI(
title="Kraken API",
description="Kraken is the BlueOS service responsible for installing and managing thirdy-party extensions.",
)
app.router.route_class = GenericErrorHandlingRoute
logger.info("Releasing the Kraken!")


@app.get("/extensions_manifest", status_code=status.HTTP_200_OK)
@version(1, 0)
async def fetch_manifest() -> Any:
return await kraken.fetch_manifest()


@app.get("/installed_extensions", status_code=status.HTTP_200_OK)
@version(1, 0)
async def get_installed_extensions() -> Any:
extensions = await kraken.get_configured_extensions()
extensions_list = [
Extension(
identifier=extension.identifier,
name=extension.name,
docker=extension.docker,
tag=extension.tag,
permissions=extension.permissions,
enabled=extension.enabled,
user_permissions=extension.user_permissions,
)
for extension in extensions
]
extensions_list.sort(key=lambda extension: extension.name)
return extensions_list


@app.post("/extension/install", status_code=status.HTTP_201_CREATED)
@version(1, 0)
async def install_extension(extension: Extension) -> Any:
if not extension.is_valid():
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid extension description",
)
if not kraken.has_enough_disk_space():
raise HTTPException(
status_code=status.HTTP_507_INSUFFICIENT_STORAGE,
detail="Not enough disk space to install the extension",
)
compatible_digest = await kraken.is_compatible_extension(extension.identifier, extension.tag)
# Throw an exception only if compatible_digest is False, indicating the extension is in the manifest but it is
# incompatible. If compatible_digest is None, we are going to trusty that the image is compatible and will work
if compatible_digest is False:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Extension is not compatible with the current machine running BlueOS.",
)
return StreamingResponse(streamer(kraken.install_extension(extension, compatible_digest)))


@app.post("/extension/update_to_version", status_code=status.HTTP_201_CREATED)
@version(1, 0)
async def update_extension(extension_identifier: str, new_version: str) -> Any:
return StreamingResponse(streamer(kraken.update_extension_to_version(extension_identifier, new_version)))


@app.post("/extension/uninstall", status_code=status.HTTP_200_OK)
@version(1, 0)
async def uninstall_extension(extension_identifier: str) -> Any:
return await kraken.uninstall_extension_from_identifier(extension_identifier)


@app.post("/extension/enable", status_code=status.HTTP_200_OK)
@version(1, 0)
async def enable_extension(extension_identifier: str) -> Any:
return await kraken.enable_extension(extension_identifier)


@app.post("/extension/disable", status_code=status.HTTP_200_OK)
@version(1, 0)
async def disable_extension(extension_identifier: str) -> Any:
return await kraken.disable_extension(extension_identifier)


@app.post("/extension/restart", status_code=status.HTTP_202_ACCEPTED)
@version(1, 0)
async def restart_extension(extension_identifier: str) -> Any:
return await kraken.restart_extension(extension_identifier)


@app.get("/list_containers", status_code=status.HTTP_200_OK)
@version(1, 0)
async def list_containers() -> Any:
containers = await kraken.list_containers()
return [
{
"name": container["Names"][0],
"image": container["Image"],
"imageId": container["ImageID"],
"status": container["Status"],
}
for container in containers
]


@app.get("/log", status_code=status.HTTP_200_OK, response_class=PlainTextResponse)
@version(1, 0)
async def log_containers(container_name: str) -> Iterable[bytes]:
return StreamingResponse(timeout_streamer(kraken.stream_logs(container_name)), media_type="text/plain") # type: ignore


@app.get("/stats", status_code=status.HTTP_200_OK)
@version(1, 0)
async def load_stats() -> Any:
return await kraken.load_stats()


app = VersionedFastAPI(app, version="1.0.0", prefix_format="/v{major}.{minor}", enable_latest=True)


@app.get("/")
async def root() -> Any:
html_content = """
<html>
<head>
<title>Kraken</title>
</head>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)

from kraken import kraken_instance

if __name__ == "__main__":
parser = argparse.ArgumentParser()
Expand All @@ -179,9 +27,9 @@ async def root() -> Any:

loop = asyncio.new_event_loop()

config = Config(app=app, loop=loop, host="0.0.0.0", port=9134, log_config=None)
config = Config(app=application, loop=loop, host="0.0.0.0", port=9134, log_config=None)
server = Server(config)

loop.create_task(kraken.run())
loop.create_task(kraken_instance.run())
loop.run_until_complete(server.serve())
loop.run_until_complete(kraken.stop())
loop.run_until_complete(kraken_instance.stop())

0 comments on commit 9a0a1b6

Please sign in to comment.