postdeploy-hooks/k8s-cleanup/executor.go (38 lines of code) (raw):
package main
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
)
// CommandExecutor contains command execution information.
type CommandExecutor struct {
// BinPath is the path of the binary being used for the command (e.g. the path
// to the kubectl binary if the kubectl command is to be used).
binPath string
}
// CreateCommandExecutor returns a CommandExecutor for the given binary.
func CreateCommandExecutor(binPath string) *CommandExecutor {
ce := &CommandExecutor{
binPath: binPath,
}
return ce
}
// execCommand runs the given command and returns the output.
func (ce CommandExecutor) execCommand(args []string) (string, error) {
fmt.Printf("Running the following command: %s %s\n", ce.binPath, args)
cmd := exec.Command(ce.binPath, args...)
// By default set locations to standard error and output (visible in cloud build logs)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
// Write error output to two locations simultaneously. This allows seeing the error output
// as the execution is happening (by writing to standard error) and also allows gathering
// all stderr at the end (by also writing to var stderr).
var stderr bytes.Buffer
errWriter := io.MultiWriter(&stderr, cmd.Stderr)
cmd.Stderr = errWriter
// Write error output to two locations simultaneously. This allows seeing the output
// as the execution is happening (by writing to stdout) and also allows gathering
// all stdout at the end (by also writing to var stdout).
var stdout bytes.Buffer
outWriter := io.MultiWriter(&stdout, cmd.Stdout)
cmd.Stdout = outWriter
// Start the command
if err := cmd.Start(); err != nil {
return "", fmt.Errorf("failed to start command, err is %w", err)
}
// Wait for everything to finish.
if err := cmd.Wait(); err != nil {
// Read the stdErr output
errorOutput := stderr.Bytes()
fullErr := fmt.Errorf("error running command: %w\n%s", err, errorOutput)
return "", fullErr
}
return stdout.String(), nil
}