integration/utils_test.go
d4e62101
 package docker
 
 import (
c001a5af
 	"bytes"
ed8f75d2
 	"fmt"
d4e62101
 	"io"
 	"io/ioutil"
afe23071
 	"net/http"
 	"net/http/httptest"
d4e62101
 	"os"
 	"path"
6e92dfdf
 	"path/filepath"
d4e62101
 	"strings"
 	"testing"
afe23071
 	"time"
92e61f89
 
b3ee9ac7
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
e304e3a6
 
b3ee9ac7
 	"github.com/docker/docker/builtins"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/engine"
9aa71549
 	flag "github.com/docker/docker/pkg/mflag"
568f86eb
 	"github.com/docker/docker/registry"
b3ee9ac7
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
d4e62101
 )
 
7c62cee5
 type Fataler interface {
 	Fatal(...interface{})
 }
 
d4e62101
 // This file contains utility functions for docker's unit test suite.
 // It has to be named XXX_test.go, apparently, in other to access private functions
 // from other XXX_test.go functions.
 
359b7df5
 // Create a temporary daemon suitable for unit testing.
d4e62101
 // Call t.Fatal() at the first error.
7c62cee5
 func mkDaemon(f Fataler) *daemon.Daemon {
92c3927b
 	eng := newTestEngine(f, false, "")
359b7df5
 	return mkDaemonFromEngine(eng, f)
d4e62101
 }
 
7c62cee5
 func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) {
e5f8ab61
 	job := eng.Job("create", name)
 	if err := job.ImportEnv(config); err != nil {
 		f.Fatal(err)
 	}
e304e3a6
 	var outputBuffer = bytes.NewBuffer(nil)
 	job.Stdout.Add(outputBuffer)
e5f8ab61
 	if err := job.Run(); err != nil {
 		f.Fatal(err)
 	}
e304e3a6
 	return engine.Tail(outputBuffer, 1)
6bdb6f22
 }
 
7c62cee5
 func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler) (shortId string) {
e5f8ab61
 	return createNamedTestContainer(eng, config, f, "")
 }
 
7c62cee5
 func startContainer(eng *engine.Engine, id string, t Fataler) {
afe23071
 	job := eng.Job("start", id)
 	if err := job.Run(); err != nil {
 		t.Fatal(err)
 	}
 }
 
7c62cee5
 func containerRun(eng *engine.Engine, id string, t Fataler) {
afe23071
 	startContainer(eng, id, t)
 	containerWait(eng, id, t)
 }
 
7c62cee5
 func containerFileExists(eng *engine.Engine, id, dir string, t Fataler) bool {
afe23071
 	c := getContainer(eng, id, t)
191aa17d
 	if err := c.Mount(); err != nil {
afe23071
 		t.Fatal(err)
 	}
bcaf6c23
 	defer c.Unmount()
bf1b27df
 	if _, err := os.Stat(path.Join(c.RootfsPath(), dir)); err != nil {
afe23071
 		if os.IsNotExist(err) {
 			return false
 		}
 		t.Fatal(err)
 	}
 	return true
 }
 
7c62cee5
 func containerAttach(eng *engine.Engine, id string, t Fataler) (io.WriteCloser, io.ReadCloser) {
afe23071
 	c := getContainer(eng, id, t)
21e44d7a
 	i := c.StdinPipe()
 	o := c.StdoutPipe()
afe23071
 	return i, o
 }
 
7c62cee5
 func containerWait(eng *engine.Engine, id string, t Fataler) int {
e0339d4b
 	ex, _ := getContainer(eng, id, t).WaitStop(-1 * time.Second)
57d86a56
 	return ex
afe23071
 }
 
7c62cee5
 func containerWaitTimeout(eng *engine.Engine, id string, t Fataler) error {
e0339d4b
 	_, err := getContainer(eng, id, t).WaitStop(500 * time.Millisecond)
57d86a56
 	return err
afe23071
 }
 
7c62cee5
 func containerKill(eng *engine.Engine, id string, t Fataler) {
2546a2c6
 	if err := eng.Job("kill", id).Run(); err != nil {
afe23071
 		t.Fatal(err)
 	}
 }
 
7c62cee5
 func containerRunning(eng *engine.Engine, id string, t Fataler) bool {
e0339d4b
 	return getContainer(eng, id, t).IsRunning()
afe23071
 }
 
7c62cee5
 func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
afe23071
 	getContainer(eng, id, t)
 }
 
7c62cee5
 func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
359b7df5
 	daemon := mkDaemonFromEngine(eng, t)
d25a6537
 	if c, _ := daemon.Get(id); c != nil {
afe23071
 		t.Fatal(fmt.Errorf("Container %s should not exist", id))
 	}
 }
 
 // assertHttpNotError expect the given response to not have an error.
 // Otherwise the it causes the test to fail.
7c62cee5
 func assertHttpNotError(r *httptest.ResponseRecorder, t Fataler) {
afe23071
 	// Non-error http status are [200, 400)
 	if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest {
 		t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code))
 	}
 }
 
 // assertHttpError expect the given response to have an error.
 // Otherwise the it causes the test to fail.
7c62cee5
 func assertHttpError(r *httptest.ResponseRecorder, t Fataler) {
afe23071
 	// Non-error http status are [200, 400)
 	if !(r.Code < http.StatusOK || r.Code >= http.StatusBadRequest) {
 		t.Fatal(fmt.Errorf("Unexpected http success code: %v", r.Code))
 	}
 }
 
7c62cee5
 func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
359b7df5
 	daemon := mkDaemonFromEngine(eng, t)
d25a6537
 	c, err := daemon.Get(id)
 	if err != nil {
 		t.Fatal(err)
afe23071
 	}
 	return c
 }
 
7c62cee5
 func mkDaemonFromEngine(eng *engine.Engine, t Fataler) *daemon.Daemon {
359b7df5
 	iDaemon := eng.Hack_GetGlobalVar("httpapi.daemon")
 	if iDaemon == nil {
 		panic("Legacy daemon field not set in engine")
c001a5af
 	}
359b7df5
 	daemon, ok := iDaemon.(*daemon.Daemon)
c001a5af
 	if !ok {
359b7df5
 		panic("Legacy daemon field in engine does not cast to *daemon.Daemon")
c001a5af
 	}
359b7df5
 	return daemon
c001a5af
 }
 
7c62cee5
 func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine {
92c3927b
 	if root == "" {
 		if dir, err := newTestDirectory(unitTestStoreBase); err != nil {
 			t.Fatal(err)
 		} else {
 			root = dir
 		}
6bdb6f22
 	}
87e8d775
 	os.MkdirAll(root, 0700)
7100ace4
 
 	eng := engine.New()
8a9e8272
 	eng.Logging = false
5a85456d
 	// Load default plugins
568f86eb
 	if err := builtins.Register(eng); err != nil {
 		t.Fatal(err)
 	}
 	// load registry service
 	if err := registry.NewService(nil).Install(eng); err != nil {
 		t.Fatal(err)
 	}
 
5a85456d
 	// (This is manually copied and modified from main() until we have a more generic plugin system)
a4befff5
 	cfg := &daemon.Config{
63503caf
 		Root:        root,
 		AutoRestart: autorestart,
 		ExecDriver:  "native",
a4befff5
 		// Either InterContainerCommunication or EnableIptables must be set,
 		// otherwise NewDaemon will fail because of conflicting settings.
 		InterContainerCommunication: true,
6e92dfdf
 		TrustKeyPath:                filepath.Join(root, "key.json"),
47a6afb9
 		LogConfig:                   runconfig.LogConfig{Type: "json-file"},
63503caf
 	}
 	d, err := daemon.NewDaemon(cfg, eng)
 	if err != nil {
 		t.Fatal(err)
 	}
 	if err := d.Install(eng); err != nil {
5a85456d
 		t.Fatal(err)
6bdb6f22
 	}
5a85456d
 	return eng
 }
6bdb6f22
 
7c62cee5
 func NewTestEngine(t Fataler) *engine.Engine {
92c3927b
 	return newTestEngine(t, false, "")
 }
 
5a85456d
 func newTestDirectory(templateDir string) (dir string, err error) {
c001a5af
 	return utils.TestDirectory(templateDir)
5a85456d
 }
 
 func getCallerName(depth int) string {
c001a5af
 	return utils.GetCallerName(depth)
6bdb6f22
 }
 
d4e62101
 // Write `content` to the file at path `dst`, creating it if necessary,
 // as well as any missing directories.
 // The file is truncated if it already exists.
 // Call t.Fatal() at the first error.
 func writeFile(dst, content string, t *testing.T) {
 	// Create subdirectories if necessary
 	if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) {
 		t.Fatal(err)
 	}
46a9f29b
 	f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
d4e62101
 	if err != nil {
 		t.Fatal(err)
 	}
 	// Write content (truncate if it exists)
 	if _, err := io.Copy(f, strings.NewReader(content)); err != nil {
 		t.Fatal(err)
 	}
 }
 
 // Return the contents of file at path `src`.
 // Call t.Fatal() at the first error (including if the file doesn't exist)
 func readFile(src string, t *testing.T) (content string) {
 	f, err := os.Open(src)
 	if err != nil {
 		t.Fatal(err)
 	}
 	data, err := ioutil.ReadAll(f)
 	if err != nil {
 		t.Fatal(err)
 	}
 	return string(data)
 }
 
359b7df5
 // Create a test container from the given daemon `r` and run arguments `args`.
080243f0
 // If the image name is "_", (eg. []string{"-i", "-t", "_", "bash"}, it is
 // dynamically replaced by the current test image.
d4e62101
 // The caller is responsible for destroying the container.
 // Call t.Fatal() at the first error.
359b7df5
 func mkContainer(r *daemon.Daemon, args []string, t *testing.T) (*daemon.Container, *runconfig.HostConfig, error) {
e45b0f92
 	config, hc, _, err := parseRun(args)
080243f0
 	defer func() {
 		if err != nil && t != nil {
 			t.Fatal(err)
 		}
 	}()
d4e62101
 	if err != nil {
661a8a0e
 		return nil, nil, err
080243f0
 	}
 	if config.Image == "_" {
 		config.Image = GetTestImage(r).ID
d4e62101
 	}
1df87b95
 	c, _, err := r.Create(config, nil, "")
d4e62101
 	if err != nil {
661a8a0e
 		return nil, nil, err
d4e62101
 	}
c001a5af
 	// NOTE: hostConfig is ignored.
 	// If `args` specify privileged mode, custom lxc conf, external mount binds,
 	// port redirects etc. they will be ignored.
 	// This is because the correct way to set these things is to pass environment
 	// to the `start` job.
 	// FIXME: this helper function should be deprecated in favor of calling
 	// `create` and `start` jobs directly.
661a8a0e
 	return c, hc, nil
d4e62101
 }
 
 // Create a test container, start it, wait for it to complete, destroy it,
 // and return its standard output as a string.
46a9f29b
 // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image.
d4e62101
 // If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally.
359b7df5
 func runContainer(eng *engine.Engine, r *daemon.Daemon, args []string, t *testing.T) (output string, err error) {
d4e62101
 	defer func() {
 		if err != nil && t != nil {
 			t.Fatal(err)
 		}
 	}()
661a8a0e
 	container, hc, err := mkContainer(r, args, t)
080243f0
 	if err != nil {
 		return "", err
 	}
ba93f831
 	defer r.Rm(container)
21e44d7a
 	stdout := container.StdoutPipe()
d4e62101
 	defer stdout.Close()
661a8a0e
 
 	job := eng.Job("start", container.ID)
 	if err := job.ImportEnv(hc); err != nil {
d4e62101
 		return "", err
 	}
661a8a0e
 	if err := job.Run(); err != nil {
 		return "", err
 	}
 
e0339d4b
 	container.WaitStop(-1 * time.Second)
d4e62101
 	data, err := ioutil.ReadAll(stdout)
 	if err != nil {
 		return "", err
 	}
 	output = string(data)
 	return
 }
193a7e1d
 
c001a5af
 // FIXME: this is duplicated from graph_test.go in the docker package.
f198ee52
 func fakeTar() (io.ReadCloser, error) {
c001a5af
 	content := []byte("Hello world!\n")
 	buf := new(bytes.Buffer)
 	tw := tar.NewWriter(buf)
 	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
 		hdr := new(tar.Header)
 		hdr.Size = int64(len(content))
 		hdr.Name = name
 		if err := tw.WriteHeader(hdr); err != nil {
 			return nil, err
551092f9
 		}
c001a5af
 		tw.Write([]byte(content))
551092f9
 	}
c001a5af
 	tw.Close()
f198ee52
 	return ioutil.NopCloser(buf), nil
551092f9
 }
17a806c8
 
 func getAllImages(eng *engine.Engine, t *testing.T) *engine.Table {
 	return getImages(eng, t, true, "")
 }
 
 func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) *engine.Table {
 	job := eng.Job("images")
 	job.SetenvBool("all", all)
 	job.Setenv("filter", filter)
e3461bc8
 	images, err := job.Stdout.AddListTable()
17a806c8
 	if err != nil {
 		t.Fatal(err)
 	}
 	if err := job.Run(); err != nil {
 		t.Fatal(err)
 	}
 	return images
 
 }
9aa71549
 
e45b0f92
 func parseRun(args []string) (*runconfig.Config, *runconfig.HostConfig, *flag.FlagSet, error) {
9aa71549
 	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
 	cmd.SetOutput(ioutil.Discard)
 	cmd.Usage = nil
e45b0f92
 	return runconfig.Parse(cmd, args)
9aa71549
 }