FinishRunning returns cmd.Wait() and/or times out.
(cmd *exec.Cmd)
| 140 | |
| 141 | // FinishRunning returns cmd.Wait() and/or times out. |
| 142 | func (c *Control) FinishRunning(cmd *exec.Cmd) error { |
| 143 | stepName := strings.Join(cmd.Args, " ") |
| 144 | if c.isTerminated() { |
| 145 | return fmt.Errorf("skipped %s (kubetest is terminated)", stepName) |
| 146 | } |
| 147 | if cmd.Stdout == nil && c.verbose { |
| 148 | cmd.Stdout = os.Stdout |
| 149 | } |
| 150 | if cmd.Stderr == nil && c.verbose { |
| 151 | cmd.Stderr = os.Stderr |
| 152 | } |
| 153 | log.Printf("Running: %v", stepName) |
| 154 | defer func(start time.Time) { |
| 155 | log.Printf("Step '%s' finished in %s", stepName, time.Since(start)) |
| 156 | }(time.Now()) |
| 157 | |
| 158 | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} |
| 159 | if err := cmd.Start(); err != nil { |
| 160 | return fmt.Errorf("error starting %v: %w", stepName, err) |
| 161 | } |
| 162 | |
| 163 | finished := make(chan error) |
| 164 | |
| 165 | sigChannel := make(chan os.Signal, 1) |
| 166 | signal.Notify(sigChannel, os.Interrupt) |
| 167 | |
| 168 | go func() { |
| 169 | finished <- cmd.Wait() |
| 170 | }() |
| 171 | |
| 172 | for { |
| 173 | select { |
| 174 | case <-sigChannel: |
| 175 | log.Printf("Killing %v(%v) after receiving signal", stepName, -cmd.Process.Pid) |
| 176 | |
| 177 | pgid := getGroupPid(cmd.Process.Pid) |
| 178 | |
| 179 | if err := syscall.Kill(-pgid, syscall.SIGKILL); err != nil { |
| 180 | log.Printf("Failed to kill %v: %v", stepName, err) |
| 181 | } |
| 182 | |
| 183 | case <-c.Terminate.C: |
| 184 | c.termLock.Lock() |
| 185 | c.terminated = true |
| 186 | c.termLock.Unlock() |
| 187 | c.Terminate.Reset(time.Duration(-1)) // Kill subsequent processes immediately. |
| 188 | pgid := getGroupPid(cmd.Process.Pid) |
| 189 | if err := syscall.Kill(-pgid, syscall.SIGKILL); err != nil { |
| 190 | log.Printf("Failed to kill %v: %v", stepName, err) |
| 191 | } |
| 192 | if err := cmd.Process.Kill(); err != nil { |
| 193 | log.Printf("Failed to terminate %s (terminated 15m after interrupt): %v", stepName, err) |
| 194 | } |
| 195 | case <-c.Interrupt.C: |
| 196 | c.intLock.Lock() |
| 197 | c.interrupted = true |
| 198 | c.intLock.Unlock() |
| 199 | log.Printf("Interrupt after %s timeout during %s. Will terminate in another 15m", c.Timeout, stepName) |