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