github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/boskos/ranch/ranch_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 ranch 18 19 import ( 20 "reflect" 21 "sort" 22 "testing" 23 "time" 24 25 "k8s.io/test-infra/boskos/common" 26 "k8s.io/test-infra/boskos/crds" 27 ) 28 29 func MakeTestRanch(resources []common.Resource) *Ranch { 30 rs := crds.NewCRDStorage(crds.NewTestResourceClient()) 31 s, _ := NewStorage(rs, "") 32 for _, res := range resources { 33 s.AddResource(res) 34 } 35 r, _ := NewRanch("", s) 36 return r 37 } 38 39 func AreErrorsEqual(got error, expect error) bool { 40 if got == nil && expect == nil { 41 return true 42 } 43 44 if got == nil || expect == nil { 45 return false 46 } 47 48 switch got.(type) { 49 case *OwnerNotMatch: 50 if o, ok := expect.(*OwnerNotMatch); ok { 51 if o.request == got.(*OwnerNotMatch).request && o.owner == got.(*OwnerNotMatch).owner { 52 return true 53 } 54 } 55 return false 56 case *ResourceNotFound: 57 if o, ok := expect.(*ResourceNotFound); ok { 58 if o.name == got.(*ResourceNotFound).name { 59 return true 60 } 61 } 62 return false 63 case *StateNotMatch: 64 if o, ok := expect.(*StateNotMatch); ok { 65 if o.expect == got.(*StateNotMatch).expect && o.current == got.(*StateNotMatch).current { 66 return true 67 } 68 } 69 return false 70 default: 71 return false 72 } 73 } 74 75 func TestAcquire(t *testing.T) { 76 FakeNow := time.Now() 77 var testcases = []struct { 78 name string 79 resources []common.Resource 80 owner string 81 rtype string 82 state string 83 dest string 84 expectErr error 85 }{ 86 { 87 name: "ranch has no resource", 88 resources: []common.Resource{}, 89 owner: "user", 90 rtype: "t", 91 state: "s", 92 dest: "d", 93 expectErr: &ResourceNotFound{"t"}, 94 }, 95 { 96 name: "no match type", 97 resources: []common.Resource{ 98 common.NewResource("res", "wrong", "s", "", FakeNow), 99 }, 100 owner: "user", 101 rtype: "t", 102 state: "s", 103 dest: "d", 104 expectErr: &ResourceNotFound{"t"}, 105 }, 106 { 107 name: "no match state", 108 resources: []common.Resource{ 109 common.NewResource("res", "t", "wrong", "", FakeNow), 110 }, 111 owner: "user", 112 rtype: "t", 113 state: "s", 114 dest: "d", 115 expectErr: &ResourceNotFound{"t"}, 116 }, 117 { 118 name: common.Busy, 119 resources: []common.Resource{ 120 common.NewResource("res", "t", "s", "foo", FakeNow), 121 }, 122 owner: "user", 123 rtype: "t", 124 state: "s", 125 dest: "d", 126 expectErr: &ResourceNotFound{"t"}, 127 }, 128 { 129 name: "ok", 130 resources: []common.Resource{ 131 common.NewResource("res", "t", "s", "", FakeNow), 132 }, 133 owner: "user", 134 rtype: "t", 135 state: "s", 136 dest: "d", 137 expectErr: nil, 138 }, 139 } 140 141 for _, tc := range testcases { 142 c := MakeTestRanch(tc.resources) 143 res, err := c.Acquire(tc.rtype, tc.state, tc.dest, tc.owner) 144 if !AreErrorsEqual(err, tc.expectErr) { 145 t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr) 146 continue 147 } 148 149 resources, err2 := c.Storage.GetResources() 150 if err2 != nil { 151 t.Errorf("failed to get resources") 152 continue 153 } 154 155 if err == nil { 156 if res.State != tc.dest { 157 t.Errorf("%s - Wrong final state. Got %v, expect %v", tc.name, res.State, tc.dest) 158 } 159 if !reflect.DeepEqual(*res, resources[0]) { 160 t.Errorf("%s - Wrong resource. Got %v, expect %v", tc.name, res, resources[0]) 161 } else if !res.LastUpdate.After(FakeNow) { 162 t.Errorf("%s - LastUpdate did not update.", tc.name) 163 } 164 } else { 165 for _, res := range resources { 166 if res.LastUpdate != FakeNow { 167 t.Errorf("%s - LastUpdate should not update. Got %v, expect %v", tc.name, resources[0].LastUpdate, FakeNow) 168 } 169 } 170 } 171 } 172 } 173 174 func TestAcquireRoundRobin(t *testing.T) { 175 FakeNow := time.Now() 176 var resources []common.Resource 177 for i := 1; i < 5; i++ { 178 resources = append(resources, common.NewResource("res-1", "t", "s", "", FakeNow)) 179 } 180 181 results := map[string]int{} 182 183 c := MakeTestRanch(resources) 184 for i := 0; i < 4; i++ { 185 res, err := c.Acquire("t", "s", "d", "foo") 186 if err != nil { 187 t.Fatalf("Unexpected error: %v", err) 188 } 189 _, found := results[res.Name] 190 if found { 191 t.Errorf("resource %s was used more than once", res.Name) 192 } 193 c.Release(res.Name, "s", "foo") 194 } 195 } 196 197 func TestRelease(t *testing.T) { 198 FakeNow := time.Now() 199 var testcases = []struct { 200 name string 201 resources []common.Resource 202 resName string 203 owner string 204 dest string 205 expectErr error 206 }{ 207 { 208 name: "ranch has no resource", 209 resources: []common.Resource{}, 210 resName: "res", 211 owner: "user", 212 dest: "d", 213 expectErr: &ResourceNotFound{"res"}, 214 }, 215 { 216 name: "wrong owner", 217 resources: []common.Resource{ 218 common.NewResource("res", "t", "s", "merlin", FakeNow), 219 }, 220 resName: "res", 221 owner: "user", 222 dest: "d", 223 expectErr: &OwnerNotMatch{"merlin", "user"}, 224 }, 225 { 226 name: "no match name", 227 resources: []common.Resource{ 228 common.NewResource("foo", "t", "s", "merlin", FakeNow), 229 }, 230 resName: "res", 231 owner: "user", 232 dest: "d", 233 expectErr: &ResourceNotFound{"res"}, 234 }, 235 { 236 name: "ok", 237 resources: []common.Resource{ 238 common.NewResource("res", "t", "s", "merlin", FakeNow), 239 }, 240 resName: "res", 241 owner: "merlin", 242 dest: "d", 243 expectErr: nil, 244 }, 245 } 246 247 for _, tc := range testcases { 248 c := MakeTestRanch(tc.resources) 249 err := c.Release(tc.resName, tc.dest, tc.owner) 250 if !AreErrorsEqual(err, tc.expectErr) { 251 t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr) 252 continue 253 } 254 resources, err2 := c.Storage.GetResources() 255 if err2 != nil { 256 t.Errorf("failed to get resources") 257 continue 258 } 259 if err == nil { 260 if resources[0].Owner != "" { 261 t.Errorf("%s - Wrong owner after release. Got %v, expect empty", tc.name, resources[0].Owner) 262 } else if resources[0].State != tc.dest { 263 t.Errorf("%s - Wrong state after release. Got %v, expect %v", tc.name, resources[0].State, tc.dest) 264 } else if !resources[0].LastUpdate.After(FakeNow) { 265 t.Errorf("%s - LastUpdate did not update.", tc.name) 266 } 267 } else { 268 for _, res := range resources { 269 if res.LastUpdate != FakeNow { 270 t.Errorf("%s - LastUpdate should not update. Got %v, expect %v", tc.name, resources[0].LastUpdate, FakeNow) 271 } 272 } 273 } 274 } 275 } 276 277 func TestReset(t *testing.T) { 278 FakeNow := time.Now() 279 280 var testcases = []struct { 281 name string 282 resources []common.Resource 283 rtype string 284 state string 285 dest string 286 expire time.Duration 287 hasContent bool 288 }{ 289 290 { 291 name: "empty - has no owner", 292 resources: []common.Resource{ 293 common.NewResource("res", "t", "s", "", FakeNow.Add(-time.Minute*20)), 294 }, 295 rtype: "t", 296 state: "s", 297 expire: time.Minute * 10, 298 dest: "d", 299 }, 300 { 301 name: "empty - not expire", 302 resources: []common.Resource{ 303 common.NewResource("res", "t", "s", "", FakeNow), 304 }, 305 rtype: "t", 306 state: "s", 307 expire: time.Minute * 10, 308 dest: "d", 309 }, 310 { 311 name: "empty - no match type", 312 resources: []common.Resource{ 313 common.NewResource("res", "wrong", "s", "", FakeNow.Add(-time.Minute*20)), 314 }, 315 rtype: "t", 316 state: "s", 317 expire: time.Minute * 10, 318 dest: "d", 319 }, 320 { 321 name: "empty - no match state", 322 resources: []common.Resource{ 323 common.NewResource("res", "t", "wrong", "", FakeNow.Add(-time.Minute*20)), 324 }, 325 rtype: "t", 326 state: "s", 327 expire: time.Minute * 10, 328 dest: "d", 329 }, 330 { 331 name: "ok", 332 resources: []common.Resource{ 333 common.NewResource("res", "t", "s", "user", FakeNow.Add(-time.Minute*20)), 334 }, 335 rtype: "t", 336 state: "s", 337 expire: time.Minute * 10, 338 dest: "d", 339 hasContent: true, 340 }, 341 } 342 343 for _, tc := range testcases { 344 c := MakeTestRanch(tc.resources) 345 rmap, err := c.Reset(tc.rtype, tc.state, tc.expire, tc.dest) 346 if err != nil { 347 t.Errorf("failed to reset %v", err) 348 } 349 350 if !tc.hasContent { 351 if len(rmap) != 0 { 352 t.Errorf("%s - Expect empty map. Got %v", tc.name, rmap) 353 } 354 } else { 355 if owner, ok := rmap["res"]; !ok || owner != "user" { 356 t.Errorf("%s - Expect res - user. Got %v", tc.name, rmap) 357 } 358 resources, err := c.Storage.GetResources() 359 if err != nil { 360 t.Errorf("failed to get resources") 361 continue 362 } 363 if !resources[0].LastUpdate.After(FakeNow) { 364 t.Errorf("%s - LastUpdate did not update.", tc.name) 365 } 366 } 367 } 368 } 369 370 func TestUpdate(t *testing.T) { 371 FakeNow := time.Now() 372 373 var testcases = []struct { 374 name string 375 resources []common.Resource 376 resName string 377 owner string 378 state string 379 expectErr error 380 }{ 381 { 382 name: "ranch has no resource", 383 resources: []common.Resource{}, 384 resName: "res", 385 owner: "user", 386 state: "s", 387 expectErr: &ResourceNotFound{"res"}, 388 }, 389 { 390 name: "wrong owner", 391 resources: []common.Resource{ 392 common.NewResource("res", "t", "s", "merlin", FakeNow), 393 }, 394 resName: "res", 395 owner: "user", 396 state: "s", 397 expectErr: &OwnerNotMatch{"user", "merlin"}, 398 }, 399 { 400 name: "wrong state", 401 resources: []common.Resource{ 402 common.NewResource("res", "t", "s", "merlin", FakeNow), 403 }, 404 resName: "res", 405 owner: "merlin", 406 state: "foo", 407 expectErr: &StateNotMatch{"s", "foo"}, 408 }, 409 { 410 name: "no matched resource", 411 resources: []common.Resource{ 412 common.NewResource("foo", "t", "s", "merlin", FakeNow), 413 }, 414 resName: "res", 415 owner: "merlin", 416 state: "s", 417 expectErr: &ResourceNotFound{"res"}, 418 }, 419 { 420 name: "ok", 421 resources: []common.Resource{ 422 common.NewResource("res", "t", "s", "merlin", FakeNow), 423 }, 424 resName: "res", 425 owner: "merlin", 426 state: "s", 427 }, 428 } 429 430 for _, tc := range testcases { 431 c := MakeTestRanch(tc.resources) 432 err := c.Update(tc.resName, tc.owner, tc.state, nil) 433 if !AreErrorsEqual(err, tc.expectErr) { 434 t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr) 435 continue 436 } 437 438 resources, err2 := c.Storage.GetResources() 439 if err2 != nil { 440 t.Errorf("failed to get resources") 441 continue 442 } 443 444 if err == nil { 445 if resources[0].Owner != tc.owner { 446 t.Errorf("%s - Wrong owner after release. Got %v, expect %v", tc.name, resources[0].Owner, tc.owner) 447 } else if resources[0].State != tc.state { 448 t.Errorf("%s - Wrong state after release. Got %v, expect %v", tc.name, resources[0].State, tc.state) 449 } else if !resources[0].LastUpdate.After(FakeNow) { 450 t.Errorf("%s - LastUpdate did not update.", tc.name) 451 } 452 } else { 453 for _, res := range resources { 454 if res.LastUpdate != FakeNow { 455 t.Errorf("%s - LastUpdate should not update. Got %v, expect %v", tc.name, resources[0].LastUpdate, FakeNow) 456 } 457 } 458 } 459 } 460 } 461 462 func TestMetric(t *testing.T) { 463 var testcases = []struct { 464 name string 465 resources []common.Resource 466 metricType string 467 expectErr error 468 expectMetric common.Metric 469 }{ 470 { 471 name: "ranch has no resource", 472 resources: []common.Resource{}, 473 metricType: "t", 474 expectErr: &ResourceNotFound{"t"}, 475 }, 476 { 477 name: "no matching resource", 478 resources: []common.Resource{ 479 common.NewResource("res", "t", "s", "merlin", time.Now()), 480 }, 481 metricType: "foo", 482 expectErr: &ResourceNotFound{"foo"}, 483 }, 484 { 485 name: "one resource", 486 resources: []common.Resource{ 487 common.NewResource("res", "t", "s", "merlin", time.Now()), 488 }, 489 metricType: "t", 490 expectMetric: common.Metric{ 491 Type: "t", 492 Current: map[string]int{ 493 "s": 1, 494 }, 495 Owners: map[string]int{ 496 "merlin": 1, 497 }, 498 }, 499 }, 500 { 501 name: "multiple resources", 502 resources: []common.Resource{ 503 common.NewResource("res-1", "t", "s", "merlin", time.Now()), 504 common.NewResource("res-2", "t", "p", "pony", time.Now()), 505 common.NewResource("res-3", "t", "s", "pony", time.Now()), 506 common.NewResource("res-4", "foo", "s", "pony", time.Now()), 507 common.NewResource("res-5", "t", "d", "merlin", time.Now()), 508 }, 509 metricType: "t", 510 expectMetric: common.Metric{ 511 Type: "t", 512 Current: map[string]int{ 513 "s": 2, 514 "d": 1, 515 "p": 1, 516 }, 517 Owners: map[string]int{ 518 "merlin": 2, 519 "pony": 2, 520 }, 521 }, 522 }, 523 } 524 525 for _, tc := range testcases { 526 c := MakeTestRanch(tc.resources) 527 metric, err := c.Metric(tc.metricType) 528 if !AreErrorsEqual(err, tc.expectErr) { 529 t.Errorf("%s - Got error %v, expect error %v", tc.name, err, tc.expectErr) 530 continue 531 } 532 533 if err == nil { 534 if !reflect.DeepEqual(metric, tc.expectMetric) { 535 t.Errorf("%s - wrong metric, got %v, want %v", tc.name, metric, tc.expectMetric) 536 } 537 } 538 } 539 } 540 541 func TestSyncResources(t *testing.T) { 542 var testcases = []struct { 543 name string 544 oldRes []common.Resource 545 newRes []common.Resource 546 expect []common.Resource 547 }{ 548 { 549 name: "empty", 550 }, 551 { 552 name: "append", 553 newRes: []common.Resource{ 554 common.NewResource("res", "t", "", "", time.Time{}), 555 }, 556 expect: []common.Resource{ 557 common.NewResource("res", "t", common.Free, "", time.Time{}), 558 }, 559 }, 560 { 561 name: "should not have a type change", 562 oldRes: []common.Resource{ 563 common.NewResource("res", "t", "", "", time.Time{}), 564 }, 565 newRes: []common.Resource{ 566 common.NewResource("res", "d", "", "", time.Time{}), 567 }, 568 expect: []common.Resource{ 569 common.NewResource("res", "t", "", "", time.Time{}), 570 }, 571 }, 572 { 573 name: "delete", 574 oldRes: []common.Resource{ 575 common.NewResource("res", "t", "", "", time.Time{}), 576 }, 577 }, 578 { 579 name: "delete busy", 580 oldRes: []common.Resource{ 581 common.NewResource("res", "t", common.Busy, "o", time.Time{}), 582 }, 583 expect: []common.Resource{ 584 common.NewResource("res", "t", common.Busy, "o", time.Time{}), 585 }, 586 }, 587 { 588 name: "append and delete", 589 oldRes: []common.Resource{ 590 common.NewResource("res-1", "t", "", "", time.Time{}), 591 }, 592 newRes: []common.Resource{ 593 common.NewResource("res-2", "t", "", "", time.Time{}), 594 }, 595 expect: []common.Resource{ 596 common.NewResource("res-2", "t", common.Free, "", time.Time{}), 597 }, 598 }, 599 { 600 name: "append and delete busy", 601 oldRes: []common.Resource{ 602 common.NewResource("res-1", "t", common.Busy, "o", time.Time{}), 603 }, 604 newRes: []common.Resource{ 605 common.NewResource("res-2", "t", "", "", time.Time{}), 606 }, 607 expect: []common.Resource{ 608 common.NewResource("res-1", "t", common.Busy, "o", time.Time{}), 609 common.NewResource("res-2", "t", common.Free, "", time.Time{}), 610 }, 611 }, 612 { 613 name: "append/delete mixed type", 614 oldRes: []common.Resource{ 615 common.NewResource("res-1", "t", "", "", time.Time{}), 616 }, 617 newRes: []common.Resource{ 618 common.NewResource("res-2", "t", "", "", time.Time{}), 619 common.NewResource("res-3", "t2", "", "", time.Time{}), 620 }, 621 expect: []common.Resource{ 622 common.NewResource("res-2", "t", "free", "", time.Time{}), 623 common.NewResource("res-3", "t2", "free", "", time.Time{}), 624 }, 625 }, 626 } 627 628 for _, tc := range testcases { 629 c := MakeTestRanch(tc.oldRes) 630 c.Storage.SyncResources(tc.newRes) 631 resources, err := c.Storage.GetResources() 632 if err != nil { 633 t.Errorf("failed to get resources") 634 continue 635 } 636 sort.Stable(common.ResourceByName(resources)) 637 sort.Stable(common.ResourceByName(tc.expect)) 638 if !reflect.DeepEqual(resources, tc.expect) { 639 t.Errorf("Test %v: got %v, expect %v", tc.name, resources, tc.expect) 640 } 641 } 642 }