@@ -4,48 +4,24 @@ package plugin
44import (
55 "context"
66 "sync"
7-
8- "github.com/vadv/gopher-lua-libs/stats"
9-
10- cloudwatch "github.com/vadv/gopher-lua-libs/aws/cloudwatch"
11- chef "github.com/vadv/gopher-lua-libs/chef"
12- cmd "github.com/vadv/gopher-lua-libs/cmd"
13- crypto "github.com/vadv/gopher-lua-libs/crypto"
14- db "github.com/vadv/gopher-lua-libs/db"
15- filepath "github.com/vadv/gopher-lua-libs/filepath"
16- goos "github.com/vadv/gopher-lua-libs/goos"
17- http "github.com/vadv/gopher-lua-libs/http"
18- humanize "github.com/vadv/gopher-lua-libs/humanize"
19- inspect "github.com/vadv/gopher-lua-libs/inspect"
20- ioutil "github.com/vadv/gopher-lua-libs/ioutil"
21- json "github.com/vadv/gopher-lua-libs/json"
22- log "github.com/vadv/gopher-lua-libs/log"
23- pb "github.com/vadv/gopher-lua-libs/pb"
24- prometheus "github.com/vadv/gopher-lua-libs/prometheus/client"
25- regexp "github.com/vadv/gopher-lua-libs/regexp"
26- storage "github.com/vadv/gopher-lua-libs/storage"
27- strings "github.com/vadv/gopher-lua-libs/strings"
28- tac "github.com/vadv/gopher-lua-libs/tac"
29- tcp "github.com/vadv/gopher-lua-libs/tcp"
30- telegram "github.com/vadv/gopher-lua-libs/telegram"
31- template "github.com/vadv/gopher-lua-libs/template"
32- time "github.com/vadv/gopher-lua-libs/time"
33- xmlpath "github.com/vadv/gopher-lua-libs/xmlpath"
34- yaml "github.com/vadv/gopher-lua-libs/yaml"
35- zabbix "github.com/vadv/gopher-lua-libs/zabbix"
7+ "time"
368
379 lua "github.com/yuin/gopher-lua"
3810)
3911
4012type luaPlugin struct {
4113 sync.Mutex
14+ * sync.Cond
4215 state * lua.LState
4316 cancelFunc context.CancelFunc
4417 running bool
4518 error error
4619 body * string
4720 filename * string
4821 jobPayload * string
22+ args []lua.LValue
23+ doneCh lua.LChannel
24+ started bool
4925}
5026
5127func (p * luaPlugin ) getError () error {
@@ -77,51 +53,38 @@ func (p *luaPlugin) setRunning(val bool) {
7753// NewPluginState return lua state
7854func NewPluginState () * lua.LState {
7955 state := lua .NewState ()
80- // preload all
81- filepath .Preload (state )
82- http .Preload (state )
83- inspect .Preload (state )
84- ioutil .Preload (state )
85- json .Preload (state )
86- regexp .Preload (state )
87- strings .Preload (state )
88- tac .Preload (state )
89- tcp .Preload (state )
90- time .Preload (state )
91- xmlpath .Preload (state )
92- yaml .Preload (state )
93- zabbix .Preload (state )
94- telegram .Preload (state )
95- storage .Preload (state )
96- crypto .Preload (state )
97- goos .Preload (state )
98- humanize .Preload (state )
99- db .Preload (state )
100- chef .Preload (state )
101- cmd .Preload (state )
102- template .Preload (state )
103- cloudwatch .Preload (state )
104- log .Preload (state )
105- prometheus .Preload (state )
106- pb .Preload (state )
107- stats .Preload (state )
56+ PreloadAll (state )
10857 return state
10958}
11059
11160func (p * luaPlugin ) start () {
11261 p .Lock ()
11362 state := NewPluginState ()
63+ defer state .Close ()
11464 p .state = state
11565 p .error = nil
11666 p .running = true
117- isBody := (p .filename == nil )
118- if ! (p .jobPayload == nil ) {
67+ p .doneCh = make (lua.LChannel , 1 )
68+ p .started = true
69+ defer close (p .doneCh )
70+ isBody := p .filename == nil
71+ if p .jobPayload != nil {
11972 payload := * p .jobPayload
12073 state .SetGlobal (`payload` , lua .LString (payload ))
12174 }
12275 ctx , cancelFunc := context .WithCancel (context .Background ())
12376 p .cancelFunc = cancelFunc
12477 p .state .SetContext (ctx )
78+ newArg := state .NewTable ()
79+ for _ , arg := range p .args {
80+ switch t := arg .Type (); t {
81+ case lua .LTFunction :
82+ arg = state .NewFunctionFromProto (arg .(* lua.LFunction ).Proto )
83+ }
84+ newArg .Append (arg )
85+ }
86+ state .SetGlobal (`arg` , newArg )
87+ p .Signal ()
12588 p .Unlock ()
12689
12790 // blocking
@@ -144,10 +107,38 @@ func checkPlugin(L *lua.LState, n int) *luaPlugin {
144107 return nil
145108}
146109
110+ func NewLuaPlugin (L * lua.LState , n int ) * luaPlugin {
111+ ret := & luaPlugin {}
112+ ret .Cond = sync .NewCond (& ret .Mutex )
113+ top := L .GetTop ()
114+ for i := n ; i <= top ; i ++ {
115+ arg := L .Get (i )
116+ switch t := arg .Type (); t {
117+ case lua .LTFunction :
118+ f := arg .(* lua.LFunction )
119+ if len (f .Upvalues ) > 0 {
120+ L .ArgError (i , "cannot pass closures" )
121+ }
122+ ret .args = append (ret .args , arg )
123+ case lua .LTTable :
124+ if L .GetMetatable (arg ) != lua .LNil {
125+ L .ArgError (i , "tables with metadata are not allowed" )
126+ }
127+ ret .args = append (ret .args , arg )
128+ case lua .LTNil , lua .LTBool , lua .LTNumber , lua .LTString , lua .LTChannel :
129+ ret .args = append (ret .args , arg )
130+ default :
131+ L .ArgError (i , t .String ()+ " is not allowed" )
132+ }
133+ }
134+ return ret
135+ }
136+
147137// DoString lua plugin.do_string(body) returns plugin_ud
148138func DoString (L * lua.LState ) int {
149139 body := L .CheckString (1 )
150- p := & luaPlugin {body : & body }
140+ p := NewLuaPlugin (L , 2 )
141+ p .body = & body
151142 ud := L .NewUserData ()
152143 ud .Value = p
153144 L .SetMetatable (ud , L .GetTypeMetatable (`plugin_ud` ))
@@ -158,7 +149,8 @@ func DoString(L *lua.LState) int {
158149// DoFile lua plugin.do_file(filename) returns plugin_ud
159150func DoFile (L * lua.LState ) int {
160151 filename := L .CheckString (1 )
161- p := & luaPlugin {filename : & filename }
152+ p := NewLuaPlugin (L , 2 )
153+ p .filename = & filename
162154 ud := L .NewUserData ()
163155 ud .Value = p
164156 L .SetMetatable (ud , L .GetTypeMetatable (`plugin_ud` ))
@@ -170,7 +162,9 @@ func DoFile(L *lua.LState) int {
170162func DoFileWithPayload (L * lua.LState ) int {
171163 filename := L .CheckString (1 )
172164 payload := L .CheckString (2 )
173- p := & luaPlugin {filename : & filename , jobPayload : & payload }
165+ p := NewLuaPlugin (L , 2 )
166+ p .filename = & filename
167+ p .jobPayload = & payload
174168 ud := L .NewUserData ()
175169 ud .Value = p
176170 L .SetMetatable (ud , L .GetTypeMetatable (`plugin_ud` ))
@@ -182,7 +176,9 @@ func DoFileWithPayload(L *lua.LState) int {
182176func DoStringWithPayload (L * lua.LState ) int {
183177 body := L .CheckString (1 )
184178 payload := L .CheckString (2 )
185- p := & luaPlugin {body : & body , jobPayload : & payload }
179+ p := NewLuaPlugin (L , 2 )
180+ p .body = & body
181+ p .jobPayload = & payload
186182 ud := L .NewUserData ()
187183 ud .Value = p
188184 L .SetMetatable (ud , L .GetTypeMetatable (`plugin_ud` ))
@@ -191,10 +187,18 @@ func DoStringWithPayload(L *lua.LState) int {
191187}
192188
193189// Run lua plugin_ud:run()
194- func Run (L * lua.LState ) int {
190+ func Run (L * lua.LState ) ( nRet int ) {
195191 p := checkPlugin (L , 1 )
196192 go p .start ()
197- return 0
193+
194+ // ensure it's started
195+ p .Lock ()
196+ defer p .Unlock ()
197+ for ! p .started {
198+ p .Cond .Wait ()
199+ }
200+
201+ return
198202}
199203
200204// IsRunning lua plugin_ud:is_running()
@@ -204,6 +208,16 @@ func IsRunning(L *lua.LState) int {
204208 return 1
205209}
206210
211+ // DoneChannel lua plugin_ud:done_chan()
212+ func DoneChannel (L * lua.LState ) int {
213+ p := checkPlugin (L , 1 )
214+ if ! p .started {
215+ L .ArgError (1 , "Cannot obtain done channel on unstarted plugin" )
216+ }
217+ L .Push (p .doneCh )
218+ return 1
219+ }
220+
207221// Error lua plugin_ud:error() returns err
208222func Error (L * lua.LState ) int {
209223 p := checkPlugin (L , 1 )
@@ -223,3 +237,36 @@ func Stop(L *lua.LState) int {
223237 p .cancelFunc ()
224238 return 0
225239}
240+
241+ // Wait lua plugin_ud:wait()
242+ func Wait (L * lua.LState ) int {
243+ p := checkPlugin (L , 1 )
244+ // Can't wait if never started
245+ if ! p .started {
246+ L .RaiseError ("cannot wait on unstarted plugin" )
247+ }
248+ // Add timeout if requested
249+ ctx := context .Background ()
250+ cancel := func () {}
251+ timeout := lua .LVAsNumber (L .Get (2 ))
252+ if timeout > 0 {
253+ ctx , cancel = context .WithTimeout (ctx , time .Duration (timeout * lua .LNumber (time .Second )))
254+ defer cancel ()
255+ }
256+
257+ // Wait for done or timeout
258+ var err error
259+ select {
260+ case <- ctx .Done ():
261+ err = ctx .Err ()
262+ case <- p .doneCh :
263+ err = p .error
264+ }
265+
266+ // return error
267+ if err != nil {
268+ L .Push (lua .LString (err .Error ()))
269+ return 1
270+ }
271+ return 0
272+ }
0 commit comments