2017-04-10 08:42:21 -04:00
|
|
|
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"
|
2017-08-25 18:48:36 -04:00
|
|
|
"github.com/docker/docker/internal/test/environment"
|
2017-04-10 08:42:21 -04:00
|
|
|
"github.com/docker/docker/pkg/stringutils"
|
2017-08-25 18:48:36 -04:00
|
|
|
"github.com/stretchr/testify/require"
|
2017-04-10 08:42:21 -04:00
|
|
|
)
|
|
|
|
|
2017-08-25 18:48:36 -04:00
|
|
|
var testEnv *environment.Execution
|
2017-04-10 08:42:21 -04:00
|
|
|
|
|
|
|
type testingT interface {
|
2017-08-25 18:48:36 -04:00
|
|
|
require.TestingT
|
2017-04-10 08:42:21 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-08-25 18:48:36 -04:00
|
|
|
// SetTestEnvironment sets a static test environment
|
|
|
|
// TODO: decouple this package from environment
|
|
|
|
func SetTestEnvironment(env *environment.Execution) {
|
|
|
|
testEnv = env
|
|
|
|
}
|
|
|
|
|
2017-04-10 08:42:21 -04:00
|
|
|
// 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 {
|
2017-08-25 18:48:36 -04:00
|
|
|
if testEnv == nil {
|
|
|
|
t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
|
|
|
|
}
|
2017-04-10 08:42:21 -04:00
|
|
|
ctx := fakecontext.New(t, dir, modifiers...)
|
2017-08-25 18:48:36 -04:00
|
|
|
if testEnv.IsLocalDaemon() {
|
|
|
|
return newLocalFakeStorage(ctx)
|
2017-04-10 08:42:21 -04:00
|
|
|
}
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2017-08-25 18:48:36 -04:00
|
|
|
func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage {
|
2017-04-10 08:42:21 -04:00
|
|
|
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(stringutils.GenerateRandomAlphaOnlyString(10)))
|
|
|
|
container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.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}
|
|
|
|
}
|