@@ -29,6 +29,8 @@ use mlua::UserDataMethods;
29
29
use mlua:: Value ;
30
30
use tokio:: time;
31
31
32
+ use crate :: admin:: MetaAdminClient ;
33
+
32
34
const LUA_UTIL : & str = include_str ! ( "../lua_util.lua" ) ;
33
35
34
36
pub struct LuaGrpcClient {
@@ -69,6 +71,77 @@ impl UserData for LuaGrpcClient {
69
71
}
70
72
}
71
73
74
+ pub struct LuaAdminClient {
75
+ client : MetaAdminClient ,
76
+ }
77
+
78
+ impl LuaAdminClient {
79
+ pub fn new ( client : MetaAdminClient ) -> Self {
80
+ Self { client }
81
+ }
82
+ }
83
+
84
+ impl UserData for LuaAdminClient {
85
+ fn add_methods < M : UserDataMethods < Self > > ( methods : & mut M ) {
86
+ methods. add_async_method ( "metrics" , |_lua, this, ( ) | async move {
87
+ match this. client . get_metrics ( ) . await {
88
+ Ok ( metrics) => Ok ( ( Some ( metrics) , None :: < String > ) ) ,
89
+ Err ( e) => Ok ( ( None :: < String > , Some ( format ! ( "Admin API error: {}" , e) ) ) ) ,
90
+ }
91
+ } ) ;
92
+
93
+ methods. add_async_method ( "status" , |lua, this, ( ) | async move {
94
+ match this. client . status ( ) . await {
95
+ Ok ( result) => match lua. to_value ( & result) {
96
+ Ok ( lua_value) => Ok ( ( Some ( lua_value) , None :: < String > ) ) ,
97
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Lua conversion error: {}" , e) ) ) ) ,
98
+ } ,
99
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Admin API error: {}" , e) ) ) ) ,
100
+ }
101
+ } ) ;
102
+
103
+ methods. add_async_method ( "transfer_leader" , |lua, this, to : Option < u64 > | async move {
104
+ match this. client . transfer_leader ( to) . await {
105
+ Ok ( result) => match lua. to_value ( & result) {
106
+ Ok ( lua_value) => Ok ( ( Some ( lua_value) , None :: < String > ) ) ,
107
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Lua conversion error: {}" , e) ) ) ) ,
108
+ } ,
109
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Admin API error: {}" , e) ) ) ) ,
110
+ }
111
+ } ) ;
112
+
113
+ methods. add_async_method ( "trigger_snapshot" , |_lua, this, ( ) | async move {
114
+ match this. client . trigger_snapshot ( ) . await {
115
+ Ok ( _) => Ok ( ( Some ( true ) , None :: < String > ) ) ,
116
+ Err ( e) => Ok ( ( None :: < bool > , Some ( format ! ( "Admin API error: {}" , e) ) ) ) ,
117
+ }
118
+ } ) ;
119
+
120
+ methods. add_async_method ( "list_features" , |lua, this, ( ) | async move {
121
+ match this. client . list_features ( ) . await {
122
+ Ok ( result) => match lua. to_value ( & result) {
123
+ Ok ( lua_value) => Ok ( ( Some ( lua_value) , None :: < String > ) ) ,
124
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Lua conversion error: {}" , e) ) ) ) ,
125
+ } ,
126
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Admin API error: {}" , e) ) ) ) ,
127
+ }
128
+ } ) ;
129
+
130
+ methods. add_async_method (
131
+ "set_feature" ,
132
+ |lua, this, ( feature, enable) : ( String , bool ) | async move {
133
+ match this. client . set_feature ( & feature, enable) . await {
134
+ Ok ( result) => match lua. to_value ( & result) {
135
+ Ok ( lua_value) => Ok ( ( Some ( lua_value) , None :: < String > ) ) ,
136
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Lua conversion error: {}" , e) ) ) ) ,
137
+ } ,
138
+ Err ( e) => Ok ( ( None :: < Value > , Some ( format ! ( "Admin API error: {}" , e) ) ) ) ,
139
+ }
140
+ } ,
141
+ ) ;
142
+ }
143
+ }
144
+
72
145
pub struct LuaTask {
73
146
handle : Rc < RefCell < Option < tokio:: task:: JoinHandle < mlua:: Value > > > > ,
74
147
}
@@ -121,6 +194,18 @@ pub fn setup_lua_environment(lua: &Lua) -> anyhow::Result<()> {
121
194
. set ( "new_grpc_client" , new_grpc_client)
122
195
. map_err ( |e| anyhow:: anyhow!( "Failed to register new_grpc_client: {}" , e) ) ?;
123
196
197
+ // Register new_admin_client function
198
+ let new_admin_client = lua
199
+ . create_function ( |_lua, address : String | {
200
+ let client = MetaAdminClient :: new ( & address) ;
201
+ Ok ( LuaAdminClient :: new ( client) )
202
+ } )
203
+ . map_err ( |e| anyhow:: anyhow!( "Failed to create new_admin_client function: {}" , e) ) ?;
204
+
205
+ metactl_table
206
+ . set ( "new_admin_client" , new_admin_client)
207
+ . map_err ( |e| anyhow:: anyhow!( "Failed to register new_admin_client: {}" , e) ) ?;
208
+
124
209
// Export NULL constant to metactl namespace
125
210
metactl_table
126
211
. set ( "NULL" , Value :: NULL )
@@ -190,3 +275,69 @@ pub fn new_grpc_client(addresses: Vec<String>) -> Result<Arc<ClientHandle>, Crea
190
275
None ,
191
276
)
192
277
}
278
+
279
+ pub fn new_admin_client ( addr : & str ) -> MetaAdminClient {
280
+ MetaAdminClient :: new ( addr)
281
+ }
282
+
283
+ pub async fn run_lua_script ( script : & str ) -> anyhow:: Result < ( ) > {
284
+ let lua = Lua :: new ( ) ;
285
+
286
+ setup_lua_environment ( & lua) ?;
287
+
288
+ #[ allow( clippy:: disallowed_types) ]
289
+ let local = tokio:: task:: LocalSet :: new ( ) ;
290
+ let res = local. run_until ( lua. load ( script) . exec_async ( ) ) . await ;
291
+
292
+ if let Err ( e) = res {
293
+ return Err ( anyhow:: anyhow!( "Lua execution error: {}" , e) ) ;
294
+ }
295
+ Ok ( ( ) )
296
+ }
297
+
298
+ pub async fn run_lua_script_with_result (
299
+ script : & str ,
300
+ ) -> anyhow:: Result < Result < Option < String > , String > > {
301
+ let lua = Lua :: new ( ) ;
302
+
303
+ setup_lua_environment ( & lua) ?;
304
+
305
+ #[ allow( clippy:: disallowed_types) ]
306
+ let local = tokio:: task:: LocalSet :: new ( ) ;
307
+ let res = local
308
+ . run_until ( lua. load ( script) . eval_async :: < mlua:: MultiValue > ( ) )
309
+ . await ;
310
+
311
+ match res {
312
+ Ok ( values) => {
313
+ let mut iter = values. iter ( ) ;
314
+ let first = iter. next ( ) ;
315
+ let second = iter. next ( ) ;
316
+
317
+ match ( first, second) {
318
+ // (result, nil) - success with result
319
+ ( Some ( Value :: String ( s) ) , Some ( Value :: Nil ) ) => match s. to_str ( ) {
320
+ Ok ( str_val) => Ok ( Ok ( Some ( str_val. to_string ( ) ) ) ) ,
321
+ Err ( _) => Ok ( Err ( "String conversion error" . to_string ( ) ) ) ,
322
+ } ,
323
+ // (nil, nil) - success with no result
324
+ ( Some ( Value :: Nil ) , Some ( Value :: Nil ) ) => Ok ( Ok ( None ) ) ,
325
+ // (nil, error) - error case
326
+ ( Some ( Value :: Nil ) , Some ( Value :: String ( err) ) ) => match err. to_str ( ) {
327
+ Ok ( err_str) => Ok ( Err ( err_str. to_string ( ) ) ) ,
328
+ Err ( _) => Ok ( Err ( "Error string conversion failed" . to_string ( ) ) ) ,
329
+ } ,
330
+ // Single return value - treat as result
331
+ ( Some ( Value :: Nil ) , None ) => Ok ( Ok ( None ) ) ,
332
+ ( Some ( Value :: String ( s) ) , None ) => match s. to_str ( ) {
333
+ Ok ( str_val) => Ok ( Ok ( Some ( str_val. to_string ( ) ) ) ) ,
334
+ Err ( _) => Ok ( Err ( "String conversion error" . to_string ( ) ) ) ,
335
+ } ,
336
+ // Other combinations - treat first value as error if second is missing
337
+ ( Some ( other) , None ) => Ok ( Err ( format ! ( "{:?}" , other) ) ) ,
338
+ _ => Ok ( Ok ( None ) ) ,
339
+ }
340
+ }
341
+ Err ( e) => Err ( anyhow:: anyhow!( "Lua execution error: {}" , e) ) ,
342
+ }
343
+ }
0 commit comments