github.com/moby/docker@v26.1.3+incompatible/integration-cli/cli/cli.go (about) 1 package cli // import "github.com/docker/docker/integration-cli/cli" 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/docker/docker/integration-cli/daemon" 11 "github.com/docker/docker/integration-cli/environment" 12 "github.com/pkg/errors" 13 "gotest.tools/v3/icmd" 14 ) 15 16 var testEnv *environment.Execution 17 18 // SetTestEnvironment sets a static test environment 19 // TODO: decouple this package from environment 20 func SetTestEnvironment(env *environment.Execution) { 21 testEnv = env 22 } 23 24 // CmdOperator defines functions that can modify a command 25 type CmdOperator func(*icmd.Cmd) func() 26 27 // DockerCmd executes the specified docker command and expect a success 28 func DockerCmd(t testing.TB, args ...string) *icmd.Result { 29 t.Helper() 30 return Docker(Args(args...)).Assert(t, icmd.Success) 31 } 32 33 // BuildCmd executes the specified docker build command and expect a success 34 func BuildCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result { 35 t.Helper() 36 return Docker(Args("build", "-t", name), cmdOperators...).Assert(t, icmd.Success) 37 } 38 39 // InspectCmd executes the specified docker inspect command and expect a success 40 func InspectCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result { 41 t.Helper() 42 return Docker(Args("inspect", name), cmdOperators...).Assert(t, icmd.Success) 43 } 44 45 // WaitRun will wait for the specified container to be running, maximum 5 seconds. 46 func WaitRun(t testing.TB, name string, cmdOperators ...CmdOperator) { 47 t.Helper() 48 waitForInspectResult(t, name, "{{.State.Running}}", "true", 5*time.Second, cmdOperators...) 49 } 50 51 // WaitExited will wait for the specified container to state exit, subject 52 // to a maximum time limit in seconds supplied by the caller 53 func WaitExited(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) { 54 t.Helper() 55 waitForInspectResult(t, name, "{{.State.Status}}", "exited", timeout, cmdOperators...) 56 } 57 58 // waitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time. 59 func waitForInspectResult(t testing.TB, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) { 60 after := time.After(timeout) 61 62 args := []string{"inspect", "-f", expr, name} 63 for { 64 result := Docker(Args(args...), cmdOperators...) 65 if result.Error != nil { 66 if !strings.Contains(strings.ToLower(result.Stderr()), "no such") { 67 t.Fatalf("error executing docker inspect: %v\n%s", 68 result.Stderr(), result.Stdout()) 69 } 70 select { 71 case <-after: 72 t.Fatal(result.Error) 73 default: 74 time.Sleep(10 * time.Millisecond) 75 continue 76 } 77 } 78 79 out := strings.TrimSpace(result.Stdout()) 80 if out == expected { 81 break 82 } 83 84 select { 85 case <-after: 86 t.Fatalf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout) 87 default: 88 } 89 90 time.Sleep(100 * time.Millisecond) 91 } 92 } 93 94 // Docker executes the specified docker command 95 func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result { 96 for _, op := range cmdOperators { 97 deferFn := op(&cmd) 98 if deferFn != nil { 99 defer deferFn() 100 } 101 } 102 cmd.Command = append([]string{testEnv.DockerBinary()}, cmd.Command...) 103 if err := validateArgs(cmd.Command...); err != nil { 104 return &icmd.Result{ 105 Error: err, 106 } 107 } 108 return icmd.RunCmd(cmd) 109 } 110 111 // validateArgs is a checker to ensure tests are not running commands which are 112 // not supported on platforms. Specifically on Windows this is 'busybox top'. 113 func validateArgs(args ...string) error { 114 if testEnv.DaemonInfo.OSType != "windows" { 115 return nil 116 } 117 foundBusybox := -1 118 for key, value := range args { 119 if strings.ToLower(value) == "busybox" { 120 foundBusybox = key 121 } 122 if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") { 123 return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()") 124 } 125 } 126 return nil 127 } 128 129 // Format sets the specified format with --format flag 130 func Format(format string) func(*icmd.Cmd) func() { 131 return func(cmd *icmd.Cmd) func() { 132 cmd.Command = append( 133 []string{cmd.Command[0]}, 134 append([]string{"--format", fmt.Sprintf("{{%s}}", format)}, cmd.Command[1:]...)..., 135 ) 136 return nil 137 } 138 } 139 140 // Args build an icmd.Cmd struct from the specified (command and) arguments. 141 func Args(commandAndArgs ...string) icmd.Cmd { 142 return icmd.Cmd{Command: commandAndArgs} 143 } 144 145 // Daemon points to the specified daemon 146 func Daemon(d *daemon.Daemon) func(*icmd.Cmd) func() { 147 return func(cmd *icmd.Cmd) func() { 148 cmd.Command = append([]string{"--host", d.Sock()}, cmd.Command...) 149 return nil 150 } 151 } 152 153 // WithTimeout sets the timeout for the command to run 154 func WithTimeout(timeout time.Duration) func(cmd *icmd.Cmd) func() { 155 return func(cmd *icmd.Cmd) func() { 156 cmd.Timeout = timeout 157 return nil 158 } 159 } 160 161 // WithEnvironmentVariables sets the specified environment variables for the command to run 162 func WithEnvironmentVariables(envs ...string) func(cmd *icmd.Cmd) func() { 163 return func(cmd *icmd.Cmd) func() { 164 cmd.Env = envs 165 return nil 166 } 167 } 168 169 // WithFlags sets the specified flags for the command to run 170 func WithFlags(flags ...string) func(*icmd.Cmd) func() { 171 return func(cmd *icmd.Cmd) func() { 172 cmd.Command = append(cmd.Command, flags...) 173 return nil 174 } 175 } 176 177 // InDir sets the folder in which the command should be executed 178 func InDir(path string) func(*icmd.Cmd) func() { 179 return func(cmd *icmd.Cmd) func() { 180 cmd.Dir = path 181 return nil 182 } 183 } 184 185 // WithStdout sets the standard output writer of the command 186 func WithStdout(writer io.Writer) func(*icmd.Cmd) func() { 187 return func(cmd *icmd.Cmd) func() { 188 cmd.Stdout = writer 189 return nil 190 } 191 } 192 193 // WithStdin sets the standard input reader for the command 194 func WithStdin(stdin io.Reader) func(*icmd.Cmd) func() { 195 return func(cmd *icmd.Cmd) func() { 196 cmd.Stdin = stdin 197 return nil 198 } 199 }