github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/internal/test/fakestorage/storage.go (about)

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