github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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 return Docker(Build(name), cmdOperators...).Assert(t, icmd.Success) 36 } 37 38 // InspectCmd executes the specified docker inspect command and expect a success 39 func InspectCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result { 40 return Docker(Inspect(name), cmdOperators...).Assert(t, icmd.Success) 41 } 42 43 // WaitRun will wait for the specified container to be running, maximum 5 seconds. 44 func WaitRun(t testing.TB, name string, cmdOperators ...CmdOperator) { 45 WaitForInspectResult(t, name, "{{.State.Running}}", "true", 5*time.Second, cmdOperators...) 46 } 47 48 // WaitExited will wait for the specified container to state exit, subject 49 // to a maximum time limit in seconds supplied by the caller 50 func WaitExited(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) { 51 WaitForInspectResult(t, name, "{{.State.Status}}", "exited", timeout, cmdOperators...) 52 } 53 54 // WaitRestart will wait for the specified container to restart once 55 func WaitRestart(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) { 56 WaitForInspectResult(t, name, "{{.RestartCount}}", "1", timeout, cmdOperators...) 57 } 58 59 // WaitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time. 60 func WaitForInspectResult(t testing.TB, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) { 61 after := time.After(timeout) 62 63 args := []string{"inspect", "-f", expr, name} 64 for { 65 result := Docker(Args(args...), cmdOperators...) 66 if result.Error != nil { 67 if !strings.Contains(strings.ToLower(result.Stderr()), "no such") { 68 t.Fatalf("error executing docker inspect: %v\n%s", 69 result.Stderr(), result.Stdout()) 70 } 71 select { 72 case <-after: 73 t.Fatal(result.Error) 74 default: 75 time.Sleep(10 * time.Millisecond) 76 continue 77 } 78 } 79 80 out := strings.TrimSpace(result.Stdout()) 81 if out == expected { 82 break 83 } 84 85 select { 86 case <-after: 87 t.Fatalf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout) 88 default: 89 } 90 91 time.Sleep(100 * time.Millisecond) 92 } 93 } 94 95 // Docker executes the specified docker command 96 func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result { 97 for _, op := range cmdOperators { 98 deferFn := op(&cmd) 99 if deferFn != nil { 100 defer deferFn() 101 } 102 } 103 appendDocker(&cmd) 104 if err := validateArgs(cmd.Command...); err != nil { 105 return &icmd.Result{ 106 Error: err, 107 } 108 } 109 return icmd.RunCmd(cmd) 110 } 111 112 // validateArgs is a checker to ensure tests are not running commands which are 113 // not supported on platforms. Specifically on Windows this is 'busybox top'. 114 func validateArgs(args ...string) error { 115 if testEnv.OSType != "windows" { 116 return nil 117 } 118 foundBusybox := -1 119 for key, value := range args { 120 if strings.ToLower(value) == "busybox" { 121 foundBusybox = key 122 } 123 if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") { 124 return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()") 125 } 126 } 127 return nil 128 } 129 130 // Build executes the specified docker build command 131 func Build(name string) icmd.Cmd { 132 return icmd.Command("build", "-t", name) 133 } 134 135 // Inspect executes the specified docker inspect command 136 func Inspect(name string) icmd.Cmd { 137 return icmd.Command("inspect", name) 138 } 139 140 // Format sets the specified format with --format flag 141 func Format(format string) func(*icmd.Cmd) func() { 142 return func(cmd *icmd.Cmd) func() { 143 cmd.Command = append( 144 []string{cmd.Command[0]}, 145 append([]string{"--format", fmt.Sprintf("{{%s}}", format)}, cmd.Command[1:]...)..., 146 ) 147 return nil 148 } 149 } 150 151 func appendDocker(cmd *icmd.Cmd) { 152 cmd.Command = append([]string{testEnv.DockerBinary()}, cmd.Command...) 153 } 154 155 // Args build an icmd.Cmd struct from the specified arguments 156 func Args(args ...string) icmd.Cmd { 157 switch len(args) { 158 case 0: 159 return icmd.Cmd{} 160 case 1: 161 return icmd.Command(args[0]) 162 default: 163 return icmd.Command(args[0], args[1:]...) 164 } 165 } 166 167 // Daemon points to the specified daemon 168 func Daemon(d *daemon.Daemon) func(*icmd.Cmd) func() { 169 return func(cmd *icmd.Cmd) func() { 170 cmd.Command = append([]string{"--host", d.Sock()}, cmd.Command...) 171 return nil 172 } 173 } 174 175 // WithTimeout sets the timeout for the command to run 176 func WithTimeout(timeout time.Duration) func(cmd *icmd.Cmd) func() { 177 return func(cmd *icmd.Cmd) func() { 178 cmd.Timeout = timeout 179 return nil 180 } 181 } 182 183 // WithEnvironmentVariables sets the specified environment variables for the command to run 184 func WithEnvironmentVariables(envs ...string) func(cmd *icmd.Cmd) func() { 185 return func(cmd *icmd.Cmd) func() { 186 cmd.Env = envs 187 return nil 188 } 189 } 190 191 // WithFlags sets the specified flags for the command to run 192 func WithFlags(flags ...string) func(*icmd.Cmd) func() { 193 return func(cmd *icmd.Cmd) func() { 194 cmd.Command = append(cmd.Command, flags...) 195 return nil 196 } 197 } 198 199 // InDir sets the folder in which the command should be executed 200 func InDir(path string) func(*icmd.Cmd) func() { 201 return func(cmd *icmd.Cmd) func() { 202 cmd.Dir = path 203 return nil 204 } 205 } 206 207 // WithStdout sets the standard output writer of the command 208 func WithStdout(writer io.Writer) func(*icmd.Cmd) func() { 209 return func(cmd *icmd.Cmd) func() { 210 cmd.Stdout = writer 211 return nil 212 } 213 } 214 215 // WithStdin sets the standard input reader for the command 216 func WithStdin(stdin io.Reader) func(*icmd.Cmd) func() { 217 return func(cmd *icmd.Cmd) func() { 218 cmd.Stdin = stdin 219 return nil 220 } 221 }