| package fakestorage |
| |
| import ( |
| "fmt" |
| "net" |
| "net/http" |
| "net/http/httptest" |
| "net/url" |
| "os" |
| "strings" |
| |
| "github.com/docker/docker/integration-cli/cli" |
| "github.com/docker/docker/integration-cli/cli/build" |
| "github.com/docker/docker/integration-cli/cli/build/fakecontext" |
| "github.com/docker/docker/integration-cli/request" |
| "github.com/docker/docker/internal/test/environment" |
| "github.com/docker/docker/internal/testutil" |
| "github.com/stretchr/testify/require" |
| ) |
| |
| var testEnv *environment.Execution |
| |
| type testingT interface { |
| require.TestingT |
| logT |
| Fatal(args ...interface{}) |
| Fatalf(string, ...interface{}) |
| } |
| |
| type logT interface { |
| Logf(string, ...interface{}) |
| } |
| |
| // Fake is a static file server. It might be running locally or remotely |
| // on test host. |
| type Fake interface { |
| Close() error |
| URL() string |
| CtxDir() string |
| } |
| |
| // SetTestEnvironment sets a static test environment |
| // TODO: decouple this package from environment |
| func SetTestEnvironment(env *environment.Execution) { |
| testEnv = env |
| } |
| |
| // New returns a static file server that will be use as build context. |
| func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake { |
| if testEnv == nil { |
| t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.") |
| } |
| ctx := fakecontext.New(t, dir, modifiers...) |
| if testEnv.IsLocalDaemon() { |
| return newLocalFakeStorage(ctx) |
| } |
| return newRemoteFileServer(t, ctx) |
| } |
| |
| // localFileStorage is a file storage on the running machine |
| type localFileStorage struct { |
| *fakecontext.Fake |
| *httptest.Server |
| } |
| |
| func (s *localFileStorage) URL() string { |
| return s.Server.URL |
| } |
| |
| func (s *localFileStorage) CtxDir() string { |
| return s.Fake.Dir |
| } |
| |
| func (s *localFileStorage) Close() error { |
| defer s.Server.Close() |
| return s.Fake.Close() |
| } |
| |
| func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage { |
| handler := http.FileServer(http.Dir(ctx.Dir)) |
| server := httptest.NewServer(handler) |
| return &localFileStorage{ |
| Fake: ctx, |
| Server: server, |
| } |
| } |
| |
| // remoteFileServer is a containerized static file server started on the remote |
| // testing machine to be used in URL-accepting docker build functionality. |
| type remoteFileServer struct { |
| host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712 |
| container string |
| image string |
| ctx *fakecontext.Fake |
| } |
| |
| func (f *remoteFileServer) URL() string { |
| u := url.URL{ |
| Scheme: "http", |
| Host: f.host} |
| return u.String() |
| } |
| |
| func (f *remoteFileServer) CtxDir() string { |
| return f.ctx.Dir |
| } |
| |
| func (f *remoteFileServer) Close() error { |
| defer func() { |
| if f.ctx != nil { |
| f.ctx.Close() |
| } |
| if f.image != "" { |
| if err := cli.Docker(cli.Args("rmi", "-f", f.image)).Error; err != nil { |
| fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err) |
| } |
| } |
| }() |
| if f.container == "" { |
| return nil |
| } |
| return cli.Docker(cli.Args("rm", "-fv", f.container)).Error |
| } |
| |
| func newRemoteFileServer(t testingT, ctx *fakecontext.Fake) *remoteFileServer { |
| var ( |
| image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10))) |
| container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10))) |
| ) |
| |
| ensureHTTPServerImage(t) |
| |
| // Build the image |
| if err := ctx.Add("Dockerfile", `FROM httpserver |
| COPY . /static`); err != nil { |
| t.Fatal(err) |
| } |
| cli.BuildCmd(t, image, build.WithoutCache, build.WithExternalBuildContext(ctx)) |
| |
| // Start the container |
| cli.DockerCmd(t, "run", "-d", "-P", "--name", container, image) |
| |
| // Find out the system assigned port |
| out := cli.DockerCmd(t, "port", container, "80/tcp").Combined() |
| fileserverHostPort := strings.Trim(out, "\n") |
| _, port, err := net.SplitHostPort(fileserverHostPort) |
| if err != nil { |
| t.Fatalf("unable to parse file server host:port: %v", err) |
| } |
| |
| dockerHostURL, err := url.Parse(request.DaemonHost()) |
| if err != nil { |
| t.Fatalf("unable to parse daemon host URL: %v", err) |
| } |
| |
| host, _, err := net.SplitHostPort(dockerHostURL.Host) |
| if err != nil { |
| t.Fatalf("unable to parse docker daemon host:port: %v", err) |
| } |
| |
| return &remoteFileServer{ |
| container: container, |
| image: image, |
| host: fmt.Sprintf("%s:%s", host, port), |
| ctx: ctx} |
| } |