77import collections
88from django .conf import settings
99
10- from asgiref .sync import sync_to_async
10+ from asgiref .sync import sync_to_async , async_to_sync
1111
12- from kubernetes .client import Configuration
12+ from kubernetes .client import Configuration , exceptions
1313from kubernetes .client .api import core_v1_api
1414from kubernetes .stream import stream
1515from kubernetes .stream .ws_client import STDOUT_CHANNEL , STDERR_CHANNEL , ERROR_CHANNEL
2727
2828class BaseAppConsumer (AsyncWebsocketConsumer ):
2929
30- async def has_perm (self ):
30+ @database_sync_to_async
31+ def has_perm (self ):
3132 if self .scope ["user" ] is None :
3233 return False , "user not login"
3334 request = Request (self .scope ["user" ], "POST" )
34- app = await database_sync_to_async (App .objects .get )(id = self .id )
35- return await database_sync_to_async (has_app_permission )(request , app )
35+ try :
36+ app = App .objects .get (id = self .id )
37+ return has_app_permission (request , app )
38+ except App .DoesNotExist :
39+ return False , "user not exists"
3640
3741 async def connect (self ):
3842 self .id = self .scope ["url_route" ]["kwargs" ]["id" ]
@@ -43,6 +47,21 @@ async def connect(self):
4347 raise DenyConnection (message )
4448
4549
50+ class BaseK8sConsumer (BaseAppConsumer ):
51+
52+ @property
53+ def kubernetes (self ):
54+ with open ('/var/run/secrets/kubernetes.io/serviceaccount/token' ) as token_file :
55+ token = token_file .read ()
56+ config = Configuration (host = settings .SCHEDULER_URL )
57+ config .api_key = {"authorization" : "Bearer " + token }
58+ config .verify_ssl = settings .K8S_API_VERIFY_TLS
59+ if config .verify_ssl :
60+ config .ssl_ca_cert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
61+ Configuration .set_default (config )
62+ return core_v1_api .CoreV1Api ()
63+
64+
4665class AppLogsConsumer (BaseAppConsumer ):
4766
4867 async def receive (self , text_data = None , bytes_data = None ):
@@ -68,19 +87,30 @@ async def receive(self, text_data=None, bytes_data=None):
6887 raise ValueError ("text_data cannot be empty!" )
6988
7089
71- class AppPodExecConsumer ( BaseAppConsumer ):
90+ class AppPodLogsConsumer ( BaseK8sConsumer ):
7291
73- @property
74- def kubernetes (self ):
75- with open ('/var/run/secrets/kubernetes.io/serviceaccount/token' ) as token_file :
76- token = token_file .read ()
77- config = Configuration (host = settings .SCHEDULER_URL )
78- config .api_key = {"authorization" : "Bearer " + token }
79- config .verify_ssl = settings .K8S_API_VERIFY_TLS
80- if config .verify_ssl :
81- config .ssl_ca_cert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
82- Configuration .set_default (config )
83- return core_v1_api .CoreV1Api ()
92+ async def connect (self ):
93+ await super ().connect ()
94+ self .pod_id = self .scope ["url_route" ]["kwargs" ]["pod_id" ]
95+
96+ @sync_to_async
97+ def receive (self , text_data = None , bytes_data = None ):
98+ kwargs = json .loads (text_data )
99+ try :
100+ stream = self .kubernetes .read_namespaced_pod_log (self .pod_id , self .id , ** {
101+ "tail_lines" : kwargs .get ("lines" , 100 ),
102+ "follow" : kwargs .get ("follow" , False ),
103+ "container" : kwargs .get ("container" , "" ),
104+ "_preload_content" : False ,
105+ }).stream ()
106+ for line in stream :
107+ async_to_sync (self .send )(text_data = line )
108+ except exceptions .ApiException as e :
109+ async_to_sync (self .send )(text_data = str (e ))
110+ async_to_sync (self .close )(code = 1000 )
111+
112+
113+ class AppPodExecConsumer (BaseK8sConsumer ):
84114
85115 async def connect (self ):
86116 self .stream = None
@@ -121,8 +151,13 @@ async def receive(self, text_data=None, bytes_data=None):
121151 args = (self .kubernetes .connect_get_namespaced_pod_exec , self .pod_id , self .id )
122152 kwargs = json .loads (text_data )
123153 kwargs .update ({"stderr" : True , "stdout" : True , "_preload_content" : False })
124- self .stream = stream (* args , ** kwargs )
125- asyncio .create_task (self .task ())
154+ try :
155+ self .stream = stream (* args , ** kwargs )
156+ except exceptions .ApiException as e :
157+ await self .send (str (e ), STDERR_CHANNEL )
158+ await self .close (code = 1000 )
159+ else :
160+ asyncio .create_task (self .task ())
126161 elif self .stream is not None :
127162 data = text_data if text_data else bytes_data
128163 channel , data = ord (data [0 ]), data [1 :]
0 commit comments