201 lines
5.2 KiB
Go
201 lines
5.2 KiB
Go
package fakestorage // import "github.com/docker/docker/internal/test/fakestorage"
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
containertypes "github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/internal/test"
|
|
"github.com/docker/docker/internal/test/environment"
|
|
"github.com/docker/docker/internal/test/fakecontext"
|
|
"github.com/docker/docker/internal/test/request"
|
|
"github.com/docker/docker/internal/testutil"
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/gotestyourself/gotestyourself/assert"
|
|
)
|
|
|
|
var testEnv *environment.Execution
|
|
|
|
type testingT interface {
|
|
assert.TestingT
|
|
logT
|
|
skipT
|
|
Fatal(args ...interface{})
|
|
Fatalf(string, ...interface{})
|
|
}
|
|
|
|
type logT interface {
|
|
Logf(string, ...interface{})
|
|
}
|
|
|
|
type skipT interface {
|
|
Skip(reason string)
|
|
}
|
|
|
|
// 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 testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
|
|
if ht, ok := t.(test.HelperT); ok {
|
|
ht.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(fmt.Sprintf("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, types.ContainerRemoveOptions{
|
|
Force: true,
|
|
RemoveVolumes: true,
|
|
})
|
|
}
|
|
|
|
func newRemoteFileServer(t testingT, 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(ioutil.Discard, resp.Body)
|
|
assert.NilError(t, err)
|
|
|
|
// Start the container
|
|
b, err := c.ContainerCreate(context.Background(), &containertypes.Config{
|
|
Image: image,
|
|
}, &containertypes.HostConfig{}, nil, container)
|
|
assert.NilError(t, err)
|
|
err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{})
|
|
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,
|
|
}
|
|
}
|