cli.go 6.3 KB

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