blob: 0a57ba0b67e554791f64972a4f4f7bcb23d4ce2c [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package execution
import (
"fmt"
"io"
"os"
"os/exec"
"strings"
)
// Executor supports chaining subcommand execution and error handling.
type Executor struct {
stdout io.Writer
stderr io.Writer
}
// NewExecutor returns a new Executor that writes to stdout and stderr.
func NewExecutor(stdout, stderr io.Writer) *Executor {
return &Executor{stdout: stdout, stderr: stderr}
}
// Exec runs the command at path with args.
func (e Executor) Exec(path string, args ...string) error {
cmd := exec.Cmd{
Path: path,
Args: append([]string{path}, args...),
Env: os.Environ(),
Stdout: e.stdout,
Stderr: e.stderr,
}
return cmd.Run()
}
// ExecAllWithRetries runs all given commands. Upon encountering an error after
// maxTries, execution stops and the error is returned. Returns an error if an element
// in cmds is empty.
func (e Executor) ExecAllWithRetries(cmds [][]string, maxTries int) error {
for i, cmd := range cmds {
if len(cmd) == 0 {
return fmt.Errorf("forbidden empty list in cmds at position %d", i)
}
path, args := cmd[0], cmd[1:]
for tries := 0; tries < maxTries; tries++ {
if err := e.Exec(path, args...); err != nil {
io.WriteString(e.stdout, fmt.Sprintf("failed to exec cmd: %s; %v", strings.Join(cmd, " "), err))
if tries == maxTries-1 {
return err
}
continue
}
break
}
}
return nil
}