@@ -21,6 +21,7 @@ import (
2121 "path/filepath"
2222 "regexp"
2323 "strconv"
24+ "sync"
2425 "time"
2526
2627 "github.com/tarantool/go-tarantool/v2"
@@ -86,38 +87,61 @@ type TarantoolInstance struct {
8687 // Dialer to check that connection established.
8788 Dialer tarantool.Dialer
8889
89- done chan error
90+ is_stopping bool
9091 is_done bool
9192 result error
92- is_stopping bool
93+ wg * sync. WaitGroup
9394}
9495
95- // Status checks if Tarantool instance is still running.
96- // Return true if it is running, false if it is not.
97- // If instance was exit and error is nil - process completed success with zero status code.
98- func (t * TarantoolInstance ) Status () (bool , error ) {
99- if t .is_done {
100- return false , t .result
96+ func newTarantoolInstance () TarantoolInstance {
97+ return TarantoolInstance {
98+ is_stopping : false ,
99+ is_done : false ,
100+ wg : & sync.WaitGroup {},
101101 }
102+ }
102103
103- select {
104- case t .result = <- t .done :
104+ func (t * TarantoolInstance ) checkDone () {
105+ if t .wg == nil {
106+ panic ("TarantoolInstance is not initialized" )
107+ }
108+ t .wg .Add (1 )
109+ go func () {
110+ defer t .wg .Done ()
111+ t .result = t .Cmd .Wait ()
105112 t .is_done = true
106- return false , t .result
107- default :
108- return true , nil
113+ if ! t .is_stopping {
114+ log .Printf ("Tarantool %q was unexpected terminated: %v" , t .Opts .Listen , t .result )
115+ }
116+ }()
117+ }
118+
119+ func (t * TarantoolInstance ) Wait () error {
120+ if t .wg == nil {
121+ panic ("TarantoolInstance is not initialized" )
109122 }
123+ t .wg .Wait ()
124+ if t .is_stopping {
125+ return nil
126+ }
127+ return t .result
110128}
111129
112- func (t * TarantoolInstance ) checkDone () {
113- t .is_done = false
114- t .is_stopping = false
115- t .done = make (chan error , 1 )
116- t .done <- t .Cmd .Wait ()
117- if ! t .is_stopping {
118- _ , err := t .Status ()
119- log .Printf ("Tarantool was unexpected terminated: %s" , err )
130+ func (t * TarantoolInstance ) Stop () error {
131+ log .Printf ("Stopping Tarantool instance %q" , t .Opts .Listen )
132+ t .is_stopping = true
133+ if t .is_done {
134+ return nil
135+ }
136+ if t .Cmd != nil && t .Cmd .Process != nil {
137+ if err := t .Cmd .Process .Kill (); err != nil && ! t .is_done {
138+ return fmt .Errorf ("failed to kill tarantool %q (pid %d), got %s" ,
139+ t .Opts .Listen , t .Cmd .Process .Pid , err )
140+ }
141+ t .Wait ()
142+ t .Cmd .Process = nil
120143 }
144+ return nil
121145}
122146
123147func isReady (dialer tarantool.Dialer , opts * tarantool.Opts ) error {
@@ -235,46 +259,77 @@ func RestartTarantool(inst *TarantoolInstance) error {
235259 return err
236260}
237261
262+ func removeByMask (dir string , masks ... string ) error {
263+ for _ , mask := range masks {
264+ files , err := filepath .Glob (filepath .Join (dir , mask ))
265+ if err != nil {
266+ return err
267+ }
268+ for _ , f := range files {
269+ if err = os .Remove (f ); err != nil {
270+ return err
271+ }
272+ }
273+ }
274+ return nil
275+ }
276+
277+ func prepareDir (workDir string ) (string , error ) {
278+ if workDir == "" {
279+ dir , err := os .MkdirTemp ("" , "work_dir" )
280+ if err != nil {
281+ return "" , err
282+ }
283+ return dir , nil
284+ }
285+ // Create work_dir.
286+ err := os .MkdirAll (workDir , 0755 )
287+ if err != nil {
288+ return "" , err
289+ }
290+
291+ // Clean up existing work_dir.
292+ // TODO: Ensure that nested files will be removed.
293+ err = removeByMask (workDir , "*.snap" , "*.xlog" )
294+ if err != nil {
295+ return "" , err
296+ }
297+ return workDir , nil
298+ }
299+
238300// StartTarantool starts a tarantool instance for tests
239301// with specifies parameters (refer to StartOpts).
240302// Process must be stopped with StopTarantool.
241303func StartTarantool (startOpts StartOpts ) (TarantoolInstance , error ) {
242304 // Prepare tarantool command.
243- var inst TarantoolInstance
244- var dir string
305+ inst := newTarantoolInstance ()
245306 var err error
246307
247308 inst .Dialer = startOpts .Dialer
248-
249- if startOpts .WorkDir == "" {
250- dir , err = os .MkdirTemp ("" , "work_dir" )
251- if err != nil {
252- return inst , err
253- }
254- startOpts .WorkDir = dir
255- } else {
256- // Clean up existing work_dir.
257- err = os .RemoveAll (startOpts .WorkDir )
258- if err != nil {
259- return inst , err
260- }
261-
262- // Create work_dir.
263- err = os .Mkdir (startOpts .WorkDir , 0755 )
264- if err != nil {
265- return inst , err
266- }
309+ startOpts .WorkDir , err = prepareDir (startOpts .WorkDir )
310+ if err != nil {
311+ return inst , fmt .Errorf ("failed prepare working dir %q: %w" , startOpts .WorkDir , err )
267312 }
268- args := []string {}
269313
314+ args := []string {}
270315 if startOpts .InitScript != "" {
316+ if ! filepath .IsAbs (startOpts .InitScript ) {
317+ cwd , err := os .Getwd ()
318+ if err != nil {
319+ return inst , fmt .Errorf ("failed to get current working directory: %w" , err )
320+ }
321+ startOpts .InitScript = filepath .Join (cwd , startOpts .InitScript )
322+ }
271323 args = append (args , startOpts .InitScript )
272324 }
273325 if startOpts .ConfigFile != "" && startOpts .InstanceName != "" {
274326 args = append (args , "--config" , startOpts .ConfigFile )
275327 args = append (args , "--name" , startOpts .InstanceName )
276328 }
277329 inst .Cmd = exec .Command (getTarantoolExec (), args ... )
330+ inst .Cmd .Dir = startOpts .WorkDir
331+ inst .Cmd .Stdout = os .Stderr
332+ inst .Cmd .Stderr = os .Stderr
278333
279334 inst .Cmd .Env = append (
280335 os .Environ (),
@@ -306,7 +361,7 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
306361 // see https://github.com/tarantool/go-tarantool/issues/136
307362 time .Sleep (startOpts .WaitStart )
308363
309- go inst .checkDone ()
364+ inst .checkDone ()
310365
311366 opts := tarantool.Opts {
312367 Timeout : 500 * time .Millisecond ,
@@ -327,15 +382,16 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
327382 }
328383 }
329384
330- working , err_st := inst .Status ()
331- if ! working || err_st != nil {
385+ if inst .is_done && inst .result != nil {
332386 StopTarantool (inst )
333- return TarantoolInstance {}, fmt .Errorf ("unexpected terminated Tarantool: %w" , err_st )
387+ return TarantoolInstance {}, fmt .Errorf ("unexpected terminated Tarantool %q: %w" ,
388+ inst .Opts .Listen , inst .result )
334389 }
335390
336391 if err != nil {
337392 StopTarantool (inst )
338- return TarantoolInstance {}, fmt .Errorf ("failed to connect Tarantool: %w" , err )
393+ return TarantoolInstance {}, fmt .Errorf ("failed to connect Tarantool %q: %w" ,
394+ inst .Opts .Listen , err )
339395 }
340396
341397 return inst , nil
@@ -345,25 +401,9 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
345401// with StartTarantool. Waits until any resources
346402// associated with the process is released. If something went wrong, fails.
347403func StopTarantool (inst TarantoolInstance ) {
348- log .Printf ("Stopping Tarantool instance" )
349- inst .is_stopping = true
350- if inst .Cmd != nil && inst .Cmd .Process != nil {
351- if err := inst .Cmd .Process .Kill (); err != nil {
352- is_running , _ := inst .Status ()
353- if is_running {
354- log .Fatalf ("Failed to kill tarantool (pid %d), got %s" , inst .Cmd .Process .Pid , err )
355- }
356- }
357-
358- // Wait releases any resources associated with the Process.
359- if _ , err := inst .Cmd .Process .Wait (); err != nil {
360- is_running , _ := inst .Status ()
361- if is_running {
362- log .Fatalf ("Failed to wait for Tarantool process to exit, got %s" , err )
363- }
364- }
365-
366- inst .Cmd .Process = nil
404+ err := inst .Stop ()
405+ if err != nil {
406+ log .Fatal (err )
367407 }
368408}
369409
0 commit comments