github.com/rawahars/moby@v24.0.4+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 return Docker(Args("build", "-t", 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(Args("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 // waitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time. 55 func waitForInspectResult(t testing.TB, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) { 56 after := time.After(timeout) 57 58 args := []string{"inspect", "-f", expr, name} 59 for { 60 result := Docker(Args(args...), cmdOperators...) 61 if result.Error != nil { 62 if !strings.Contains(strings.ToLower(result.Stderr()), "no such") { 63 t.Fatalf("error executing docker inspect: %v\n%s", 64 result.Stderr(), result.Stdout()) 65 } 66 select { 67 case <-after: 68 t.Fatal(result.Error) 69 default: 70 time.Sleep(10 * time.Millisecond) 71 continue 72 } 73 } 74 75 out := strings.TrimSpace(result.Stdout()) 76 if out == expected { 77 break 78 } 79 80 select { 81 case <-after: 82 t.Fatalf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout) 83 default: 84 } 85 86 time.Sleep(100 * time.Millisecond) 87 } 88 } 89 90 // Docker executes the specified docker command 91 func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result { 92 for _, op := range cmdOperators { 93 deferFn := op(&cmd) 94 if deferFn != nil { 95 defer deferFn() 96 } 97 } 98 cmd.Command = append([]string{testEnv.DockerBinary()}, cmd.Command...) 99 if err := validateArgs(cmd.Command...); err != nil { 100 return &icmd.Result{ 101 Error: err, 102 } 103 } 104 return icmd.RunCmd(cmd) 105 } 106 107 // validateArgs is a checker to ensure tests are not running commands which are 108 // not supported on platforms. Specifically on Windows this is 'busybox top'. 109 func validateArgs(args ...string) error { 110 if testEnv.OSType != "windows" { 111 return nil 112 } 113 foundBusybox := -1 114 for key, value := range args { 115 if strings.ToLower(value) == "busybox" { 116 foundBusybox = key 117 } 118 if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") { 119 return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()") 120 } 121 } 122 return nil 123 } 124 125 // Format sets the specified format with --format flag 126 func Format(format string) func(*icmd.Cmd) func() { 127 return func(cmd *icmd.Cmd) func() { 128 cmd.Command = append( 129 []string{cmd.Command[0]}, 130 append([]string{"--format", fmt.Sprintf("{{%s}}", format)}, cmd.Command[1:]...)..., 131 ) 132 return nil 133 } 134 } 135 136 // Args build an icmd.Cmd struct from the specified (command and) arguments. 137 func Args(commandAndArgs ...string) icmd.Cmd { 138 return icmd.Cmd{Command: commandAndArgs} 139 } 140 141 // Daemon points to the specified daemon 142 func Daemon(d *daemon.Daemon) func(*icmd.Cmd) func() { 143 return func(cmd *icmd.Cmd) func() { 144 cmd.Command = append([]string{"--host", d.Sock()}, cmd.Command...) 145 return nil 146 } 147 } 148 149 // WithTimeout sets the timeout for the command to run 150 func WithTimeout(timeout time.Duration) func(cmd *icmd.Cmd) func() { 151 return func(cmd *icmd.Cmd) func() { 152 cmd.Timeout = timeout 153 return nil 154 } 155 } 156 157 // WithEnvironmentVariables sets the specified environment variables for the command to run 158 func WithEnvironmentVariables(envs ...string) func(cmd *icmd.Cmd) func() { 159 return func(cmd *icmd.Cmd) func() { 160 cmd.Env = envs 161 return nil 162 } 163 } 164 165 // WithFlags sets the specified flags for the command to run 166 func WithFlags(flags ...string) func(*icmd.Cmd) func() { 167 return func(cmd *icmd.Cmd) func() { 168 cmd.Command = append(cmd.Command, flags...) 169 return nil 170 } 171 } 172 173 // InDir sets the folder in which the command should be executed 174 func InDir(path string) func(*icmd.Cmd) func() { 175 return func(cmd *icmd.Cmd) func() { 176 cmd.Dir = path 177 return nil 178 } 179 } 180 181 // WithStdout sets the standard output writer of the command 182 func WithStdout(writer io.Writer) func(*icmd.Cmd) func() { 183 return func(cmd *icmd.Cmd) func() { 184 cmd.Stdout = writer 185 return nil 186 } 187 } 188 189 // WithStdin sets the standard input reader for the command 190 func WithStdin(stdin io.Reader) func(*icmd.Cmd) func() { 191 return func(cmd *icmd.Cmd) func() { 192 cmd.Stdin = stdin 193 return nil 194 } 195 }