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