-
Notifications
You must be signed in to change notification settings - Fork 18.6k
/
cli.go
221 lines (191 loc) · 6.25 KB
/
cli.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package cli // import "github.com/docker/docker/integration-cli/cli"
import (
"fmt"
"io"
"strings"
"testing"
"time"
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/environment"
"github.com/pkg/errors"
"gotest.tools/v3/icmd"
)
var testEnv *environment.Execution
// SetTestEnvironment sets a static test environment
// TODO: decouple this package from environment
func SetTestEnvironment(env *environment.Execution) {
testEnv = env
}
// CmdOperator defines functions that can modify a command
type CmdOperator func(*icmd.Cmd) func()
// DockerCmd executes the specified docker command and expect a success
func DockerCmd(t testing.TB, args ...string) *icmd.Result {
t.Helper()
return Docker(Args(args...)).Assert(t, icmd.Success)
}
// BuildCmd executes the specified docker build command and expect a success
func BuildCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result {
return Docker(Build(name), cmdOperators...).Assert(t, icmd.Success)
}
// InspectCmd executes the specified docker inspect command and expect a success
func InspectCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result {
return Docker(Inspect(name), cmdOperators...).Assert(t, icmd.Success)
}
// WaitRun will wait for the specified container to be running, maximum 5 seconds.
func WaitRun(t testing.TB, name string, cmdOperators ...CmdOperator) {
WaitForInspectResult(t, name, "{{.State.Running}}", "true", 5*time.Second, cmdOperators...)
}
// WaitExited will wait for the specified container to state exit, subject
// to a maximum time limit in seconds supplied by the caller
func WaitExited(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
WaitForInspectResult(t, name, "{{.State.Status}}", "exited", timeout, cmdOperators...)
}
// WaitRestart will wait for the specified container to restart once
func WaitRestart(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
WaitForInspectResult(t, name, "{{.RestartCount}}", "1", timeout, cmdOperators...)
}
// WaitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time.
func WaitForInspectResult(t testing.TB, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) {
after := time.After(timeout)
args := []string{"inspect", "-f", expr, name}
for {
result := Docker(Args(args...), cmdOperators...)
if result.Error != nil {
if !strings.Contains(strings.ToLower(result.Stderr()), "no such") {
t.Fatalf("error executing docker inspect: %v\n%s",
result.Stderr(), result.Stdout())
}
select {
case <-after:
t.Fatal(result.Error)
default:
time.Sleep(10 * time.Millisecond)
continue
}
}
out := strings.TrimSpace(result.Stdout())
if out == expected {
break
}
select {
case <-after:
t.Fatalf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout)
default:
}
time.Sleep(100 * time.Millisecond)
}
}
// Docker executes the specified docker command
func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result {
for _, op := range cmdOperators {
deferFn := op(&cmd)
if deferFn != nil {
defer deferFn()
}
}
appendDocker(&cmd)
if err := validateArgs(cmd.Command...); err != nil {
return &icmd.Result{
Error: err,
}
}
return icmd.RunCmd(cmd)
}
// validateArgs is a checker to ensure tests are not running commands which are
// not supported on platforms. Specifically on Windows this is 'busybox top'.
func validateArgs(args ...string) error {
if testEnv.OSType != "windows" {
return nil
}
foundBusybox := -1
for key, value := range args {
if strings.ToLower(value) == "busybox" {
foundBusybox = key
}
if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") {
return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()")
}
}
return nil
}
// Build executes the specified docker build command
func Build(name string) icmd.Cmd {
return icmd.Command("build", "-t", name)
}
// Inspect executes the specified docker inspect command
func Inspect(name string) icmd.Cmd {
return icmd.Command("inspect", name)
}
// Format sets the specified format with --format flag
func Format(format string) func(*icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Command = append(
[]string{cmd.Command[0]},
append([]string{"--format", fmt.Sprintf("{{%s}}", format)}, cmd.Command[1:]...)...,
)
return nil
}
}
func appendDocker(cmd *icmd.Cmd) {
cmd.Command = append([]string{testEnv.DockerBinary()}, cmd.Command...)
}
// Args build an icmd.Cmd struct from the specified arguments
func Args(args ...string) icmd.Cmd {
switch len(args) {
case 0:
return icmd.Cmd{}
case 1:
return icmd.Command(args[0])
default:
return icmd.Command(args[0], args[1:]...)
}
}
// Daemon points to the specified daemon
func Daemon(d *daemon.Daemon) func(*icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Command = append([]string{"--host", d.Sock()}, cmd.Command...)
return nil
}
}
// WithTimeout sets the timeout for the command to run
func WithTimeout(timeout time.Duration) func(cmd *icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Timeout = timeout
return nil
}
}
// WithEnvironmentVariables sets the specified environment variables for the command to run
func WithEnvironmentVariables(envs ...string) func(cmd *icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Env = envs
return nil
}
}
// WithFlags sets the specified flags for the command to run
func WithFlags(flags ...string) func(*icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Command = append(cmd.Command, flags...)
return nil
}
}
// InDir sets the folder in which the command should be executed
func InDir(path string) func(*icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Dir = path
return nil
}
}
// WithStdout sets the standard output writer of the command
func WithStdout(writer io.Writer) func(*icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Stdout = writer
return nil
}
}
// WithStdin sets the standard input reader for the command
func WithStdin(stdin io.Reader) func(*icmd.Cmd) func() {
return func(cmd *icmd.Cmd) func() {
cmd.Stdin = stdin
return nil
}
}