github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/testutil/fakestorage/storage.go (about) 1 package fakestorage // import "github.com/docker/docker/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/docker/docker/api/types" 15 containertypes "github.com/docker/docker/api/types/container" 16 "github.com/docker/docker/client" 17 "github.com/docker/docker/testutil" 18 "github.com/docker/docker/testutil/environment" 19 "github.com/docker/docker/testutil/fakecontext" 20 "github.com/docker/docker/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 return u.String() 102 } 103 104 func (f *remoteFileServer) CtxDir() string { 105 return f.ctx.Dir 106 } 107 108 func (f *remoteFileServer) Close() error { 109 defer func() { 110 if f.ctx != nil { 111 f.ctx.Close() 112 } 113 if f.image != "" { 114 if _, err := f.client.ImageRemove(context.Background(), f.image, types.ImageRemoveOptions{ 115 Force: true, 116 }); err != nil { 117 fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err) 118 } 119 } 120 if err := f.client.Close(); err != nil { 121 fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err) 122 } 123 }() 124 if f.container == "" { 125 return nil 126 } 127 return f.client.ContainerRemove(context.Background(), f.container, types.ContainerRemoveOptions{ 128 Force: true, 129 RemoveVolumes: true, 130 }) 131 } 132 133 func newRemoteFileServer(t testing.TB, ctx *fakecontext.Fake, c client.APIClient) *remoteFileServer { 134 var ( 135 image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10))) 136 container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10))) 137 ) 138 139 ensureHTTPServerImage(t) 140 141 // Build the image 142 if err := ctx.Add("Dockerfile", `FROM httpserver 143 COPY . /static`); err != nil { 144 t.Fatal(err) 145 } 146 resp, err := c.ImageBuild(context.Background(), ctx.AsTarReader(t), types.ImageBuildOptions{ 147 NoCache: true, 148 Tags: []string{image}, 149 }) 150 assert.NilError(t, err) 151 _, err = io.Copy(io.Discard, resp.Body) 152 assert.NilError(t, err) 153 154 // Start the container 155 b, err := c.ContainerCreate(context.Background(), &containertypes.Config{ 156 Image: image, 157 }, &containertypes.HostConfig{}, nil, nil, container) 158 assert.NilError(t, err) 159 err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{}) 160 assert.NilError(t, err) 161 162 // Find out the system assigned port 163 i, err := c.ContainerInspect(context.Background(), b.ID) 164 assert.NilError(t, err) 165 newP, err := nat.NewPort("tcp", "80") 166 assert.NilError(t, err) 167 ports, exists := i.NetworkSettings.Ports[newP] 168 if !exists || len(ports) != 1 { 169 t.Fatalf("unable to find port 80/tcp for %s", container) 170 } 171 host := ports[0].HostIP 172 port := ports[0].HostPort 173 174 return &remoteFileServer{ 175 container: container, 176 image: image, 177 host: fmt.Sprintf("%s:%s", host, port), 178 ctx: ctx, 179 client: c, 180 } 181 }