1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
/*
Copyright 2019 The Kubernetes Authors.
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 exec
import (
"bufio"
"bytes"
"io"
"os"
"strings"
"github.com/alessio/shellescape"
"sigs.k8s.io/kind/pkg/errors"
)
// PrettyCommand takes arguments identical to Cmder.Command,
// it returns a pretty printed command that could be pasted into a shell
func PrettyCommand(name string, args ...string) string {
var out strings.Builder
out.WriteString(shellescape.Quote(name))
for _, arg := range args {
out.WriteByte(' ')
out.WriteString(shellescape.Quote(arg))
}
return out.String()
}
// RunErrorForError returns a RunError if the error contains a RunError.
// Otherwise it returns nil
func RunErrorForError(err error) *RunError {
var runError *RunError
for {
if rErr, ok := err.(*RunError); ok {
runError = rErr
}
if causerErr, ok := err.(errors.Causer); ok {
err = causerErr.Cause()
} else {
break
}
}
return runError
}
// CombinedOutputLines is like os/exec's cmd.CombinedOutput(),
// but over our Cmd interface, and instead of returning the byte buffer of
// stderr + stdout, it scans these for lines and returns a slice of output lines
func CombinedOutputLines(cmd Cmd) (lines []string, err error) {
var buff bytes.Buffer
cmd.SetStdout(&buff)
cmd.SetStderr(&buff)
err = cmd.Run()
scanner := bufio.NewScanner(&buff)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, err
}
// OutputLines is like os/exec's cmd.Output(),
// but over our Cmd interface, and instead of returning the byte buffer of
// stdout, it scans these for lines and returns a slice of output lines
func OutputLines(cmd Cmd) (lines []string, err error) {
var buff bytes.Buffer
cmd.SetStdout(&buff)
err = cmd.Run()
scanner := bufio.NewScanner(&buff)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, err
}
// Output is like os/exec's cmd.Output, but over our Cmd interface
func Output(cmd Cmd) ([]byte, error) {
var buff bytes.Buffer
cmd.SetStdout(&buff)
err := cmd.Run()
return buff.Bytes(), err
}
// InheritOutput sets cmd's output to write to the current process's stdout and stderr
func InheritOutput(cmd Cmd) Cmd {
cmd.SetStderr(os.Stderr)
cmd.SetStdout(os.Stdout)
return cmd
}
// RunWithStdoutReader runs cmd with stdout piped to readerFunc
func RunWithStdoutReader(cmd Cmd, readerFunc func(io.Reader) error) error {
pr, pw, err := os.Pipe()
if err != nil {
return err
}
cmd.SetStdout(pw)
return errors.AggregateConcurrent([]func() error{
func() error {
defer pr.Close()
return readerFunc(pr)
},
func() error {
defer pw.Close()
return cmd.Run()
},
})
}
// RunWithStdinWriter runs cmd with writerFunc piped to stdin
func RunWithStdinWriter(cmd Cmd, writerFunc func(io.Writer) error) error {
pr, pw, err := os.Pipe()
if err != nil {
return err
}
cmd.SetStdin(pr)
return errors.AggregateConcurrent([]func() error{
func() error {
defer pw.Close()
return writerFunc(pw)
},
func() error {
defer pr.Close()
return cmd.Run()
},
})
}
|