@@ -9,6 +9,12 @@ import (
9
9
"syscall"
10
10
)
11
11
12
+ // type CommandGroup struct {
13
+ // Commands []func(context.Context) *exec.Cmd
14
+ // Parallel bool
15
+ // Sequential bool
16
+ // }
17
+
12
18
type CmdExecutor struct {
13
19
logger * slog.Logger
14
20
parentCtx context.Context
@@ -19,12 +25,20 @@ type CmdExecutor struct {
19
25
mu sync.Mutex
20
26
21
27
kill func () error
28
+
29
+ Parallel []ParallelCommands
30
+ }
31
+
32
+ type ParallelCommands struct {
33
+ Index int
34
+ Len int
22
35
}
23
36
24
37
type CmdExecutorArgs struct {
25
38
Logger * slog.Logger
26
39
Commands []func (context.Context ) * exec.Cmd
27
40
Interactive bool
41
+ Parallel []ParallelCommands
28
42
}
29
43
30
44
func NewCmdExecutor (ctx context.Context , args CmdExecutorArgs ) * CmdExecutor {
@@ -38,6 +52,7 @@ func NewCmdExecutor(ctx context.Context, args CmdExecutorArgs) *CmdExecutor {
38
52
commands : args .Commands ,
39
53
mu : sync.Mutex {},
40
54
interactive : args .Interactive ,
55
+ Parallel : args .Parallel ,
41
56
}
42
57
}
43
58
@@ -67,72 +82,136 @@ func killPID(pid int, logger ...*slog.Logger) error {
67
82
return nil
68
83
}
69
84
70
- // Start implements Executor.
71
- func (ex * CmdExecutor ) Start () error {
72
- ex .mu .Lock ()
73
- defer ex .mu .Unlock ()
74
- for i := range ex .commands {
75
- if err := ex .parentCtx .Err (); err != nil {
76
- return err
77
- }
85
+ func (ex * CmdExecutor ) exec (newCmd func (context.Context ) * exec.Cmd ) error {
86
+ if err := ex .parentCtx .Err (); err != nil {
87
+ return err
88
+ }
78
89
79
- ctx , cf := context .WithCancel (ex .parentCtx )
80
- defer cf ()
90
+ ctx , cf := context .WithCancel (ex .parentCtx )
91
+ defer cf ()
81
92
82
- cmd := ex. commands [ i ] (ctx )
93
+ cmd := newCmd (ctx )
83
94
84
- cmd .SysProcAttr = & syscall.SysProcAttr {Setpgid : true }
85
- if ex .interactive {
86
- cmd .Stdin = os .Stdin
87
- cmd .SysProcAttr .Foreground = true
88
- }
95
+ cmd .SysProcAttr = & syscall.SysProcAttr {Setpgid : true }
96
+ if ex .interactive {
97
+ cmd .Stdin = os .Stdin
98
+ cmd .SysProcAttr .Foreground = true
99
+ }
89
100
90
- if err := cmd .Start (); err != nil {
91
- return err
92
- }
101
+ if err := cmd .Start (); err != nil {
102
+ return err
103
+ }
93
104
94
- logger := ex .logger .With ("pid" , cmd .Process .Pid , "command" , i + 1 )
105
+ logger := ex .logger .With ("pid" , cmd .Process .Pid , "command" , cmd . String () )
95
106
96
- ex .kill = func () error {
97
- return killPID (cmd .Process .Pid , logger )
98
- }
107
+ ex .kill = func () error {
108
+ return killPID (cmd .Process .Pid , logger )
109
+ }
110
+
111
+ exitErr := make (chan error , 1 )
99
112
100
- go func () {
101
- if err := cmd .Wait (); err != nil {
102
- logger .Debug ("process finished (wait completed), got" , "err" , err )
113
+ go func () {
114
+ if err := cmd .Wait (); err != nil {
115
+ exitErr <- err
116
+ logger .Debug ("process finished (wait completed), got" , "err" , err )
117
+ }
118
+ cf ()
119
+ }()
120
+
121
+ select {
122
+ case <- ctx .Done ():
123
+ logger .Debug ("process finished (context cancelled)" )
124
+ case err := <- exitErr :
125
+ if exitErr , ok := err .(* exec.ExitError ); ok {
126
+ logger .Debug ("process finished" , "exit.code" , exitErr .ExitCode ())
127
+ if exitErr .ExitCode () != 0 {
128
+ return err
103
129
}
104
- cf ()
105
- }()
106
-
107
- select {
108
- case <- ctx .Done ():
109
- logger .Debug ("process finished (context cancelled)" )
110
- case <- ex .parentCtx .Done ():
111
- logger .Debug ("process finished (parent context cancelled)" )
130
+ }
131
+ case <- ex .parentCtx .Done ():
132
+ logger .Debug ("process finished (parent context cancelled)" )
133
+ }
134
+
135
+ if ex .interactive {
136
+ // Send SIGTERM to the interactive process, as user will see it on his screen
137
+ proc , err := os .FindProcess (os .Getpid ())
138
+ if err != nil {
139
+ return err
112
140
}
113
141
114
- if ex . interactive {
115
- // Send SIGTERM to the interactive process, as user will see it on his screen
116
- proc , err := os . FindProcess ( os . Getpid ())
117
- if err != nil {
142
+ err = proc . Signal ( syscall . SIGTERM )
143
+ if err != nil {
144
+ if err != syscall . ESRCH {
145
+ logger . Error ( "failed to kill, got" , "err" , err )
118
146
return err
119
147
}
148
+ return err
149
+ }
150
+ }
151
+
152
+ if err := ex .kill (); err != nil {
153
+ return err
154
+ }
120
155
121
- err = proc .Signal (syscall .SIGTERM )
122
- if err != nil {
123
- if err != syscall .ESRCH {
124
- logger .Error ("failed to kill, got" , "err" , err )
125
- return err
156
+ logger .Debug ("command fully executed and processed" )
157
+ return nil
158
+ }
159
+
160
+ // Start implements Executor.
161
+ func (ex * CmdExecutor ) Start () error {
162
+ ex .mu .Lock ()
163
+ defer ex .mu .Unlock ()
164
+
165
+ var wg sync.WaitGroup
166
+
167
+ for i := 0 ; i < len (ex .commands ); i ++ {
168
+ newCmd := ex .commands [i ]
169
+
170
+ ex .logger .Info ("HELLO" , "idx" , i , "ex.parallel" , ex .Parallel )
171
+ isParallel := false
172
+
173
+ for _ , p := range ex .Parallel {
174
+ if p .Index == i {
175
+ isParallel = true
176
+ for k := i ; k <= i + p .Len ; k ++ {
177
+ wg .Add (1 )
178
+ go func () {
179
+ defer wg .Done ()
180
+ if err := ex .exec (newCmd ); err != nil {
181
+ ex .logger .Info ("executing, got" , "err" , err )
182
+ // handle error
183
+ }
184
+ }()
126
185
}
127
- return err
186
+
187
+ i = i + p .Len - 1
128
188
}
189
+ break
129
190
}
130
191
131
- if err := ex .kill (); err != nil {
192
+ if isParallel {
193
+ continue
194
+ }
195
+
196
+ // if ex.Parallel {
197
+ // wg.Add(1)
198
+ // go func() {
199
+ // defer wg.Add(1)
200
+ // if err := ex.exec(newCmd); err != nil {
201
+ // // handle error
202
+ // }
203
+ // }()
204
+ // continue
205
+ // }
206
+
207
+ if err := ex .exec (newCmd ); err != nil {
208
+ ex .logger .Error ("cmd failed with" , "err" , err )
132
209
return err
133
210
}
211
+ }
134
212
135
- logger .Debug ("command fully executed and processed" )
213
+ if len (ex .Parallel ) > 0 {
214
+ wg .Wait ()
136
215
}
137
216
138
217
return nil
0 commit comments