github.com/scorpionis/docker@v1.6.0-rc7/integration/utils_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
    18  
    19  	"github.com/docker/docker/builtins"
    20  	"github.com/docker/docker/daemon"
    21  	"github.com/docker/docker/engine"
    22  	flag "github.com/docker/docker/pkg/mflag"
    23  	"github.com/docker/docker/registry"
    24  	"github.com/docker/docker/runconfig"
    25  	"github.com/docker/docker/utils"
    26  )
    27  
    28  type Fataler interface {
    29  	Fatal(...interface{})
    30  }
    31  
    32  // This file contains utility functions for docker's unit test suite.
    33  // It has to be named XXX_test.go, apparently, in other to access private functions
    34  // from other XXX_test.go functions.
    35  
    36  // Create a temporary daemon suitable for unit testing.
    37  // Call t.Fatal() at the first error.
    38  func mkDaemon(f Fataler) *daemon.Daemon {
    39  	eng := newTestEngine(f, false, "")
    40  	return mkDaemonFromEngine(eng, f)
    41  }
    42  
    43  func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) {
    44  	job := eng.Job("create", name)
    45  	if err := job.ImportEnv(config); err != nil {
    46  		f.Fatal(err)
    47  	}
    48  	var outputBuffer = bytes.NewBuffer(nil)
    49  	job.Stdout.Add(outputBuffer)
    50  	if err := job.Run(); err != nil {
    51  		f.Fatal(err)
    52  	}
    53  	return engine.Tail(outputBuffer, 1)
    54  }
    55  
    56  func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler) (shortId string) {
    57  	return createNamedTestContainer(eng, config, f, "")
    58  }
    59  
    60  func startContainer(eng *engine.Engine, id string, t Fataler) {
    61  	job := eng.Job("start", id)
    62  	if err := job.Run(); err != nil {
    63  		t.Fatal(err)
    64  	}
    65  }
    66  
    67  func containerRun(eng *engine.Engine, id string, t Fataler) {
    68  	startContainer(eng, id, t)
    69  	containerWait(eng, id, t)
    70  }
    71  
    72  func containerFileExists(eng *engine.Engine, id, dir string, t Fataler) bool {
    73  	c := getContainer(eng, id, t)
    74  	if err := c.Mount(); err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	defer c.Unmount()
    78  	if _, err := os.Stat(path.Join(c.RootfsPath(), dir)); err != nil {
    79  		if os.IsNotExist(err) {
    80  			return false
    81  		}
    82  		t.Fatal(err)
    83  	}
    84  	return true
    85  }
    86  
    87  func containerAttach(eng *engine.Engine, id string, t Fataler) (io.WriteCloser, io.ReadCloser) {
    88  	c := getContainer(eng, id, t)
    89  	i := c.StdinPipe()
    90  	o := c.StdoutPipe()
    91  	return i, o
    92  }
    93  
    94  func containerWait(eng *engine.Engine, id string, t Fataler) int {
    95  	ex, _ := getContainer(eng, id, t).WaitStop(-1 * time.Second)
    96  	return ex
    97  }
    98  
    99  func containerWaitTimeout(eng *engine.Engine, id string, t Fataler) error {
   100  	_, err := getContainer(eng, id, t).WaitStop(500 * time.Millisecond)
   101  	return err
   102  }
   103  
   104  func containerKill(eng *engine.Engine, id string, t Fataler) {
   105  	if err := eng.Job("kill", id).Run(); err != nil {
   106  		t.Fatal(err)
   107  	}
   108  }
   109  
   110  func containerRunning(eng *engine.Engine, id string, t Fataler) bool {
   111  	return getContainer(eng, id, t).IsRunning()
   112  }
   113  
   114  func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
   115  	getContainer(eng, id, t)
   116  }
   117  
   118  func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
   119  	daemon := mkDaemonFromEngine(eng, t)
   120  	if c, _ := daemon.Get(id); c != nil {
   121  		t.Fatal(fmt.Errorf("Container %s should not exist", id))
   122  	}
   123  }
   124  
   125  // assertHttpNotError expect the given response to not have an error.
   126  // Otherwise the it causes the test to fail.
   127  func assertHttpNotError(r *httptest.ResponseRecorder, t Fataler) {
   128  	// Non-error http status are [200, 400)
   129  	if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest {
   130  		t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code))
   131  	}
   132  }
   133  
   134  // assertHttpError expect the given response to have an error.
   135  // Otherwise the it causes the test to fail.
   136  func assertHttpError(r *httptest.ResponseRecorder, t Fataler) {
   137  	// Non-error http status are [200, 400)
   138  	if !(r.Code < http.StatusOK || r.Code >= http.StatusBadRequest) {
   139  		t.Fatal(fmt.Errorf("Unexpected http success code: %v", r.Code))
   140  	}
   141  }
   142  
   143  func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
   144  	daemon := mkDaemonFromEngine(eng, t)
   145  	c, err := daemon.Get(id)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	return c
   150  }
   151  
   152  func mkDaemonFromEngine(eng *engine.Engine, t Fataler) *daemon.Daemon {
   153  	iDaemon := eng.Hack_GetGlobalVar("httpapi.daemon")
   154  	if iDaemon == nil {
   155  		panic("Legacy daemon field not set in engine")
   156  	}
   157  	daemon, ok := iDaemon.(*daemon.Daemon)
   158  	if !ok {
   159  		panic("Legacy daemon field in engine does not cast to *daemon.Daemon")
   160  	}
   161  	return daemon
   162  }
   163  
   164  func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine {
   165  	if root == "" {
   166  		if dir, err := newTestDirectory(unitTestStoreBase); err != nil {
   167  			t.Fatal(err)
   168  		} else {
   169  			root = dir
   170  		}
   171  	}
   172  	os.MkdirAll(root, 0700)
   173  
   174  	eng := engine.New()
   175  	eng.Logging = false
   176  	// Load default plugins
   177  	if err := builtins.Register(eng); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	// load registry service
   181  	if err := registry.NewService(nil).Install(eng); err != nil {
   182  		t.Fatal(err)
   183  	}
   184  
   185  	// (This is manually copied and modified from main() until we have a more generic plugin system)
   186  	cfg := &daemon.Config{
   187  		Root:        root,
   188  		AutoRestart: autorestart,
   189  		ExecDriver:  "native",
   190  		// Either InterContainerCommunication or EnableIptables must be set,
   191  		// otherwise NewDaemon will fail because of conflicting settings.
   192  		InterContainerCommunication: true,
   193  		TrustKeyPath:                filepath.Join(root, "key.json"),
   194  		LogConfig:                   runconfig.LogConfig{Type: "json-file"},
   195  	}
   196  	d, err := daemon.NewDaemon(cfg, eng)
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	if err := d.Install(eng); err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	return eng
   204  }
   205  
   206  func NewTestEngine(t Fataler) *engine.Engine {
   207  	return newTestEngine(t, false, "")
   208  }
   209  
   210  func newTestDirectory(templateDir string) (dir string, err error) {
   211  	return utils.TestDirectory(templateDir)
   212  }
   213  
   214  func getCallerName(depth int) string {
   215  	return utils.GetCallerName(depth)
   216  }
   217  
   218  // Write `content` to the file at path `dst`, creating it if necessary,
   219  // as well as any missing directories.
   220  // The file is truncated if it already exists.
   221  // Call t.Fatal() at the first error.
   222  func writeFile(dst, content string, t *testing.T) {
   223  	// Create subdirectories if necessary
   224  	if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) {
   225  		t.Fatal(err)
   226  	}
   227  	f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	// Write content (truncate if it exists)
   232  	if _, err := io.Copy(f, strings.NewReader(content)); err != nil {
   233  		t.Fatal(err)
   234  	}
   235  }
   236  
   237  // Return the contents of file at path `src`.
   238  // Call t.Fatal() at the first error (including if the file doesn't exist)
   239  func readFile(src string, t *testing.T) (content string) {
   240  	f, err := os.Open(src)
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	data, err := ioutil.ReadAll(f)
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	return string(data)
   249  }
   250  
   251  // Create a test container from the given daemon `r` and run arguments `args`.
   252  // If the image name is "_", (eg. []string{"-i", "-t", "_", "bash"}, it is
   253  // dynamically replaced by the current test image.
   254  // The caller is responsible for destroying the container.
   255  // Call t.Fatal() at the first error.
   256  func mkContainer(r *daemon.Daemon, args []string, t *testing.T) (*daemon.Container, *runconfig.HostConfig, error) {
   257  	config, hc, _, err := parseRun(args)
   258  	defer func() {
   259  		if err != nil && t != nil {
   260  			t.Fatal(err)
   261  		}
   262  	}()
   263  	if err != nil {
   264  		return nil, nil, err
   265  	}
   266  	if config.Image == "_" {
   267  		config.Image = GetTestImage(r).ID
   268  	}
   269  	c, _, err := r.Create(config, nil, "")
   270  	if err != nil {
   271  		return nil, nil, err
   272  	}
   273  	// NOTE: hostConfig is ignored.
   274  	// If `args` specify privileged mode, custom lxc conf, external mount binds,
   275  	// port redirects etc. they will be ignored.
   276  	// This is because the correct way to set these things is to pass environment
   277  	// to the `start` job.
   278  	// FIXME: this helper function should be deprecated in favor of calling
   279  	// `create` and `start` jobs directly.
   280  	return c, hc, nil
   281  }
   282  
   283  // Create a test container, start it, wait for it to complete, destroy it,
   284  // and return its standard output as a string.
   285  // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image.
   286  // If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally.
   287  func runContainer(eng *engine.Engine, r *daemon.Daemon, args []string, t *testing.T) (output string, err error) {
   288  	defer func() {
   289  		if err != nil && t != nil {
   290  			t.Fatal(err)
   291  		}
   292  	}()
   293  	container, hc, err := mkContainer(r, args, t)
   294  	if err != nil {
   295  		return "", err
   296  	}
   297  	defer r.Rm(container)
   298  	stdout := container.StdoutPipe()
   299  	defer stdout.Close()
   300  
   301  	job := eng.Job("start", container.ID)
   302  	if err := job.ImportEnv(hc); err != nil {
   303  		return "", err
   304  	}
   305  	if err := job.Run(); err != nil {
   306  		return "", err
   307  	}
   308  
   309  	container.WaitStop(-1 * time.Second)
   310  	data, err := ioutil.ReadAll(stdout)
   311  	if err != nil {
   312  		return "", err
   313  	}
   314  	output = string(data)
   315  	return
   316  }
   317  
   318  // FIXME: this is duplicated from graph_test.go in the docker package.
   319  func fakeTar() (io.ReadCloser, error) {
   320  	content := []byte("Hello world!\n")
   321  	buf := new(bytes.Buffer)
   322  	tw := tar.NewWriter(buf)
   323  	for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
   324  		hdr := new(tar.Header)
   325  		hdr.Size = int64(len(content))
   326  		hdr.Name = name
   327  		if err := tw.WriteHeader(hdr); err != nil {
   328  			return nil, err
   329  		}
   330  		tw.Write([]byte(content))
   331  	}
   332  	tw.Close()
   333  	return ioutil.NopCloser(buf), nil
   334  }
   335  
   336  func getAllImages(eng *engine.Engine, t *testing.T) *engine.Table {
   337  	return getImages(eng, t, true, "")
   338  }
   339  
   340  func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) *engine.Table {
   341  	job := eng.Job("images")
   342  	job.SetenvBool("all", all)
   343  	job.Setenv("filter", filter)
   344  	images, err := job.Stdout.AddListTable()
   345  	if err != nil {
   346  		t.Fatal(err)
   347  	}
   348  	if err := job.Run(); err != nil {
   349  		t.Fatal(err)
   350  	}
   351  	return images
   352  
   353  }
   354  
   355  func parseRun(args []string) (*runconfig.Config, *runconfig.HostConfig, *flag.FlagSet, error) {
   356  	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
   357  	cmd.SetOutput(ioutil.Discard)
   358  	cmd.Usage = nil
   359  	return runconfig.Parse(cmd, args)
   360  }