github.com/erriapo/docker@v1.6.0-rc2/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 }