github.com/oyvindsk/docker@v1.5.0/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 := daemon.Get(id) 146 if c == nil { 147 t.Fatal(fmt.Errorf("No such container: %s", id)) 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 } 195 d, err := daemon.NewDaemon(cfg, eng) 196 if err != nil { 197 t.Fatal(err) 198 } 199 if err := d.Install(eng); err != nil { 200 t.Fatal(err) 201 } 202 return eng 203 } 204 205 func NewTestEngine(t Fataler) *engine.Engine { 206 return newTestEngine(t, false, "") 207 } 208 209 func newTestDirectory(templateDir string) (dir string, err error) { 210 return utils.TestDirectory(templateDir) 211 } 212 213 func getCallerName(depth int) string { 214 return utils.GetCallerName(depth) 215 } 216 217 // Write `content` to the file at path `dst`, creating it if necessary, 218 // as well as any missing directories. 219 // The file is truncated if it already exists. 220 // Call t.Fatal() at the first error. 221 func writeFile(dst, content string, t *testing.T) { 222 // Create subdirectories if necessary 223 if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) { 224 t.Fatal(err) 225 } 226 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 227 if err != nil { 228 t.Fatal(err) 229 } 230 // Write content (truncate if it exists) 231 if _, err := io.Copy(f, strings.NewReader(content)); err != nil { 232 t.Fatal(err) 233 } 234 } 235 236 // Return the contents of file at path `src`. 237 // Call t.Fatal() at the first error (including if the file doesn't exist) 238 func readFile(src string, t *testing.T) (content string) { 239 f, err := os.Open(src) 240 if err != nil { 241 t.Fatal(err) 242 } 243 data, err := ioutil.ReadAll(f) 244 if err != nil { 245 t.Fatal(err) 246 } 247 return string(data) 248 } 249 250 // Create a test container from the given daemon `r` and run arguments `args`. 251 // If the image name is "_", (eg. []string{"-i", "-t", "_", "bash"}, it is 252 // dynamically replaced by the current test image. 253 // The caller is responsible for destroying the container. 254 // Call t.Fatal() at the first error. 255 func mkContainer(r *daemon.Daemon, args []string, t *testing.T) (*daemon.Container, *runconfig.HostConfig, error) { 256 config, hc, _, err := parseRun(args) 257 defer func() { 258 if err != nil && t != nil { 259 t.Fatal(err) 260 } 261 }() 262 if err != nil { 263 return nil, nil, err 264 } 265 if config.Image == "_" { 266 config.Image = GetTestImage(r).ID 267 } 268 c, _, err := r.Create(config, nil, "") 269 if err != nil { 270 return nil, nil, err 271 } 272 // NOTE: hostConfig is ignored. 273 // If `args` specify privileged mode, custom lxc conf, external mount binds, 274 // port redirects etc. they will be ignored. 275 // This is because the correct way to set these things is to pass environment 276 // to the `start` job. 277 // FIXME: this helper function should be deprecated in favor of calling 278 // `create` and `start` jobs directly. 279 return c, hc, nil 280 } 281 282 // Create a test container, start it, wait for it to complete, destroy it, 283 // and return its standard output as a string. 284 // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. 285 // If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally. 286 func runContainer(eng *engine.Engine, r *daemon.Daemon, args []string, t *testing.T) (output string, err error) { 287 defer func() { 288 if err != nil && t != nil { 289 t.Fatal(err) 290 } 291 }() 292 container, hc, err := mkContainer(r, args, t) 293 if err != nil { 294 return "", err 295 } 296 defer r.Destroy(container) 297 stdout := container.StdoutPipe() 298 defer stdout.Close() 299 300 job := eng.Job("start", container.ID) 301 if err := job.ImportEnv(hc); err != nil { 302 return "", err 303 } 304 if err := job.Run(); err != nil { 305 return "", err 306 } 307 308 container.WaitStop(-1 * time.Second) 309 data, err := ioutil.ReadAll(stdout) 310 if err != nil { 311 return "", err 312 } 313 output = string(data) 314 return 315 } 316 317 // FIXME: this is duplicated from graph_test.go in the docker package. 318 func fakeTar() (io.ReadCloser, error) { 319 content := []byte("Hello world!\n") 320 buf := new(bytes.Buffer) 321 tw := tar.NewWriter(buf) 322 for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} { 323 hdr := new(tar.Header) 324 hdr.Size = int64(len(content)) 325 hdr.Name = name 326 if err := tw.WriteHeader(hdr); err != nil { 327 return nil, err 328 } 329 tw.Write([]byte(content)) 330 } 331 tw.Close() 332 return ioutil.NopCloser(buf), nil 333 } 334 335 func getAllImages(eng *engine.Engine, t *testing.T) *engine.Table { 336 return getImages(eng, t, true, "") 337 } 338 339 func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) *engine.Table { 340 job := eng.Job("images") 341 job.SetenvBool("all", all) 342 job.Setenv("filter", filter) 343 images, err := job.Stdout.AddListTable() 344 if err != nil { 345 t.Fatal(err) 346 } 347 if err := job.Run(); err != nil { 348 t.Fatal(err) 349 } 350 return images 351 352 } 353 354 func parseRun(args []string) (*runconfig.Config, *runconfig.HostConfig, *flag.FlagSet, error) { 355 cmd := flag.NewFlagSet("run", flag.ContinueOnError) 356 cmd.SetOutput(ioutil.Discard) 357 cmd.Usage = nil 358 return runconfig.Parse(cmd, args) 359 }