github.com/dmaizel/tests@v0.0.0-20210728163746-cae6a2d9cee8/command.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package tests
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os/exec"
    11  	"syscall"
    12  	"time"
    13  )
    14  
    15  // Command contains the information of the command to run
    16  type Command struct {
    17  	// cmd exec.Cmd
    18  	cmd *exec.Cmd
    19  
    20  	// Timeout is the time limit of seconds of the command
    21  	Timeout time.Duration
    22  }
    23  
    24  // NewCommand returns a new instance of Command
    25  func NewCommand(path string, args ...string) *Command {
    26  	c := new(Command)
    27  	c.cmd = exec.Command(path, args...)
    28  	c.Timeout = time.Duration(Timeout)
    29  
    30  	return c
    31  }
    32  
    33  // Run runs a command returning its stdout, stderr and exit code
    34  func (c *Command) Run() (string, string, int) {
    35  	return c.RunWithPipe(nil)
    36  }
    37  
    38  // RunWithPipe runs a command with stdin as an input and returning its stdout, stderr and exit code
    39  func (c *Command) RunWithPipe(stdin *bytes.Buffer) (string, string, int) {
    40  	LogIfFail("Running command '%s %s'\n", c.cmd.Path, c.cmd.Args)
    41  
    42  	keepAliveTime := 1 * time.Minute
    43  
    44  	var stdout, stderr bytes.Buffer
    45  	c.cmd.Stdout = &stdout
    46  	c.cmd.Stderr = &stderr
    47  
    48  	if stdin != nil {
    49  		c.cmd.Stdin = stdin
    50  	}
    51  
    52  	if err := c.cmd.Start(); err != nil {
    53  		LogIfFail("could no start command: %v\n", err)
    54  	}
    55  
    56  	done := make(chan error)
    57  	go func() { done <- c.cmd.Wait() }()
    58  
    59  	var timeout <-chan time.Time
    60  	if c.Timeout > 0 {
    61  		timeout = time.After(c.Timeout * time.Second)
    62  	}
    63  
    64  	keepAliveCh := time.NewTimer(keepAliveTime)
    65  
    66  	for {
    67  		select {
    68  		case <-timeout:
    69  			keepAliveCh.Stop()
    70  			LogIfFail("Killing process timeout reached '%d' seconds\n", c.Timeout)
    71  			_ = c.cmd.Process.Kill()
    72  			return "", "", -1
    73  
    74  		case <-keepAliveCh.C:
    75  			// Avoid CI (i.e jenkins) kills the process for inactivity by printing a dot
    76  			fmt.Println(".")
    77  			keepAliveCh.Reset(keepAliveTime)
    78  
    79  		case err := <-done:
    80  			keepAliveCh.Stop()
    81  			if err != nil {
    82  				LogIfFail("command failed error '%s'\n", err)
    83  			}
    84  
    85  			exitCode := c.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
    86  
    87  			LogIfFail("%+v\nTimeout: %d seconds\nExit Code: %d\nStdout: %s\nStderr: %s\n",
    88  				c.cmd.Args, c.Timeout, exitCode, stdout.String(), stderr.String())
    89  
    90  			return stdout.String(), stderr.String(), exitCode
    91  		}
    92  	}
    93  }