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