cli.go 6.4 KB

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