cli.go 6.3 KB

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