github.com/lmars/docker@v1.6.0-rc2/api/server/server_unit_test.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "net/http/httptest" 10 "reflect" 11 "strings" 12 "testing" 13 14 "github.com/docker/docker/api" 15 "github.com/docker/docker/engine" 16 "github.com/docker/docker/pkg/version" 17 ) 18 19 func TestGetBoolParam(t *testing.T) { 20 if ret, err := getBoolParam("true"); err != nil || !ret { 21 t.Fatalf("true -> true, nil | got %t %s", ret, err) 22 } 23 if ret, err := getBoolParam("True"); err != nil || !ret { 24 t.Fatalf("True -> true, nil | got %t %s", ret, err) 25 } 26 if ret, err := getBoolParam("1"); err != nil || !ret { 27 t.Fatalf("1 -> true, nil | got %t %s", ret, err) 28 } 29 if ret, err := getBoolParam(""); err != nil || ret { 30 t.Fatalf("\"\" -> false, nil | got %t %s", ret, err) 31 } 32 if ret, err := getBoolParam("false"); err != nil || ret { 33 t.Fatalf("false -> false, nil | got %t %s", ret, err) 34 } 35 if ret, err := getBoolParam("0"); err != nil || ret { 36 t.Fatalf("0 -> false, nil | got %t %s", ret, err) 37 } 38 if ret, err := getBoolParam("faux"); err == nil || ret { 39 t.Fatalf("faux -> false, err | got %t %s", ret, err) 40 41 } 42 } 43 44 func TesthttpError(t *testing.T) { 45 r := httptest.NewRecorder() 46 47 httpError(r, fmt.Errorf("No such method")) 48 if r.Code != http.StatusNotFound { 49 t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code) 50 } 51 52 httpError(r, fmt.Errorf("This accound hasn't been activated")) 53 if r.Code != http.StatusForbidden { 54 t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code) 55 } 56 57 httpError(r, fmt.Errorf("Some error")) 58 if r.Code != http.StatusInternalServerError { 59 t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code) 60 } 61 } 62 63 func TestGetVersion(t *testing.T) { 64 eng := engine.New() 65 var called bool 66 eng.Register("version", func(job *engine.Job) engine.Status { 67 called = true 68 v := &engine.Env{} 69 v.SetJson("Version", "42.1") 70 v.Set("ApiVersion", "1.1.1.1.1") 71 v.Set("GoVersion", "2.42") 72 v.Set("Os", "Linux") 73 v.Set("Arch", "x86_64") 74 if _, err := v.WriteTo(job.Stdout); err != nil { 75 return job.Error(err) 76 } 77 return engine.StatusOK 78 }) 79 r := serveRequest("GET", "/version", nil, eng, t) 80 if !called { 81 t.Fatalf("handler was not called") 82 } 83 v := readEnv(r.Body, t) 84 if v.Get("Version") != "42.1" { 85 t.Fatalf("%#v\n", v) 86 } 87 if r.HeaderMap.Get("Content-Type") != "application/json" { 88 t.Fatalf("%#v\n", r) 89 } 90 } 91 92 func TestGetInfo(t *testing.T) { 93 eng := engine.New() 94 var called bool 95 eng.Register("info", func(job *engine.Job) engine.Status { 96 called = true 97 v := &engine.Env{} 98 v.SetInt("Containers", 1) 99 v.SetInt("Images", 42000) 100 if _, err := v.WriteTo(job.Stdout); err != nil { 101 return job.Error(err) 102 } 103 return engine.StatusOK 104 }) 105 r := serveRequest("GET", "/info", nil, eng, t) 106 if !called { 107 t.Fatalf("handler was not called") 108 } 109 v := readEnv(r.Body, t) 110 if v.GetInt("Images") != 42000 { 111 t.Fatalf("%#v\n", v) 112 } 113 if v.GetInt("Containers") != 1 { 114 t.Fatalf("%#v\n", v) 115 } 116 assertContentType(r, "application/json", t) 117 } 118 119 func TestGetImagesJSON(t *testing.T) { 120 eng := engine.New() 121 var called bool 122 eng.Register("images", func(job *engine.Job) engine.Status { 123 called = true 124 v := createEnvFromGetImagesJSONStruct(sampleImage) 125 if _, err := v.WriteTo(job.Stdout); err != nil { 126 return job.Error(err) 127 } 128 return engine.StatusOK 129 }) 130 r := serveRequest("GET", "/images/json", nil, eng, t) 131 if !called { 132 t.Fatal("handler was not called") 133 } 134 assertHttpNotError(r, t) 135 assertContentType(r, "application/json", t) 136 var observed getImagesJSONStruct 137 if err := json.Unmarshal(r.Body.Bytes(), &observed); err != nil { 138 t.Fatal(err) 139 } 140 if !reflect.DeepEqual(observed, sampleImage) { 141 t.Errorf("Expected %#v but got %#v", sampleImage, observed) 142 } 143 } 144 145 func TestGetImagesJSONFilter(t *testing.T) { 146 eng := engine.New() 147 filter := "nothing" 148 eng.Register("images", func(job *engine.Job) engine.Status { 149 filter = job.Getenv("filter") 150 return engine.StatusOK 151 }) 152 serveRequest("GET", "/images/json?filter=aaaa", nil, eng, t) 153 if filter != "aaaa" { 154 t.Errorf("%#v", filter) 155 } 156 } 157 158 func TestGetImagesJSONFilters(t *testing.T) { 159 eng := engine.New() 160 filter := "nothing" 161 eng.Register("images", func(job *engine.Job) engine.Status { 162 filter = job.Getenv("filters") 163 return engine.StatusOK 164 }) 165 serveRequest("GET", "/images/json?filters=nnnn", nil, eng, t) 166 if filter != "nnnn" { 167 t.Errorf("%#v", filter) 168 } 169 } 170 171 func TestGetImagesJSONAll(t *testing.T) { 172 eng := engine.New() 173 allFilter := "-1" 174 eng.Register("images", func(job *engine.Job) engine.Status { 175 allFilter = job.Getenv("all") 176 return engine.StatusOK 177 }) 178 serveRequest("GET", "/images/json?all=1", nil, eng, t) 179 if allFilter != "1" { 180 t.Errorf("%#v", allFilter) 181 } 182 } 183 184 func TestGetImagesJSONLegacyFormat(t *testing.T) { 185 eng := engine.New() 186 var called bool 187 eng.Register("images", func(job *engine.Job) engine.Status { 188 called = true 189 outsLegacy := engine.NewTable("Created", 0) 190 outsLegacy.Add(createEnvFromGetImagesJSONStruct(sampleImage)) 191 if _, err := outsLegacy.WriteListTo(job.Stdout); err != nil { 192 return job.Error(err) 193 } 194 return engine.StatusOK 195 }) 196 r := serveRequestUsingVersion("GET", "/images/json", "1.6", nil, eng, t) 197 if !called { 198 t.Fatal("handler was not called") 199 } 200 assertHttpNotError(r, t) 201 assertContentType(r, "application/json", t) 202 images := engine.NewTable("Created", 0) 203 if _, err := images.ReadListFrom(r.Body.Bytes()); err != nil { 204 t.Fatal(err) 205 } 206 if images.Len() != 1 { 207 t.Fatalf("Expected 1 image, %d found", images.Len()) 208 } 209 image := images.Data[0] 210 if image.Get("Tag") != "test-tag" { 211 t.Errorf("Expected tag 'test-tag', found '%s'", image.Get("Tag")) 212 } 213 if image.Get("Repository") != "test-name" { 214 t.Errorf("Expected repository 'test-name', found '%s'", image.Get("Repository")) 215 } 216 } 217 218 func TestGetContainersByName(t *testing.T) { 219 eng := engine.New() 220 name := "container_name" 221 var called bool 222 eng.Register("container_inspect", func(job *engine.Job) engine.Status { 223 called = true 224 if job.Args[0] != name { 225 t.Errorf("name != '%s': %#v", name, job.Args[0]) 226 } 227 if api.APIVERSION.LessThan("1.12") && !job.GetenvBool("dirty") { 228 t.Errorf("dirty env variable not set") 229 } else if api.APIVERSION.GreaterThanOrEqualTo("1.12") && job.GetenvBool("dirty") { 230 t.Errorf("dirty env variable set when it shouldn't") 231 } 232 v := &engine.Env{} 233 v.SetBool("dirty", true) 234 if _, err := v.WriteTo(job.Stdout); err != nil { 235 return job.Error(err) 236 } 237 return engine.StatusOK 238 }) 239 r := serveRequest("GET", "/containers/"+name+"/json", nil, eng, t) 240 if !called { 241 t.Fatal("handler was not called") 242 } 243 assertContentType(r, "application/json", t) 244 var stdoutJson interface{} 245 if err := json.Unmarshal(r.Body.Bytes(), &stdoutJson); err != nil { 246 t.Fatalf("%#v", err) 247 } 248 if stdoutJson.(map[string]interface{})["dirty"].(float64) != 1 { 249 t.Fatalf("%#v", stdoutJson) 250 } 251 } 252 253 func TestGetEvents(t *testing.T) { 254 eng := engine.New() 255 var called bool 256 eng.Register("events", func(job *engine.Job) engine.Status { 257 called = true 258 since := job.Getenv("since") 259 if since != "1" { 260 t.Fatalf("'since' should be 1, found %#v instead", since) 261 } 262 until := job.Getenv("until") 263 if until != "0" { 264 t.Fatalf("'until' should be 0, found %#v instead", until) 265 } 266 v := &engine.Env{} 267 v.Set("since", since) 268 v.Set("until", until) 269 if _, err := v.WriteTo(job.Stdout); err != nil { 270 return job.Error(err) 271 } 272 return engine.StatusOK 273 }) 274 r := serveRequest("GET", "/events?since=1&until=0", nil, eng, t) 275 if !called { 276 t.Fatal("handler was not called") 277 } 278 assertContentType(r, "application/json", t) 279 var stdout_json struct { 280 Since int 281 Until int 282 } 283 if err := json.Unmarshal(r.Body.Bytes(), &stdout_json); err != nil { 284 t.Fatal(err) 285 } 286 if stdout_json.Since != 1 { 287 t.Errorf("since != 1: %#v", stdout_json.Since) 288 } 289 if stdout_json.Until != 0 { 290 t.Errorf("until != 0: %#v", stdout_json.Until) 291 } 292 } 293 294 func TestLogs(t *testing.T) { 295 eng := engine.New() 296 var inspect bool 297 var logs bool 298 eng.Register("container_inspect", func(job *engine.Job) engine.Status { 299 inspect = true 300 if len(job.Args) == 0 { 301 t.Fatal("Job arguments is empty") 302 } 303 if job.Args[0] != "test" { 304 t.Fatalf("Container name %s, must be test", job.Args[0]) 305 } 306 return engine.StatusOK 307 }) 308 expected := "logs" 309 eng.Register("logs", func(job *engine.Job) engine.Status { 310 logs = true 311 if len(job.Args) == 0 { 312 t.Fatal("Job arguments is empty") 313 } 314 if job.Args[0] != "test" { 315 t.Fatalf("Container name %s, must be test", job.Args[0]) 316 } 317 follow := job.Getenv("follow") 318 if follow != "1" { 319 t.Fatalf("follow: %s, must be 1", follow) 320 } 321 stdout := job.Getenv("stdout") 322 if stdout != "1" { 323 t.Fatalf("stdout %s, must be 1", stdout) 324 } 325 stderr := job.Getenv("stderr") 326 if stderr != "" { 327 t.Fatalf("stderr %s, must be empty", stderr) 328 } 329 timestamps := job.Getenv("timestamps") 330 if timestamps != "1" { 331 t.Fatalf("timestamps %s, must be 1", timestamps) 332 } 333 job.Stdout.Write([]byte(expected)) 334 return engine.StatusOK 335 }) 336 r := serveRequest("GET", "/containers/test/logs?follow=1&stdout=1×tamps=1", nil, eng, t) 337 if r.Code != http.StatusOK { 338 t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK) 339 } 340 if !inspect { 341 t.Fatal("container_inspect job was not called") 342 } 343 if !logs { 344 t.Fatal("logs job was not called") 345 } 346 res := r.Body.String() 347 if res != expected { 348 t.Fatalf("Output %s, expected %s", res, expected) 349 } 350 } 351 352 func TestLogsNoStreams(t *testing.T) { 353 eng := engine.New() 354 var inspect bool 355 var logs bool 356 eng.Register("container_inspect", func(job *engine.Job) engine.Status { 357 inspect = true 358 if len(job.Args) == 0 { 359 t.Fatal("Job arguments is empty") 360 } 361 if job.Args[0] != "test" { 362 t.Fatalf("Container name %s, must be test", job.Args[0]) 363 } 364 return engine.StatusOK 365 }) 366 eng.Register("logs", func(job *engine.Job) engine.Status { 367 logs = true 368 return engine.StatusOK 369 }) 370 r := serveRequest("GET", "/containers/test/logs", nil, eng, t) 371 if r.Code != http.StatusBadRequest { 372 t.Fatalf("Got status %d, expected %d", r.Code, http.StatusBadRequest) 373 } 374 if inspect { 375 t.Fatal("container_inspect job was called, but it shouldn't") 376 } 377 if logs { 378 t.Fatal("logs job was called, but it shouldn't") 379 } 380 res := strings.TrimSpace(r.Body.String()) 381 expected := "Bad parameters: you must choose at least one stream" 382 if !strings.Contains(res, expected) { 383 t.Fatalf("Output %s, expected %s in it", res, expected) 384 } 385 } 386 387 func TestGetImagesHistory(t *testing.T) { 388 eng := engine.New() 389 imageName := "docker-test-image" 390 var called bool 391 eng.Register("history", func(job *engine.Job) engine.Status { 392 called = true 393 if len(job.Args) == 0 { 394 t.Fatal("Job arguments is empty") 395 } 396 if job.Args[0] != imageName { 397 t.Fatalf("name != '%s': %#v", imageName, job.Args[0]) 398 } 399 v := &engine.Env{} 400 if _, err := v.WriteTo(job.Stdout); err != nil { 401 return job.Error(err) 402 } 403 return engine.StatusOK 404 }) 405 r := serveRequest("GET", "/images/"+imageName+"/history", nil, eng, t) 406 if !called { 407 t.Fatalf("handler was not called") 408 } 409 if r.Code != http.StatusOK { 410 t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK) 411 } 412 if r.HeaderMap.Get("Content-Type") != "application/json" { 413 t.Fatalf("%#v\n", r) 414 } 415 } 416 417 func TestGetImagesByName(t *testing.T) { 418 eng := engine.New() 419 name := "image_name" 420 var called bool 421 eng.Register("image_inspect", func(job *engine.Job) engine.Status { 422 called = true 423 if job.Args[0] != name { 424 t.Fatalf("name != '%s': %#v", name, job.Args[0]) 425 } 426 if api.APIVERSION.LessThan("1.12") && !job.GetenvBool("dirty") { 427 t.Fatal("dirty env variable not set") 428 } else if api.APIVERSION.GreaterThanOrEqualTo("1.12") && job.GetenvBool("dirty") { 429 t.Fatal("dirty env variable set when it shouldn't") 430 } 431 v := &engine.Env{} 432 v.SetBool("dirty", true) 433 if _, err := v.WriteTo(job.Stdout); err != nil { 434 return job.Error(err) 435 } 436 return engine.StatusOK 437 }) 438 r := serveRequest("GET", "/images/"+name+"/json", nil, eng, t) 439 if !called { 440 t.Fatal("handler was not called") 441 } 442 if r.HeaderMap.Get("Content-Type") != "application/json" { 443 t.Fatalf("%#v\n", r) 444 } 445 var stdoutJson interface{} 446 if err := json.Unmarshal(r.Body.Bytes(), &stdoutJson); err != nil { 447 t.Fatalf("%#v", err) 448 } 449 if stdoutJson.(map[string]interface{})["dirty"].(float64) != 1 { 450 t.Fatalf("%#v", stdoutJson) 451 } 452 } 453 454 func TestDeleteContainers(t *testing.T) { 455 eng := engine.New() 456 name := "foo" 457 var called bool 458 eng.Register("rm", func(job *engine.Job) engine.Status { 459 called = true 460 if len(job.Args) == 0 { 461 t.Fatalf("Job arguments is empty") 462 } 463 if job.Args[0] != name { 464 t.Fatalf("name != '%s': %#v", name, job.Args[0]) 465 } 466 return engine.StatusOK 467 }) 468 r := serveRequest("DELETE", "/containers/"+name, nil, eng, t) 469 if !called { 470 t.Fatalf("handler was not called") 471 } 472 if r.Code != http.StatusNoContent { 473 t.Fatalf("Got status %d, expected %d", r.Code, http.StatusNoContent) 474 } 475 } 476 477 func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder { 478 return serveRequestUsingVersion(method, target, api.APIVERSION, body, eng, t) 479 } 480 481 func serveRequestUsingVersion(method, target string, version version.Version, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder { 482 r := httptest.NewRecorder() 483 req, err := http.NewRequest(method, target, body) 484 if err != nil { 485 t.Fatal(err) 486 } 487 ServeRequest(eng, version, r, req) 488 return r 489 } 490 491 func readEnv(src io.Reader, t *testing.T) *engine.Env { 492 out := engine.NewOutput() 493 v, err := out.AddEnv() 494 if err != nil { 495 t.Fatal(err) 496 } 497 if _, err := io.Copy(out, src); err != nil { 498 t.Fatal(err) 499 } 500 out.Close() 501 return v 502 } 503 504 func toJson(data interface{}, t *testing.T) io.Reader { 505 var buf bytes.Buffer 506 if err := json.NewEncoder(&buf).Encode(data); err != nil { 507 t.Fatal(err) 508 } 509 return &buf 510 } 511 512 func assertContentType(recorder *httptest.ResponseRecorder, content_type string, t *testing.T) { 513 if recorder.HeaderMap.Get("Content-Type") != content_type { 514 t.Fatalf("%#v\n", recorder) 515 } 516 } 517 518 // XXX: Duplicated from integration/utils_test.go, but maybe that's OK as that 519 // should die as soon as we converted all integration tests? 520 // assertHttpNotError expect the given response to not have an error. 521 // Otherwise the it causes the test to fail. 522 func assertHttpNotError(r *httptest.ResponseRecorder, t *testing.T) { 523 // Non-error http status are [200, 400) 524 if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest { 525 t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code)) 526 } 527 } 528 529 func createEnvFromGetImagesJSONStruct(data getImagesJSONStruct) *engine.Env { 530 v := &engine.Env{} 531 v.SetList("RepoTags", data.RepoTags) 532 v.Set("Id", data.Id) 533 v.SetInt64("Created", data.Created) 534 v.SetInt64("Size", data.Size) 535 v.SetInt64("VirtualSize", data.VirtualSize) 536 return v 537 } 538 539 type getImagesJSONStruct struct { 540 RepoTags []string 541 Id string 542 Created int64 543 Size int64 544 VirtualSize int64 545 } 546 547 var sampleImage getImagesJSONStruct = getImagesJSONStruct{ 548 RepoTags: []string{"test-name:test-tag"}, 549 Id: "ID", 550 Created: 999, 551 Size: 777, 552 VirtualSize: 666, 553 }