github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/boskos/boskos_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "encoding/json" 21 "net/http" 22 "net/http/httptest" 23 "net/url" 24 "reflect" 25 "testing" 26 "time" 27 28 "k8s.io/test-infra/boskos/common" 29 "k8s.io/test-infra/boskos/crds" 30 "k8s.io/test-infra/boskos/ranch" 31 ) 32 33 func MakeTestRanch(resources []common.Resource) *ranch.Ranch { 34 resourceClient := crds.NewTestResourceClient() 35 s, _ := ranch.NewStorage(crds.NewCRDStorage(resourceClient), "") 36 for _, r := range resources { 37 s.AddResource(r) 38 } 39 r, _ := ranch.NewRanch("", s) 40 return r 41 } 42 43 func TestAcquire(t *testing.T) { 44 var testcases = []struct { 45 name string 46 resources []common.Resource 47 path string 48 code int 49 method string 50 }{ 51 { 52 name: "reject get method", 53 resources: []common.Resource{}, 54 path: "?type=t&state=s&dest=d&owner=o", 55 code: http.StatusMethodNotAllowed, 56 method: http.MethodGet, 57 }, 58 { 59 name: "reject request no arg", 60 resources: []common.Resource{}, 61 path: "", 62 code: http.StatusBadRequest, 63 method: http.MethodPost, 64 }, 65 { 66 name: "reject request missing type", 67 resources: []common.Resource{}, 68 path: "?state=s&dest=d&owner=o", 69 code: http.StatusBadRequest, 70 method: http.MethodPost, 71 }, 72 { 73 name: "reject request missing state", 74 resources: []common.Resource{}, 75 path: "?type=t&dest=d&owner=o", 76 code: http.StatusBadRequest, 77 method: http.MethodPost, 78 }, 79 { 80 name: "reject request missing owner", 81 resources: []common.Resource{}, 82 path: "?type=t&state=s&dest=d", 83 code: http.StatusBadRequest, 84 method: http.MethodPost, 85 }, 86 { 87 name: "reject request missing dest", 88 resources: []common.Resource{}, 89 path: "?type=t&state=s&owner=o", 90 code: http.StatusBadRequest, 91 method: http.MethodPost, 92 }, 93 { 94 name: "ranch has no resource", 95 resources: []common.Resource{}, 96 path: "?type=t&state=s&dest=d&owner=o", 97 code: http.StatusNotFound, 98 method: http.MethodPost, 99 }, 100 { 101 name: "no match type", 102 resources: []common.Resource{ 103 { 104 Name: "res", 105 Type: "wrong", 106 State: "s", 107 Owner: "", 108 }, 109 }, 110 path: "?type=t&state=s&dest=d&owner=o", 111 code: http.StatusNotFound, 112 method: http.MethodPost, 113 }, 114 { 115 name: "no match state", 116 resources: []common.Resource{ 117 { 118 Name: "res", 119 Type: "t", 120 State: "wrong", 121 Owner: "", 122 }, 123 }, 124 path: "?type=t&state=s&dest=d&owner=o", 125 code: http.StatusNotFound, 126 method: http.MethodPost, 127 }, 128 { 129 name: "busy", 130 resources: []common.Resource{ 131 { 132 Name: "res", 133 Type: "t", 134 State: "s", 135 Owner: "user", 136 }, 137 }, 138 path: "?type=t&state=s&dest=d&owner=o", 139 code: http.StatusNotFound, 140 method: http.MethodPost, 141 }, 142 { 143 name: "ok", 144 resources: []common.Resource{ 145 { 146 Name: "res", 147 Type: "t", 148 State: "s", 149 Owner: "", 150 }, 151 }, 152 path: "?type=t&state=s&dest=d&owner=o", 153 code: http.StatusOK, 154 method: http.MethodPost, 155 }, 156 } 157 158 for _, tc := range testcases { 159 c := MakeTestRanch(tc.resources) 160 handler := handleAcquire(c) 161 req, err := http.NewRequest(tc.method, "", nil) 162 if err != nil { 163 t.Fatalf("Error making request: %v", err) 164 } 165 u, err := url.Parse(tc.path) 166 if err != nil { 167 t.Fatalf("Error parsing URL: %v", err) 168 } 169 req.URL = u 170 rr := httptest.NewRecorder() 171 handler.ServeHTTP(rr, req) 172 if rr.Code != tc.code { 173 t.Errorf("%s - Wrong error code. Got %v, expect %v", tc.name, rr.Code, tc.code) 174 } 175 176 if rr.Code == http.StatusOK { 177 var data common.Resource 178 json.Unmarshal(rr.Body.Bytes(), &data) 179 if data.Name != "res" { 180 t.Errorf("%s - Got res %v, expect res", tc.name, data.Name) 181 } 182 183 if data.State != "d" { 184 t.Errorf("%s - Got state %v, expect d", tc.name, data.State) 185 } 186 187 resources, err := c.Storage.GetResources() 188 if err != nil { 189 t.Error("cannot get resources") 190 continue 191 } 192 if resources[0].Owner != "o" { 193 t.Errorf("%s - Wrong owner. Got %v, expect o", tc.name, resources[0].Owner) 194 } 195 } 196 } 197 } 198 199 func TestRelease(t *testing.T) { 200 var testcases = []struct { 201 name string 202 resources []common.Resource 203 path string 204 code int 205 method string 206 }{ 207 { 208 name: "reject get method", 209 resources: []common.Resource{}, 210 path: "?name=res&dest=d&owner=foo", 211 code: http.StatusMethodNotAllowed, 212 method: http.MethodGet, 213 }, 214 { 215 name: "reject request no arg", 216 resources: []common.Resource{}, 217 path: "", 218 code: http.StatusBadRequest, 219 method: http.MethodPost, 220 }, 221 { 222 name: "reject request missing name", 223 resources: []common.Resource{}, 224 path: "?dest=d&owner=foo", 225 code: http.StatusBadRequest, 226 method: http.MethodPost, 227 }, 228 { 229 name: "reject request missing dest", 230 resources: []common.Resource{}, 231 path: "?name=res&owner=foo", 232 code: http.StatusBadRequest, 233 method: http.MethodPost, 234 }, 235 { 236 name: "reject request missing owner", 237 resources: []common.Resource{}, 238 path: "?name=res&dest=d", 239 code: http.StatusBadRequest, 240 method: http.MethodPost, 241 }, 242 { 243 name: "ranch has no resource", 244 resources: []common.Resource{}, 245 path: "?name=res&dest=d&owner=foo", 246 code: http.StatusNotFound, 247 method: http.MethodPost, 248 }, 249 { 250 name: "wrong owner", 251 resources: []common.Resource{ 252 { 253 Name: "res", 254 Type: "t", 255 State: "s", 256 Owner: "merlin", 257 }, 258 }, 259 path: "?name=res&dest=d&owner=foo", 260 code: http.StatusUnauthorized, 261 method: http.MethodPost, 262 }, 263 { 264 name: "no match name", 265 resources: []common.Resource{ 266 { 267 Name: "foo", 268 Type: "t", 269 State: "s", 270 Owner: "merlin", 271 }, 272 }, 273 path: "?name=res&dest=d&owner=merlin", 274 code: http.StatusNotFound, 275 method: http.MethodPost, 276 }, 277 { 278 name: "ok", 279 resources: []common.Resource{ 280 { 281 Name: "res", 282 Type: "t", 283 State: "s", 284 Owner: "merlin", 285 }, 286 }, 287 path: "?name=res&dest=d&owner=merlin", 288 code: http.StatusOK, 289 method: http.MethodPost, 290 }, 291 } 292 293 for _, tc := range testcases { 294 c := MakeTestRanch(tc.resources) 295 handler := handleRelease(c) 296 req, err := http.NewRequest(tc.method, "", nil) 297 if err != nil { 298 t.Fatalf("Error making request: %v", err) 299 } 300 u, err := url.Parse(tc.path) 301 if err != nil { 302 t.Fatalf("Error parsing URL: %v", err) 303 } 304 req.URL = u 305 rr := httptest.NewRecorder() 306 handler.ServeHTTP(rr, req) 307 if rr.Code != tc.code { 308 t.Errorf("%s - Wrong error code. Got %v, expect %v", tc.name, rr.Code, tc.code) 309 } 310 311 if rr.Code == http.StatusOK { 312 resources, err := c.Storage.GetResources() 313 if err != nil { 314 t.Error("cannot get resources") 315 continue 316 } 317 if resources[0].State != "d" { 318 t.Errorf("%s - Wrong state. Got %v, expect d", tc.name, resources[0].State) 319 } 320 321 if resources[0].Owner != "" { 322 t.Errorf("%s - Wrong owner. Got %v, expect empty", tc.name, resources[0].Owner) 323 } 324 } 325 } 326 } 327 328 func TestReset(t *testing.T) { 329 var testcases = []struct { 330 name string 331 resources []common.Resource 332 path string 333 code int 334 method string 335 hasContent bool 336 }{ 337 { 338 name: "reject get method", 339 resources: []common.Resource{}, 340 path: "?type=t&state=s&expire=10m&dest=d", 341 code: http.StatusMethodNotAllowed, 342 method: http.MethodGet, 343 }, 344 { 345 name: "reject request no arg", 346 resources: []common.Resource{}, 347 path: "", 348 code: http.StatusBadRequest, 349 method: http.MethodPost, 350 }, 351 { 352 name: "reject request missing type", 353 resources: []common.Resource{}, 354 path: "?state=s&expire=10m&dest=d", 355 code: http.StatusBadRequest, 356 method: http.MethodPost, 357 }, 358 { 359 name: "reject request missing state", 360 resources: []common.Resource{}, 361 path: "?type=t&expire=10m&dest=d", 362 code: http.StatusBadRequest, 363 method: http.MethodPost, 364 }, 365 { 366 name: "reject request missing expire", 367 resources: []common.Resource{}, 368 path: "?type=t&state=s&dest=d", 369 code: http.StatusBadRequest, 370 method: http.MethodPost, 371 }, 372 { 373 name: "reject request missing dest", 374 resources: []common.Resource{}, 375 path: "?type=t&state=s&expire=10m", 376 code: http.StatusBadRequest, 377 method: http.MethodPost, 378 }, 379 { 380 name: "reject request bad expire", 381 resources: []common.Resource{}, 382 path: "?type=t&state=s&expire=woooo&dest=d", 383 code: http.StatusBadRequest, 384 method: http.MethodPost, 385 }, 386 { 387 name: "empty - has no owner", 388 resources: []common.Resource{ 389 { 390 Name: "res", 391 Type: "t", 392 State: "s", 393 Owner: "", 394 LastUpdate: time.Now().Add(-time.Minute * 20), 395 }, 396 }, 397 path: "?type=t&state=s&expire=10m&dest=d", 398 code: http.StatusOK, 399 method: http.MethodPost, 400 }, 401 { 402 name: "empty - not expire", 403 resources: []common.Resource{ 404 { 405 Name: "res", 406 Type: "t", 407 State: "s", 408 Owner: "", 409 LastUpdate: time.Now(), 410 }, 411 }, 412 path: "?type=t&state=s&expire=10m&dest=d", 413 code: http.StatusOK, 414 method: http.MethodPost, 415 }, 416 { 417 name: "empty - no match type", 418 resources: []common.Resource{ 419 { 420 Name: "res", 421 Type: "wrong", 422 State: "s", 423 Owner: "", 424 LastUpdate: time.Now().Add(-time.Minute * 20), 425 }, 426 }, 427 path: "?type=t&state=s&expire=10m&dest=d", 428 code: http.StatusOK, 429 method: http.MethodPost, 430 }, 431 { 432 name: "empty - no match state", 433 resources: []common.Resource{ 434 { 435 Name: "res", 436 Type: "t", 437 State: "wrong", 438 Owner: "", 439 LastUpdate: time.Now().Add(-time.Minute * 20), 440 }, 441 }, 442 path: "?type=t&state=s&expire=10m&dest=d", 443 code: http.StatusOK, 444 method: http.MethodPost, 445 }, 446 { 447 name: "ok", 448 resources: []common.Resource{ 449 { 450 Name: "res", 451 Type: "t", 452 State: "s", 453 Owner: "user", 454 LastUpdate: time.Now().Add(-time.Minute * 20), 455 }, 456 }, 457 path: "?type=t&state=s&expire=10m&dest=d", 458 code: http.StatusOK, 459 method: http.MethodPost, 460 hasContent: true, 461 }, 462 } 463 464 for _, tc := range testcases { 465 c := MakeTestRanch(tc.resources) 466 handler := handleReset(c) 467 req, err := http.NewRequest(tc.method, "", nil) 468 if err != nil { 469 t.Fatalf("Error making request: %v", err) 470 } 471 u, err := url.Parse(tc.path) 472 if err != nil { 473 t.Fatalf("Error parsing URL: %v", err) 474 } 475 req.URL = u 476 rr := httptest.NewRecorder() 477 handler.ServeHTTP(rr, req) 478 if rr.Code != tc.code { 479 t.Errorf("%s - Wrong error code. Got %v, expect %v", tc.name, rr.Code, tc.code) 480 } 481 482 if rr.Code == http.StatusOK { 483 rmap := make(map[string]string) 484 json.Unmarshal(rr.Body.Bytes(), &rmap) 485 if !tc.hasContent { 486 if len(rmap) != 0 { 487 t.Errorf("%s - Expect empty map. Got %v", tc.name, rmap) 488 } 489 } else { 490 if owner, ok := rmap["res"]; !ok || owner != "user" { 491 t.Errorf("%s - Expect res - user. Got %v", tc.name, rmap) 492 } 493 } 494 } 495 } 496 } 497 498 func TestUpdate(t *testing.T) { 499 FakeNow := time.Now() 500 501 var testcases = []struct { 502 name string 503 resources []common.Resource 504 path string 505 code int 506 method string 507 }{ 508 { 509 name: "reject get method", 510 resources: []common.Resource{}, 511 path: "?name=foo", 512 code: http.StatusMethodNotAllowed, 513 method: http.MethodGet, 514 }, 515 { 516 name: "reject request no arg", 517 resources: []common.Resource{}, 518 path: "", 519 code: http.StatusBadRequest, 520 method: http.MethodPost, 521 }, 522 { 523 name: "reject request missing name", 524 resources: []common.Resource{}, 525 path: "?state=s&owner=merlin", 526 code: http.StatusBadRequest, 527 method: http.MethodPost, 528 }, 529 { 530 name: "reject request missing owner", 531 resources: []common.Resource{}, 532 path: "?name=res&state=s", 533 code: http.StatusBadRequest, 534 method: http.MethodPost, 535 }, 536 { 537 name: "reject request missing state", 538 resources: []common.Resource{}, 539 path: "?name=res&owner=merlin", 540 code: http.StatusBadRequest, 541 method: http.MethodPost, 542 }, 543 { 544 name: "ranch has no resource", 545 resources: []common.Resource{}, 546 path: "?name=res&state=s&owner=merlin", 547 code: http.StatusNotFound, 548 method: http.MethodPost, 549 }, 550 { 551 name: "wrong owner", 552 resources: []common.Resource{ 553 { 554 Name: "res", 555 Type: "t", 556 State: "s", 557 Owner: "evil", 558 }, 559 }, 560 path: "?name=res&state=s&owner=merlin", 561 code: http.StatusUnauthorized, 562 method: http.MethodPost, 563 }, 564 { 565 name: "wrong state", 566 resources: []common.Resource{ 567 { 568 Name: "res", 569 Type: "t", 570 State: "s", 571 Owner: "merlin", 572 }, 573 }, 574 path: "?name=res&state=d&owner=merlin", 575 code: http.StatusConflict, 576 method: http.MethodPost, 577 }, 578 { 579 name: "no matched resource", 580 resources: []common.Resource{ 581 { 582 Name: "res", 583 Type: "t", 584 State: "s", 585 Owner: "merlin", 586 }, 587 }, 588 path: "?name=foo&state=s&owner=merlin", 589 code: http.StatusNotFound, 590 method: http.MethodPost, 591 }, 592 { 593 name: "ok", 594 resources: []common.Resource{ 595 { 596 Name: "res", 597 Type: "t", 598 State: "s", 599 Owner: "merlin", 600 LastUpdate: FakeNow, 601 }, 602 }, 603 path: "?name=res&state=s&owner=merlin", 604 code: http.StatusOK, 605 method: http.MethodPost, 606 }, 607 { 608 name: "ok", 609 resources: []common.Resource{ 610 { 611 Name: "res", 612 Type: "t", 613 State: "s", 614 Owner: "merlin", 615 LastUpdate: FakeNow, 616 }, 617 }, 618 path: "?name=res&state=s&owner=merlin", 619 code: http.StatusOK, 620 method: http.MethodPost, 621 }, 622 } 623 624 for _, tc := range testcases { 625 c := MakeTestRanch(tc.resources) 626 handler := handleUpdate(c) 627 req, err := http.NewRequest(tc.method, "", nil) 628 if err != nil { 629 t.Fatalf("Error making request: %v", err) 630 } 631 u, err := url.Parse(tc.path) 632 if err != nil { 633 t.Fatalf("Error parsing URL: %v", err) 634 } 635 req.URL = u 636 rr := httptest.NewRecorder() 637 handler.ServeHTTP(rr, req) 638 if rr.Code != tc.code { 639 t.Errorf("%s - Wrong error code. Got %v, expect %v", tc.name, rr.Code, tc.code) 640 } 641 642 if rr.Code == http.StatusOK { 643 resources, err := c.Storage.GetResources() 644 if err != nil { 645 t.Error("cannot get resources") 646 continue 647 } 648 if resources[0].LastUpdate == FakeNow { 649 t.Errorf("%s - Timestamp is not updated!", tc.name) 650 } 651 } 652 } 653 } 654 655 func TestGetMetric(t *testing.T) { 656 var testcases = []struct { 657 name string 658 resources []common.Resource 659 path string 660 code int 661 method string 662 expect common.Metric 663 }{ 664 { 665 name: "reject none-get method", 666 resources: []common.Resource{}, 667 path: "?type=t", 668 code: http.StatusMethodNotAllowed, 669 method: http.MethodPost, 670 }, 671 { 672 name: "reject request no type", 673 resources: []common.Resource{}, 674 path: "", 675 code: http.StatusBadRequest, 676 method: http.MethodGet, 677 }, 678 { 679 name: "ranch has no resource", 680 resources: []common.Resource{}, 681 path: "?type=t", 682 code: http.StatusNotFound, 683 method: http.MethodGet, 684 }, 685 { 686 name: "wrong type", 687 resources: []common.Resource{ 688 { 689 Name: "res", 690 Type: "t", 691 State: "s", 692 Owner: "evil", 693 }, 694 }, 695 path: "?type=foo", 696 code: http.StatusNotFound, 697 method: http.MethodGet, 698 }, 699 { 700 name: "ok", 701 resources: []common.Resource{ 702 { 703 Name: "res", 704 Type: "t", 705 State: "s", 706 Owner: "merlin", 707 }, 708 }, 709 path: "?type=t", 710 code: http.StatusOK, 711 method: http.MethodGet, 712 expect: common.Metric{ 713 Type: "t", 714 Current: map[string]int{ 715 "s": 1, 716 }, 717 Owners: map[string]int{ 718 "merlin": 1, 719 }, 720 }, 721 }, 722 } 723 724 for _, tc := range testcases { 725 c := MakeTestRanch(tc.resources) 726 handler := handleMetric(c) 727 req, err := http.NewRequest(tc.method, "", nil) 728 if err != nil { 729 t.Fatalf("Error making request: %v", err) 730 } 731 u, err := url.Parse(tc.path) 732 if err != nil { 733 t.Fatalf("Error parsing URL: %v", err) 734 } 735 req.URL = u 736 rr := httptest.NewRecorder() 737 handler.ServeHTTP(rr, req) 738 if rr.Code != tc.code { 739 t.Errorf("%s - Wrong error code. Got %v, expect %v", tc.name, rr.Code, tc.code) 740 } 741 742 if rr.Code == http.StatusOK { 743 var metric common.Metric 744 if err := json.Unmarshal(rr.Body.Bytes(), &metric); err != nil { 745 t.Errorf("%s - Fail to unmarshal body - %s", tc.name, err) 746 } 747 if !reflect.DeepEqual(metric, tc.expect) { 748 t.Errorf("%s - wrong metric, got %v, want %v", tc.name, metric, tc.expect) 749 } 750 } 751 } 752 } 753 754 func TestDefault(t *testing.T) { 755 var testcases = []struct { 756 name string 757 code int 758 }{ 759 { 760 name: "empty", 761 code: http.StatusOK, 762 }, 763 } 764 765 for _, tc := range testcases { 766 handler := handleDefault(nil) 767 req, err := http.NewRequest(http.MethodGet, "", nil) 768 if err != nil { 769 t.Fatalf("Error making request: %v", err) 770 } 771 rr := httptest.NewRecorder() 772 handler.ServeHTTP(rr, req) 773 if rr.Code != tc.code { 774 t.Errorf("%s - Wrong error code. Got %v, expect %v", tc.name, rr.Code, tc.code) 775 } 776 } 777 } 778 779 func TestConfig(t *testing.T) { 780 resources, err := ranch.ParseConfig("resources.yaml") 781 if err != nil { 782 t.Errorf("parseConfig error: %v", err) 783 } 784 785 if len(resources) == 0 { 786 t.Errorf("empty data") 787 } 788 resourceNames := map[string]bool{} 789 790 for _, p := range resources { 791 if p.Name == "" { 792 t.Errorf("empty resource name: %v", p.Name) 793 } 794 795 if _, ok := resourceNames[p.Name]; ok { 796 t.Errorf("duplicated resource name: %v", p.Name) 797 } else { 798 resourceNames[p.Name] = true 799 } 800 } 801 }