github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/integration-cli/cli/build/fakestorage/storage.go (about)

     1  package fakestorage
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/docker/docker/integration-cli/cli"
    13  	"github.com/docker/docker/integration-cli/cli/build"
    14  	"github.com/docker/docker/integration-cli/cli/build/fakecontext"
    15  	"github.com/docker/docker/integration-cli/request"
    16  	"github.com/docker/docker/internal/test/environment"
    17  	"github.com/docker/docker/internal/testutil"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  var testEnv *environment.Execution
    22  
    23  type testingT interface {
    24  	require.TestingT
    25  	logT
    26  	Fatal(args ...interface{})
    27  	Fatalf(string, ...interface{})
    28  }
    29  
    30  type logT interface {
    31  	Logf(string, ...interface{})
    32  }
    33  
    34  // Fake is a static file server. It might be running locally or remotely
    35  // on test host.
    36  type Fake interface {
    37  	Close() error
    38  	URL() string
    39  	CtxDir() string
    40  }
    41  
    42  // SetTestEnvironment sets a static test environment
    43  // TODO: decouple this package from environment
    44  func SetTestEnvironment(env *environment.Execution) {
    45  	testEnv = env
    46  }
    47  
    48  // New returns a static file server that will be use as build context.
    49  func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
    50  	if testEnv == nil {
    51  		t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
    52  	}
    53  	ctx := fakecontext.New(t, dir, modifiers...)
    54  	if testEnv.IsLocalDaemon() {
    55  		return newLocalFakeStorage(ctx)
    56  	}
    57  	return newRemoteFileServer(t, ctx)
    58  }
    59  
    60  // localFileStorage is a file storage on the running machine
    61  type localFileStorage struct {
    62  	*fakecontext.Fake
    63  	*httptest.Server
    64  }
    65  
    66  func (s *localFileStorage) URL() string {
    67  	return s.Server.URL
    68  }
    69  
    70  func (s *localFileStorage) CtxDir() string {
    71  	return s.Fake.Dir
    72  }
    73  
    74  func (s *localFileStorage) Close() error {
    75  	defer s.Server.Close()
    76  	return s.Fake.Close()
    77  }
    78  
    79  func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage {
    80  	handler := http.FileServer(http.Dir(ctx.Dir))
    81  	server := httptest.NewServer(handler)
    82  	return &localFileStorage{
    83  		Fake:   ctx,
    84  		Server: server,
    85  	}
    86  }
    87  
    88  // remoteFileServer is a containerized static file server started on the remote
    89  // testing machine to be used in URL-accepting docker build functionality.
    90  type remoteFileServer struct {
    91  	host      string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
    92  	container string
    93  	image     string
    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 := cli.Docker(cli.Args("rmi", "-f", f.image)).Error; err != nil {
   115  				fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
   116  			}
   117  		}
   118  	}()
   119  	if f.container == "" {
   120  		return nil
   121  	}
   122  	return cli.Docker(cli.Args("rm", "-fv", f.container)).Error
   123  }
   124  
   125  func newRemoteFileServer(t testingT, ctx *fakecontext.Fake) *remoteFileServer {
   126  	var (
   127  		image     = fmt.Sprintf("fileserver-img-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10)))
   128  		container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10)))
   129  	)
   130  
   131  	ensureHTTPServerImage(t)
   132  
   133  	// Build the image
   134  	if err := ctx.Add("Dockerfile", `FROM httpserver
   135  COPY . /static`); err != nil {
   136  		t.Fatal(err)
   137  	}
   138  	cli.BuildCmd(t, image, build.WithoutCache, build.WithExternalBuildContext(ctx))
   139  
   140  	// Start the container
   141  	cli.DockerCmd(t, "run", "-d", "-P", "--name", container, image)
   142  
   143  	// Find out the system assigned port
   144  	out := cli.DockerCmd(t, "port", container, "80/tcp").Combined()
   145  	fileserverHostPort := strings.Trim(out, "\n")
   146  	_, port, err := net.SplitHostPort(fileserverHostPort)
   147  	if err != nil {
   148  		t.Fatalf("unable to parse file server host:port: %v", err)
   149  	}
   150  
   151  	dockerHostURL, err := url.Parse(request.DaemonHost())
   152  	if err != nil {
   153  		t.Fatalf("unable to parse daemon host URL: %v", err)
   154  	}
   155  
   156  	host, _, err := net.SplitHostPort(dockerHostURL.Host)
   157  	if err != nil {
   158  		t.Fatalf("unable to parse docker daemon host:port: %v", err)
   159  	}
   160  
   161  	return &remoteFileServer{
   162  		container: container,
   163  		image:     image,
   164  		host:      fmt.Sprintf("%s:%s", host, port),
   165  		ctx:       ctx}
   166  }