github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/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  	"sync"
    12  
    13  	"github.com/docker/docker/integration-cli/cli"
    14  	"github.com/docker/docker/integration-cli/cli/build"
    15  	"github.com/docker/docker/integration-cli/cli/build/fakecontext"
    16  	"github.com/docker/docker/integration-cli/environment"
    17  	"github.com/docker/docker/integration-cli/request"
    18  	"github.com/docker/docker/pkg/stringutils"
    19  )
    20  
    21  var (
    22  	testEnv  *environment.Execution
    23  	onlyOnce sync.Once
    24  )
    25  
    26  // EnsureTestEnvIsLoaded make sure the test environment is loaded for this package
    27  func EnsureTestEnvIsLoaded(t testingT) {
    28  	var doIt bool
    29  	var err error
    30  	onlyOnce.Do(func() {
    31  		doIt = true
    32  	})
    33  
    34  	if !doIt {
    35  		return
    36  	}
    37  	testEnv, err = environment.New()
    38  	if err != nil {
    39  		t.Fatalf("error loading testenv : %v", err)
    40  	}
    41  }
    42  
    43  type testingT interface {
    44  	logT
    45  	Fatal(args ...interface{})
    46  	Fatalf(string, ...interface{})
    47  }
    48  
    49  type logT interface {
    50  	Logf(string, ...interface{})
    51  }
    52  
    53  // Fake is a static file server. It might be running locally or remotely
    54  // on test host.
    55  type Fake interface {
    56  	Close() error
    57  	URL() string
    58  	CtxDir() string
    59  }
    60  
    61  // New returns a static file server that will be use as build context.
    62  func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
    63  	ctx := fakecontext.New(t, dir, modifiers...)
    64  	if testEnv.LocalDaemon() {
    65  		return newLocalFakeStorage(t, ctx)
    66  	}
    67  	return newRemoteFileServer(t, ctx)
    68  }
    69  
    70  // localFileStorage is a file storage on the running machine
    71  type localFileStorage struct {
    72  	*fakecontext.Fake
    73  	*httptest.Server
    74  }
    75  
    76  func (s *localFileStorage) URL() string {
    77  	return s.Server.URL
    78  }
    79  
    80  func (s *localFileStorage) CtxDir() string {
    81  	return s.Fake.Dir
    82  }
    83  
    84  func (s *localFileStorage) Close() error {
    85  	defer s.Server.Close()
    86  	return s.Fake.Close()
    87  }
    88  
    89  func newLocalFakeStorage(t testingT, ctx *fakecontext.Fake) *localFileStorage {
    90  	handler := http.FileServer(http.Dir(ctx.Dir))
    91  	server := httptest.NewServer(handler)
    92  	return &localFileStorage{
    93  		Fake:   ctx,
    94  		Server: server,
    95  	}
    96  }
    97  
    98  // remoteFileServer is a containerized static file server started on the remote
    99  // testing machine to be used in URL-accepting docker build functionality.
   100  type remoteFileServer struct {
   101  	host      string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
   102  	container string
   103  	image     string
   104  	ctx       *fakecontext.Fake
   105  }
   106  
   107  func (f *remoteFileServer) URL() string {
   108  	u := url.URL{
   109  		Scheme: "http",
   110  		Host:   f.host}
   111  	return u.String()
   112  }
   113  
   114  func (f *remoteFileServer) CtxDir() string {
   115  	return f.ctx.Dir
   116  }
   117  
   118  func (f *remoteFileServer) Close() error {
   119  	defer func() {
   120  		if f.ctx != nil {
   121  			f.ctx.Close()
   122  		}
   123  		if f.image != "" {
   124  			if err := cli.Docker(cli.Args("rmi", "-f", f.image)).Error; err != nil {
   125  				fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
   126  			}
   127  		}
   128  	}()
   129  	if f.container == "" {
   130  		return nil
   131  	}
   132  	return cli.Docker(cli.Args("rm", "-fv", f.container)).Error
   133  }
   134  
   135  func newRemoteFileServer(t testingT, ctx *fakecontext.Fake) *remoteFileServer {
   136  	var (
   137  		image     = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
   138  		container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
   139  	)
   140  
   141  	ensureHTTPServerImage(t)
   142  
   143  	// Build the image
   144  	if err := ctx.Add("Dockerfile", `FROM httpserver
   145  COPY . /static`); err != nil {
   146  		t.Fatal(err)
   147  	}
   148  	cli.BuildCmd(t, image, build.WithoutCache, build.WithExternalBuildContext(ctx))
   149  
   150  	// Start the container
   151  	cli.DockerCmd(t, "run", "-d", "-P", "--name", container, image)
   152  
   153  	// Find out the system assigned port
   154  	out := cli.DockerCmd(t, "port", container, "80/tcp").Combined()
   155  	fileserverHostPort := strings.Trim(out, "\n")
   156  	_, port, err := net.SplitHostPort(fileserverHostPort)
   157  	if err != nil {
   158  		t.Fatalf("unable to parse file server host:port: %v", err)
   159  	}
   160  
   161  	dockerHostURL, err := url.Parse(request.DaemonHost())
   162  	if err != nil {
   163  		t.Fatalf("unable to parse daemon host URL: %v", err)
   164  	}
   165  
   166  	host, _, err := net.SplitHostPort(dockerHostURL.Host)
   167  	if err != nil {
   168  		t.Fatalf("unable to parse docker daemon host:port: %v", err)
   169  	}
   170  
   171  	return &remoteFileServer{
   172  		container: container,
   173  		image:     image,
   174  		host:      fmt.Sprintf("%s:%s", host, port),
   175  		ctx:       ctx}
   176  }