storage.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package fakestorage // import "github.com/docker/docker/testutil/fakestorage"
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/http/httptest"
  8. "net/url"
  9. "os"
  10. "strings"
  11. "testing"
  12. "github.com/docker/docker/api/types"
  13. containertypes "github.com/docker/docker/api/types/container"
  14. "github.com/docker/docker/client"
  15. "github.com/docker/docker/testutil"
  16. "github.com/docker/docker/testutil/environment"
  17. "github.com/docker/docker/testutil/fakecontext"
  18. "github.com/docker/docker/testutil/request"
  19. "github.com/docker/go-connections/nat"
  20. "gotest.tools/v3/assert"
  21. )
  22. var testEnv *environment.Execution
  23. // Fake is a static file server. It might be running locally or remotely
  24. // on test host.
  25. type Fake interface {
  26. Close() error
  27. URL() string
  28. CtxDir() string
  29. }
  30. // SetTestEnvironment sets a static test environment
  31. // TODO: decouple this package from environment
  32. func SetTestEnvironment(env *environment.Execution) {
  33. testEnv = env
  34. }
  35. // New returns a static file server that will be use as build context.
  36. func New(t testing.TB, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
  37. t.Helper()
  38. if testEnv == nil {
  39. t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
  40. }
  41. ctx := fakecontext.New(t, dir, modifiers...)
  42. switch {
  43. case testEnv.IsRemoteDaemon() && strings.HasPrefix(request.DaemonHost(), "unix:///"):
  44. t.Skip("e2e run : daemon is remote but docker host points to a unix socket")
  45. case testEnv.IsLocalDaemon():
  46. return newLocalFakeStorage(ctx)
  47. default:
  48. return newRemoteFileServer(t, ctx, testEnv.APIClient())
  49. }
  50. return nil
  51. }
  52. // localFileStorage is a file storage on the running machine
  53. type localFileStorage struct {
  54. *fakecontext.Fake
  55. *httptest.Server
  56. }
  57. func (s *localFileStorage) URL() string {
  58. return s.Server.URL
  59. }
  60. func (s *localFileStorage) CtxDir() string {
  61. return s.Fake.Dir
  62. }
  63. func (s *localFileStorage) Close() error {
  64. defer s.Server.Close()
  65. return s.Fake.Close()
  66. }
  67. func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage {
  68. handler := http.FileServer(http.Dir(ctx.Dir))
  69. server := httptest.NewServer(handler)
  70. return &localFileStorage{
  71. Fake: ctx,
  72. Server: server,
  73. }
  74. }
  75. // remoteFileServer is a containerized static file server started on the remote
  76. // testing machine to be used in URL-accepting docker build functionality.
  77. type remoteFileServer struct {
  78. host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
  79. container string
  80. image string
  81. client client.APIClient
  82. ctx *fakecontext.Fake
  83. }
  84. func (f *remoteFileServer) URL() string {
  85. u := url.URL{
  86. Scheme: "http",
  87. Host: f.host,
  88. }
  89. return u.String()
  90. }
  91. func (f *remoteFileServer) CtxDir() string {
  92. return f.ctx.Dir
  93. }
  94. func (f *remoteFileServer) Close() error {
  95. defer func() {
  96. if f.ctx != nil {
  97. f.ctx.Close()
  98. }
  99. if f.image != "" {
  100. if _, err := f.client.ImageRemove(context.Background(), f.image, types.ImageRemoveOptions{
  101. Force: true,
  102. }); err != nil {
  103. fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
  104. }
  105. }
  106. if err := f.client.Close(); err != nil {
  107. fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
  108. }
  109. }()
  110. if f.container == "" {
  111. return nil
  112. }
  113. return f.client.ContainerRemove(context.Background(), f.container, containertypes.RemoveOptions{
  114. Force: true,
  115. RemoveVolumes: true,
  116. })
  117. }
  118. func newRemoteFileServer(t testing.TB, ctx *fakecontext.Fake, c client.APIClient) *remoteFileServer {
  119. var (
  120. image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10)))
  121. container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10)))
  122. )
  123. ensureHTTPServerImage(t)
  124. // Build the image
  125. if err := ctx.Add("Dockerfile", `FROM httpserver
  126. COPY . /static`); err != nil {
  127. t.Fatal(err)
  128. }
  129. resp, err := c.ImageBuild(context.Background(), ctx.AsTarReader(t), types.ImageBuildOptions{
  130. NoCache: true,
  131. Tags: []string{image},
  132. })
  133. assert.NilError(t, err)
  134. _, err = io.Copy(io.Discard, resp.Body)
  135. assert.NilError(t, err)
  136. // Start the container
  137. b, err := c.ContainerCreate(context.Background(), &containertypes.Config{
  138. Image: image,
  139. }, &containertypes.HostConfig{}, nil, nil, container)
  140. assert.NilError(t, err)
  141. err = c.ContainerStart(context.Background(), b.ID, containertypes.StartOptions{})
  142. assert.NilError(t, err)
  143. // Find out the system assigned port
  144. i, err := c.ContainerInspect(context.Background(), b.ID)
  145. assert.NilError(t, err)
  146. newP, err := nat.NewPort("tcp", "80")
  147. assert.NilError(t, err)
  148. ports, exists := i.NetworkSettings.Ports[newP]
  149. if !exists || len(ports) != 1 {
  150. t.Fatalf("unable to find port 80/tcp for %s", container)
  151. }
  152. host := ports[0].HostIP
  153. port := ports[0].HostPort
  154. return &remoteFileServer{
  155. container: container,
  156. image: image,
  157. host: fmt.Sprintf("%s:%s", host, port),
  158. ctx: ctx,
  159. client: c,
  160. }
  161. }