22import argparse
33import asyncio
44import logging
5- from typing import Any , Iterable
65
7- from commonwealth .utils .apis import GenericErrorHandlingRoute
86from commonwealth .utils .logs import InterceptHandler , init_logger
9- from commonwealth .utils .streaming import streamer , timeout_streamer
10- from fastapi import FastAPI , HTTPException , status
11- from fastapi .responses import HTMLResponse , PlainTextResponse , StreamingResponse
12- from fastapi_versioning import VersionedFastAPI , version
137from loguru import logger
14- from pydantic import BaseModel
158from uvicorn import Config , Server
169
17- from kraken import Kraken
18-
19-
20- class Extension (BaseModel ):
21- name : str
22- docker : str
23- tag : str
24- permissions : str
25- enabled : bool
26- identifier : str
27- user_permissions : str
28-
29- def is_valid (self ) -> bool :
30- return all ([self .name , self .docker , self .tag , any ([self .permissions , self .user_permissions ]), self .identifier ])
31-
32-
33- SERVICE_NAME = "kraken"
10+ from api import application
11+ from config import SERVICE_NAME
3412
3513logging .basicConfig (handlers = [InterceptHandler ()], level = 0 )
3614init_logger (SERVICE_NAME )
3715
38- kraken = Kraken ()
39-
40- app = FastAPI (
41- title = "Kraken API" ,
42- description = "Kraken is the BlueOS service responsible for installing and managing thirdy-party extensions." ,
43- )
44- app .router .route_class = GenericErrorHandlingRoute
45- logger .info ("Releasing the Kraken!" )
46-
47-
48- @app .get ("/extensions_manifest" , status_code = status .HTTP_200_OK )
49- @version (1 , 0 )
50- async def fetch_manifest () -> Any :
51- return await kraken .fetch_manifest ()
52-
53-
54- @app .get ("/installed_extensions" , status_code = status .HTTP_200_OK )
55- @version (1 , 0 )
56- async def get_installed_extensions () -> Any :
57- extensions = await kraken .get_configured_extensions ()
58- extensions_list = [
59- Extension (
60- identifier = extension .identifier ,
61- name = extension .name ,
62- docker = extension .docker ,
63- tag = extension .tag ,
64- permissions = extension .permissions ,
65- enabled = extension .enabled ,
66- user_permissions = extension .user_permissions ,
67- )
68- for extension in extensions
69- ]
70- extensions_list .sort (key = lambda extension : extension .name )
71- return extensions_list
72-
73-
74- @app .post ("/extension/install" , status_code = status .HTTP_201_CREATED )
75- @version (1 , 0 )
76- async def install_extension (extension : Extension ) -> Any :
77- if not extension .is_valid ():
78- raise HTTPException (
79- status_code = status .HTTP_400_BAD_REQUEST ,
80- detail = "Invalid extension description" ,
81- )
82- if not kraken .has_enough_disk_space ():
83- raise HTTPException (
84- status_code = status .HTTP_507_INSUFFICIENT_STORAGE ,
85- detail = "Not enough disk space to install the extension" ,
86- )
87- compatible_digest = await kraken .is_compatible_extension (extension .identifier , extension .tag )
88- # Throw an exception only if compatible_digest is False, indicating the extension is in the manifest but it is
89- # incompatible. If compatible_digest is None, we are going to trusty that the image is compatible and will work
90- if compatible_digest is False :
91- raise HTTPException (
92- status_code = status .HTTP_400_BAD_REQUEST ,
93- detail = "Extension is not compatible with the current machine running BlueOS." ,
94- )
95- return StreamingResponse (streamer (kraken .install_extension (extension , compatible_digest )))
96-
97-
98- @app .post ("/extension/update_to_version" , status_code = status .HTTP_201_CREATED )
99- @version (1 , 0 )
100- async def update_extension (extension_identifier : str , new_version : str ) -> Any :
101- return StreamingResponse (streamer (kraken .update_extension_to_version (extension_identifier , new_version )))
102-
103-
104- @app .post ("/extension/uninstall" , status_code = status .HTTP_200_OK )
105- @version (1 , 0 )
106- async def uninstall_extension (extension_identifier : str ) -> Any :
107- return await kraken .uninstall_extension_from_identifier (extension_identifier )
108-
109-
110- @app .post ("/extension/enable" , status_code = status .HTTP_200_OK )
111- @version (1 , 0 )
112- async def enable_extension (extension_identifier : str ) -> Any :
113- return await kraken .enable_extension (extension_identifier )
114-
115-
116- @app .post ("/extension/disable" , status_code = status .HTTP_200_OK )
117- @version (1 , 0 )
118- async def disable_extension (extension_identifier : str ) -> Any :
119- return await kraken .disable_extension (extension_identifier )
120-
121-
122- @app .post ("/extension/restart" , status_code = status .HTTP_202_ACCEPTED )
123- @version (1 , 0 )
124- async def restart_extension (extension_identifier : str ) -> Any :
125- return await kraken .restart_extension (extension_identifier )
126-
127-
128- @app .get ("/list_containers" , status_code = status .HTTP_200_OK )
129- @version (1 , 0 )
130- async def list_containers () -> Any :
131- containers = await kraken .list_containers ()
132- return [
133- {
134- "name" : container ["Names" ][0 ],
135- "image" : container ["Image" ],
136- "imageId" : container ["ImageID" ],
137- "status" : container ["Status" ],
138- }
139- for container in containers
140- ]
141-
142-
143- @app .get ("/log" , status_code = status .HTTP_200_OK , response_class = PlainTextResponse )
144- @version (1 , 0 )
145- async def log_containers (container_name : str ) -> Iterable [bytes ]:
146- return StreamingResponse (timeout_streamer (kraken .stream_logs (container_name )), media_type = "text/plain" ) # type: ignore
147-
148-
149- @app .get ("/stats" , status_code = status .HTTP_200_OK )
150- @version (1 , 0 )
151- async def load_stats () -> Any :
152- return await kraken .load_stats ()
153-
154-
155- app = VersionedFastAPI (app , version = "1.0.0" , prefix_format = "/v{major}.{minor}" , enable_latest = True )
156-
157-
158- @app .get ("/" )
159- async def root () -> Any :
160- html_content = """
161- <html>
162- <head>
163- <title>Kraken</title>
164- </head>
165- </html>
166- """
167- return HTMLResponse (content = html_content , status_code = 200 )
168-
16+ from kraken import kraken_instance
16917
17018if __name__ == "__main__" :
17119 parser = argparse .ArgumentParser ()
@@ -179,9 +27,9 @@ async def root() -> Any:
17927
18028 loop = asyncio .new_event_loop ()
18129
182- config = Config (app = app , loop = loop , host = "0.0.0.0" , port = 9134 , log_config = None )
30+ config = Config (app = application , loop = loop , host = "0.0.0.0" , port = 9134 , log_config = None )
18331 server = Server (config )
18432
185- loop .create_task (kraken .run ())
33+ loop .create_task (kraken_instance .run ())
18634 loop .run_until_complete (server .serve ())
187- loop .run_until_complete (kraken .stop ())
35+ loop .run_until_complete (kraken_instance .stop ())
0 commit comments