forked from kubernetes-sigs/kind
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhelpers.go
134 lines (118 loc) · 3.28 KB
/
helpers.go
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
/*
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
}
// 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()
},
})
}