internal/runner/runner.go (93 lines of code) (raw):
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package runner provides utilities to execute commands.
package runner
import (
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
)
// Runner is the interface to execute commands.
type Runner interface {
CmdRun(cmd *exec.Cmd) error
CmdOutput(cmd *exec.Cmd) ([]byte, error)
CmdCombinedOutput(cmd *exec.Cmd) ([]byte, error)
}
// Default is the Runner that executes the command by default.
type Default struct {
Quiet bool
}
// CmdRun executes the command.
func (d *Default) CmdRun(cmd *exec.Cmd) error {
if !d.Quiet {
log.Printf("Running: %v", cmd.Args)
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("%v: %s", err, stderr.String())
}
return nil
}
// CmdOutput executes the command and returns the command stdout.
func (d *Default) CmdOutput(cmd *exec.Cmd) ([]byte, error) {
if !d.Quiet {
log.Printf("Running: %v", cmd.Args)
}
var stderr bytes.Buffer
cmd.Stderr = &stderr
b, err := cmd.Output()
if err != nil {
return b, fmt.Errorf("%v: %s", err, stderr.String())
}
return b, nil
}
// CmdCombinedOutput executes the command and returns the command stdout and stderr.
func (d *Default) CmdCombinedOutput(cmd *exec.Cmd) ([]byte, error) {
if !d.Quiet {
log.Printf("Running: %v", cmd.Args)
}
return cmd.CombinedOutput()
}
// Multi will both print the output for the user and return it to callers.
// Useful for debugging, e.g. if the importer calls terraform import but it freezes without returning output.
// Inspired by https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html
type Multi struct {
Quiet bool
}
// CmdRun executes the command and prints stdout and stderr without returning either.
func (d *Multi) CmdRun(cmd *exec.Cmd) error {
if !d.Quiet {
log.Printf("Running: %v", cmd.Args)
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// CmdOutput executes the command and prints stdout and stderr then returns just stdout.
func (d *Multi) CmdOutput(cmd *exec.Cmd) ([]byte, error) {
if !d.Quiet {
log.Printf("Running: %v", cmd.Args)
}
var stdout bytes.Buffer
cmd.Stdin = os.Stdin
cmd.Stdout = io.MultiWriter(os.Stdout, &stdout)
cmd.Stderr = os.Stderr
err := cmd.Run()
return stdout.Bytes(), err
}
// CmdCombinedOutput executes the command and prints stdout and stderr then returns them.
func (d *Multi) CmdCombinedOutput(cmd *exec.Cmd) ([]byte, error) {
if !d.Quiet {
log.Printf("Running: %v", cmd.Args)
}
var combined bytes.Buffer
cmd.Stdin = os.Stdin
cmd.Stdout = io.MultiWriter(os.Stdout, &combined)
cmd.Stderr = io.MultiWriter(os.Stderr, &combined)
err := cmd.Run()
return combined.Bytes(), err
}
// Dry is the Runner that only prints commands and does not execute them.
type Dry struct{}
// CmdRun prints the command.
func (*Dry) CmdRun(cmd *exec.Cmd) error {
log.Printf("%v", cmd.String())
return nil
}
// CmdOutput prints the command.
func (d *Dry) CmdOutput(cmd *exec.Cmd) ([]byte, error) {
return []byte(cmd.String()), d.CmdRun(cmd)
}
// CmdCombinedOutput prints the command.
func (d *Dry) CmdCombinedOutput(cmd *exec.Cmd) ([]byte, error) {
return []byte(cmd.String()), d.CmdRun(cmd)
}