123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package fakestorage // import "github.com/docker/docker/testutil/fakestorage"
- import (
- "context"
- "fmt"
- "io"
- "net/http"
- "net/http/httptest"
- "net/url"
- "os"
- "strings"
- "testing"
- "github.com/docker/docker/api/types"
- containertypes "github.com/docker/docker/api/types/container"
- "github.com/docker/docker/client"
- "github.com/docker/docker/testutil"
- "github.com/docker/docker/testutil/environment"
- "github.com/docker/docker/testutil/fakecontext"
- "github.com/docker/docker/testutil/request"
- "github.com/docker/go-connections/nat"
- "gotest.tools/v3/assert"
- )
- var testEnv *environment.Execution
- // 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 testing.TB, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
- t.Helper()
- if testEnv == nil {
- t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
- }
- ctx := fakecontext.New(t, dir, modifiers...)
- switch {
- case testEnv.IsRemoteDaemon() && strings.HasPrefix(request.DaemonHost(), "unix:///"):
- t.Skip("e2e run : daemon is remote but docker host points to a unix socket")
- case testEnv.IsLocalDaemon():
- return newLocalFakeStorage(ctx)
- default:
- return newRemoteFileServer(t, ctx, testEnv.APIClient())
- }
- return nil
- }
- // 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
- client client.APIClient
- 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 := f.client.ImageRemove(context.Background(), f.image, types.ImageRemoveOptions{
- Force: true,
- }); err != nil {
- fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
- }
- }
- if err := f.client.Close(); err != nil {
- fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
- }
- }()
- if f.container == "" {
- return nil
- }
- return f.client.ContainerRemove(context.Background(), f.container, containertypes.RemoveOptions{
- Force: true,
- RemoveVolumes: true,
- })
- }
- func newRemoteFileServer(t testing.TB, ctx *fakecontext.Fake, c client.APIClient) *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)
- }
- resp, err := c.ImageBuild(context.Background(), ctx.AsTarReader(t), types.ImageBuildOptions{
- NoCache: true,
- Tags: []string{image},
- })
- assert.NilError(t, err)
- _, err = io.Copy(io.Discard, resp.Body)
- assert.NilError(t, err)
- // Start the container
- b, err := c.ContainerCreate(context.Background(), &containertypes.Config{
- Image: image,
- }, &containertypes.HostConfig{}, nil, nil, container)
- assert.NilError(t, err)
- err = c.ContainerStart(context.Background(), b.ID, containertypes.StartOptions{})
- assert.NilError(t, err)
- // Find out the system assigned port
- i, err := c.ContainerInspect(context.Background(), b.ID)
- assert.NilError(t, err)
- newP, err := nat.NewPort("tcp", "80")
- assert.NilError(t, err)
- ports, exists := i.NetworkSettings.Ports[newP]
- if !exists || len(ports) != 1 {
- t.Fatalf("unable to find port 80/tcp for %s", container)
- }
- host := ports[0].HostIP
- port := ports[0].HostPort
- return &remoteFileServer{
- container: container,
- image: image,
- host: fmt.Sprintf("%s:%s", host, port),
- ctx: ctx,
- client: c,
- }
- }
|