storage.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package fakestorage
  2. import (
  3. "fmt"
  4. "net"
  5. "net/http"
  6. "net/http/httptest"
  7. "net/url"
  8. "os"
  9. "strings"
  10. "sync"
  11. "github.com/docker/docker/integration-cli/cli"
  12. "github.com/docker/docker/integration-cli/cli/build"
  13. "github.com/docker/docker/integration-cli/cli/build/fakecontext"
  14. "github.com/docker/docker/integration-cli/environment"
  15. "github.com/docker/docker/integration-cli/request"
  16. "github.com/docker/docker/pkg/stringutils"
  17. )
  18. var (
  19. testEnv *environment.Execution
  20. onlyOnce sync.Once
  21. )
  22. // EnsureTestEnvIsLoaded make sure the test environment is loaded for this package
  23. func EnsureTestEnvIsLoaded(t testingT) {
  24. var doIt bool
  25. var err error
  26. onlyOnce.Do(func() {
  27. doIt = true
  28. })
  29. if !doIt {
  30. return
  31. }
  32. testEnv, err = environment.New()
  33. if err != nil {
  34. t.Fatalf("error loading testenv : %v", err)
  35. }
  36. }
  37. type testingT interface {
  38. logT
  39. Fatal(args ...interface{})
  40. Fatalf(string, ...interface{})
  41. }
  42. type logT interface {
  43. Logf(string, ...interface{})
  44. }
  45. // Fake is a static file server. It might be running locally or remotely
  46. // on test host.
  47. type Fake interface {
  48. Close() error
  49. URL() string
  50. CtxDir() string
  51. }
  52. // New returns a static file server that will be use as build context.
  53. func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
  54. ctx := fakecontext.New(t, dir, modifiers...)
  55. if testEnv.LocalDaemon() {
  56. return newLocalFakeStorage(t, ctx)
  57. }
  58. return newRemoteFileServer(t, ctx)
  59. }
  60. // localFileStorage is a file storage on the running machine
  61. type localFileStorage struct {
  62. *fakecontext.Fake
  63. *httptest.Server
  64. }
  65. func (s *localFileStorage) URL() string {
  66. return s.Server.URL
  67. }
  68. func (s *localFileStorage) CtxDir() string {
  69. return s.Fake.Dir
  70. }
  71. func (s *localFileStorage) Close() error {
  72. defer s.Server.Close()
  73. return s.Fake.Close()
  74. }
  75. func newLocalFakeStorage(t testingT, ctx *fakecontext.Fake) *localFileStorage {
  76. handler := http.FileServer(http.Dir(ctx.Dir))
  77. server := httptest.NewServer(handler)
  78. return &localFileStorage{
  79. Fake: ctx,
  80. Server: server,
  81. }
  82. }
  83. // remoteFileServer is a containerized static file server started on the remote
  84. // testing machine to be used in URL-accepting docker build functionality.
  85. type remoteFileServer struct {
  86. host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
  87. container string
  88. image string
  89. ctx *fakecontext.Fake
  90. }
  91. func (f *remoteFileServer) URL() string {
  92. u := url.URL{
  93. Scheme: "http",
  94. Host: f.host}
  95. return u.String()
  96. }
  97. func (f *remoteFileServer) CtxDir() string {
  98. return f.ctx.Dir
  99. }
  100. func (f *remoteFileServer) Close() error {
  101. defer func() {
  102. if f.ctx != nil {
  103. f.ctx.Close()
  104. }
  105. if f.image != "" {
  106. if err := cli.Docker(cli.Args("rmi", "-f", f.image)).Error; err != nil {
  107. fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
  108. }
  109. }
  110. }()
  111. if f.container == "" {
  112. return nil
  113. }
  114. return cli.Docker(cli.Args("rm", "-fv", f.container)).Error
  115. }
  116. func newRemoteFileServer(t testingT, ctx *fakecontext.Fake) *remoteFileServer {
  117. var (
  118. image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
  119. container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
  120. )
  121. ensureHTTPServerImage(t)
  122. // Build the image
  123. if err := ctx.Add("Dockerfile", `FROM httpserver
  124. COPY . /static`); err != nil {
  125. t.Fatal(err)
  126. }
  127. cli.BuildCmd(t, image, build.WithoutCache, build.WithExternalBuildContext(ctx))
  128. // Start the container
  129. cli.DockerCmd(t, "run", "-d", "-P", "--name", container, image)
  130. // Find out the system assigned port
  131. out := cli.DockerCmd(t, "port", container, "80/tcp").Combined()
  132. fileserverHostPort := strings.Trim(out, "\n")
  133. _, port, err := net.SplitHostPort(fileserverHostPort)
  134. if err != nil {
  135. t.Fatalf("unable to parse file server host:port: %v", err)
  136. }
  137. dockerHostURL, err := url.Parse(request.DaemonHost())
  138. if err != nil {
  139. t.Fatalf("unable to parse daemon host URL: %v", err)
  140. }
  141. host, _, err := net.SplitHostPort(dockerHostURL.Host)
  142. if err != nil {
  143. t.Fatalf("unable to parse docker daemon host:port: %v", err)
  144. }
  145. return &remoteFileServer{
  146. container: container,
  147. image: image,
  148. host: fmt.Sprintf("%s:%s", host, port),
  149. ctx: ctx}
  150. }