github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/builds/planner_test.go (about) 1 package builds_test 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "github.com/pf-qiu/concourse/v6/atc" 8 "github.com/pf-qiu/concourse/v6/atc/builds" 9 "github.com/pf-qiu/concourse/v6/atc/db" 10 "github.com/stretchr/testify/require" 11 "github.com/stretchr/testify/suite" 12 ) 13 14 type PlannerSuite struct { 15 suite.Suite 16 *require.Assertions 17 } 18 19 func TestPlanner(t *testing.T) { 20 suite.Run(t, &PlannerSuite{ 21 Assertions: require.New(t), 22 }) 23 } 24 25 type PlannerTest struct { 26 Title string 27 28 Config atc.StepConfig 29 Inputs []db.BuildInput 30 31 CompareIDs bool 32 PlanJSON string 33 Err error 34 } 35 36 var resources = db.SchedulerResources{ 37 db.SchedulerResource{ 38 Name: "some-resource", 39 Type: "some-resource-type", 40 Source: atc.Source{"some": "source"}, 41 }, 42 db.SchedulerResource{ 43 Name: "some-base-resource", 44 Type: "some-base-resource-type", 45 Source: atc.Source{"some": "source"}, 46 }, 47 } 48 49 var resourceTypes = atc.VersionedResourceTypes{ 50 { 51 ResourceType: atc.ResourceType{ 52 Name: "some-resource-type", 53 Type: "some-base-resource-type", 54 Source: atc.Source{"some": "type-source"}, 55 Defaults: atc.Source{"default-key": "default-value"}, 56 }, 57 Version: atc.Version{"some": "type-version"}, 58 }, 59 } 60 61 var baseResourceTypeDefaults = map[string]atc.Source{ 62 "some-base-resource-type": {"default-key": "default-value"}, 63 } 64 65 var factoryTests = []PlannerTest{ 66 { 67 Title: "get step", 68 Config: &atc.GetStep{ 69 Name: "some-name", 70 Resource: "some-resource", 71 Params: atc.Params{"some": "params"}, 72 Version: &atc.VersionConfig{Pinned: atc.Version{"doesnt": "matter"}}, 73 Tags: atc.Tags{"tag-1", "tag-2"}, 74 }, 75 Inputs: []db.BuildInput{ 76 { 77 Name: "some-name", 78 Version: atc.Version{"some": "version"}, 79 }, 80 }, 81 PlanJSON: `{ 82 "id": "(unique)", 83 "get": { 84 "name": "some-name", 85 "type": "some-resource-type", 86 "resource": "some-resource", 87 "source": {"some":"source","default-key":"default-value"}, 88 "params": {"some":"params"}, 89 "version": {"some":"version"}, 90 "tags": ["tag-1", "tag-2"], 91 "resource_types": [ 92 { 93 "name": "some-resource-type", 94 "type": "some-base-resource-type", 95 "source": {"some": "type-source"}, 96 "defaults": {"default-key":"default-value"}, 97 "version": {"some": "type-version"} 98 } 99 ] 100 } 101 }`, 102 }, 103 { 104 Title: "get step with base resource type", 105 Config: &atc.GetStep{ 106 Name: "some-name", 107 Resource: "some-base-resource", 108 Params: atc.Params{"some": "params"}, 109 Version: &atc.VersionConfig{Pinned: atc.Version{"doesnt": "matter"}}, 110 Tags: atc.Tags{"tag-1", "tag-2"}, 111 }, 112 Inputs: []db.BuildInput{ 113 { 114 Name: "some-name", 115 Version: atc.Version{"some": "version"}, 116 }, 117 }, 118 PlanJSON: `{ 119 "id": "(unique)", 120 "get": { 121 "name": "some-name", 122 "type": "some-base-resource-type", 123 "resource": "some-base-resource", 124 "source": {"some":"source","default-key":"default-value"}, 125 "params": {"some":"params"}, 126 "version": {"some":"version"}, 127 "tags": ["tag-1", "tag-2"], 128 "resource_types": [ 129 { 130 "name": "some-resource-type", 131 "type": "some-base-resource-type", 132 "source": {"some": "type-source"}, 133 "defaults": {"default-key":"default-value"}, 134 "version": {"some": "type-version"} 135 } 136 ] 137 } 138 }`, 139 }, 140 { 141 Title: "get step with unknown resource", 142 Config: &atc.GetStep{ 143 Name: "some-name", 144 Resource: "bogus-resource", 145 }, 146 Err: builds.UnknownResourceError{Resource: "bogus-resource"}, 147 }, 148 { 149 Title: "get step with no available version", 150 Config: &atc.GetStep{ 151 Name: "some-name", 152 Resource: "some-resource", 153 }, 154 Err: builds.VersionNotProvidedError{Input: "some-name"}, 155 }, 156 { 157 Title: "put step", 158 Config: &atc.PutStep{ 159 Name: "some-name", 160 Resource: "some-resource", 161 Params: atc.Params{"some": "params"}, 162 Tags: atc.Tags{"tag-1", "tag-2"}, 163 Inputs: &atc.InputsConfig{All: true}, 164 GetParams: atc.Params{"some": "get-params"}, 165 }, 166 Inputs: []db.BuildInput{ 167 { 168 Name: "some-name", 169 Version: atc.Version{"some": "version"}, 170 }, 171 }, 172 173 // the ids are significant for versioned_from 174 CompareIDs: true, 175 PlanJSON: `{ 176 "id": "3", 177 "on_success": { 178 "step": { 179 "id": "1", 180 "put": { 181 "name": "some-name", 182 "type": "some-resource-type", 183 "resource": "some-resource", 184 "inputs": "all", 185 "source": {"some":"source","default-key":"default-value"}, 186 "params": {"some":"params"}, 187 "tags": ["tag-1", "tag-2"], 188 "resource_types": [ 189 { 190 "name": "some-resource-type", 191 "type": "some-base-resource-type", 192 "source": {"some": "type-source"}, 193 "defaults": {"default-key":"default-value"}, 194 "version": {"some": "type-version"} 195 } 196 ] 197 } 198 }, 199 "on_success": { 200 "id": "2", 201 "get": { 202 "name": "some-name", 203 "type": "some-resource-type", 204 "resource": "some-resource", 205 "source": {"some":"source","default-key":"default-value"}, 206 "params": {"some":"get-params"}, 207 "tags": ["tag-1", "tag-2"], 208 "version_from": "1", 209 "resource_types": [ 210 { 211 "name": "some-resource-type", 212 "type": "some-base-resource-type", 213 "source": {"some": "type-source"}, 214 "defaults": {"default-key":"default-value"}, 215 "version": {"some": "type-version"} 216 } 217 ] 218 } 219 } 220 } 221 }`, 222 }, 223 { 224 Title: "task step", 225 226 Config: &atc.TaskStep{ 227 Name: "some-task", 228 Privileged: true, 229 Config: &atc.TaskConfig{ 230 Platform: "linux", 231 Run: atc.TaskRunConfig{Path: "hello"}, 232 }, 233 ConfigPath: "some-task-file", 234 Vars: atc.Params{"some": "vars"}, 235 Params: atc.TaskEnv{"SOME": "PARAMS"}, 236 Tags: atc.Tags{"tag-1", "tag-2"}, 237 InputMapping: map[string]string{"generic": "specific"}, 238 OutputMapping: map[string]string{"specific": "generic"}, 239 ImageArtifactName: "some-image", 240 }, 241 242 PlanJSON: `{ 243 "id": "(unique)", 244 "task": { 245 "name": "some-task", 246 "privileged": true, 247 "config": { 248 "platform": "linux", 249 "run": {"path": "hello"} 250 }, 251 "config_path": "some-task-file", 252 "vars": {"some": "vars"}, 253 "params": {"SOME": "PARAMS"}, 254 "tags": ["tag-1", "tag-2"], 255 "input_mapping": {"generic": "specific"}, 256 "output_mapping": {"specific": "generic"}, 257 "image": "some-image", 258 "resource_types": [ 259 { 260 "name": "some-resource-type", 261 "type": "some-base-resource-type", 262 "source": {"some": "type-source"}, 263 "defaults": {"default-key":"default-value"}, 264 "version": {"some": "type-version"} 265 } 266 ] 267 } 268 }`, 269 }, 270 { 271 Title: "set_pipeline step", 272 273 Config: &atc.SetPipelineStep{ 274 Name: "some-pipeline", 275 File: "some-pipeline-file", 276 Vars: atc.Params{"some": "vars"}, 277 VarFiles: []string{"file-1", "file-2"}, 278 InstanceVars: atc.InstanceVars{"branch": "feature/foo"}, 279 }, 280 281 PlanJSON: `{ 282 "id": "(unique)", 283 "set_pipeline": { 284 "name": "some-pipeline", 285 "file": "some-pipeline-file", 286 "vars": {"some": "vars"}, 287 "var_files": ["file-1", "file-2"], 288 "instance_vars": {"branch": "feature/foo"} 289 } 290 }`, 291 }, 292 { 293 Title: "load_var step", 294 295 Config: &atc.LoadVarStep{ 296 Name: "some-var", 297 File: "some-var-file", 298 Format: "raw", 299 Reveal: true, 300 }, 301 302 PlanJSON: `{ 303 "id": "(unique)", 304 "load_var": { 305 "name": "some-var", 306 "file": "some-var-file", 307 "format": "raw", 308 "reveal": true 309 } 310 }`, 311 }, 312 { 313 Title: "try step", 314 315 Config: &atc.TryStep{ 316 Step: atc.Step{ 317 Config: &atc.LoadVarStep{ 318 Name: "some-var", 319 File: "some-file", 320 }, 321 }, 322 }, 323 324 PlanJSON: `{ 325 "id": "(unique)", 326 "try": { 327 "step": { 328 "id": "(unique)", 329 "load_var": { 330 "name": "some-var", 331 "file": "some-file" 332 } 333 } 334 } 335 }`, 336 }, 337 { 338 Title: "do step", 339 340 Config: &atc.DoStep{ 341 Steps: []atc.Step{ 342 { 343 Config: &atc.LoadVarStep{ 344 Name: "some-var", 345 File: "some-file", 346 }, 347 }, 348 { 349 Config: &atc.LoadVarStep{ 350 Name: "some-other-var", 351 File: "some-other-file", 352 }, 353 }, 354 }, 355 }, 356 357 PlanJSON: `{ 358 "id": "(unique)", 359 "do": [ 360 { 361 "id": "(unique)", 362 "load_var": { 363 "name": "some-var", 364 "file": "some-file" 365 } 366 }, 367 { 368 "id": "(unique)", 369 "load_var": { 370 "name": "some-other-var", 371 "file": "some-other-file" 372 } 373 } 374 ] 375 }`, 376 }, 377 { 378 Title: "in_parallel step", 379 380 Config: &atc.InParallelStep{ 381 Config: atc.InParallelConfig{ 382 Limit: 3, 383 FailFast: true, 384 Steps: []atc.Step{ 385 { 386 Config: &atc.LoadVarStep{ 387 Name: "some-var", 388 File: "some-file", 389 }, 390 }, 391 { 392 Config: &atc.LoadVarStep{ 393 Name: "some-other-var", 394 File: "some-other-file", 395 }, 396 }, 397 }, 398 }, 399 }, 400 401 PlanJSON: `{ 402 "id": "(unique)", 403 "in_parallel": { 404 "steps": [ 405 { 406 "id": "(unique)", 407 "load_var": { 408 "name": "some-var", 409 "file": "some-file" 410 } 411 }, 412 { 413 "id": "(unique)", 414 "load_var": { 415 "name": "some-other-var", 416 "file": "some-other-file" 417 } 418 } 419 ], 420 "limit": 3, 421 "fail_fast": true 422 } 423 }`, 424 }, 425 { 426 Title: "aggregate step", 427 428 Config: &atc.AggregateStep{ 429 Steps: []atc.Step{ 430 { 431 Config: &atc.LoadVarStep{ 432 Name: "some-var", 433 File: "some-file", 434 }, 435 }, 436 { 437 Config: &atc.LoadVarStep{ 438 Name: "some-other-var", 439 File: "some-other-file", 440 }, 441 }, 442 }, 443 }, 444 445 PlanJSON: `{ 446 "id": "(unique)", 447 "aggregate": [ 448 { 449 "id": "(unique)", 450 "load_var": { 451 "name": "some-var", 452 "file": "some-file" 453 } 454 }, 455 { 456 "id": "(unique)", 457 "load_var": { 458 "name": "some-other-var", 459 "file": "some-other-file" 460 } 461 } 462 ] 463 }`, 464 }, 465 { 466 Title: "across step", 467 468 Config: &atc.AcrossStep{ 469 Step: &atc.LoadVarStep{ 470 Name: "some-var", 471 File: "some-file", 472 }, 473 Vars: []atc.AcrossVarConfig{ 474 { 475 Var: "var1", 476 Values: []interface{}{"a1", "a2"}, 477 MaxInFlight: &atc.MaxInFlightConfig{All: true}, 478 }, 479 { 480 Var: "var2", 481 Values: []interface{}{"b1", "b2"}, 482 MaxInFlight: &atc.MaxInFlightConfig{Limit: 1}, 483 }, 484 }, 485 }, 486 487 PlanJSON: `{ 488 "id": "(unique)", 489 "across": { 490 "vars": [ 491 { 492 "name": "var1", 493 "values": ["a1", "a2"], 494 "max_in_flight": "all" 495 }, 496 { 497 "name": "var2", 498 "values": ["b1", "b2"], 499 "max_in_flight": 1 500 } 501 ], 502 "steps": [ 503 { 504 "values": ["a1", "b1"], 505 "step": { 506 "id": "(unique)", 507 "load_var": { 508 "name": "some-var", 509 "file": "some-file" 510 } 511 } 512 }, 513 { 514 "values": ["a1", "b2"], 515 "step": { 516 "id": "(unique)", 517 "load_var": { 518 "name": "some-var", 519 "file": "some-file" 520 } 521 } 522 }, 523 { 524 "values": ["a2", "b1"], 525 "step": { 526 "id": "(unique)", 527 "load_var": { 528 "name": "some-var", 529 "file": "some-file" 530 } 531 } 532 }, 533 { 534 "values": ["a2", "b2"], 535 "step": { 536 "id": "(unique)", 537 "load_var": { 538 "name": "some-var", 539 "file": "some-file" 540 } 541 } 542 } 543 ] 544 } 545 }`, 546 }, 547 { 548 Title: "timeout modifier", 549 550 Config: &atc.TimeoutStep{ 551 Step: &atc.LoadVarStep{ 552 Name: "some-var", 553 File: "some-file", 554 }, 555 Duration: "1h", 556 }, 557 558 PlanJSON: `{ 559 "id": "(unique)", 560 "timeout": { 561 "step": { 562 "id": "(unique)", 563 "load_var": { 564 "name": "some-var", 565 "file": "some-file" 566 } 567 }, 568 "duration": "1h" 569 } 570 }`, 571 }, 572 { 573 Title: "attempts modifier", 574 575 Config: &atc.RetryStep{ 576 Step: &atc.LoadVarStep{ 577 Name: "some-var", 578 File: "some-file", 579 }, 580 Attempts: 3, 581 }, 582 583 CompareIDs: true, 584 PlanJSON: `{ 585 "id": "4", 586 "retry": [ 587 { 588 "id": "1", 589 "load_var": { 590 "name": "some-var", 591 "file": "some-file" 592 } 593 }, 594 { 595 "id": "2", 596 "load_var": { 597 "name": "some-var", 598 "file": "some-file" 599 } 600 }, 601 { 602 "id": "3", 603 "load_var": { 604 "name": "some-var", 605 "file": "some-file" 606 } 607 } 608 ] 609 }`, 610 }, 611 { 612 Title: "on_success step", 613 614 Config: &atc.OnSuccessStep{ 615 Step: &atc.LoadVarStep{ 616 Name: "some-var", 617 File: "some-file", 618 }, 619 Hook: atc.Step{ 620 Config: &atc.LoadVarStep{ 621 Name: "some-other-var", 622 File: "some-other-file", 623 }, 624 }, 625 }, 626 627 PlanJSON: `{ 628 "id": "(unique)", 629 "on_success": { 630 "step": { 631 "id": "(unique)", 632 "load_var": { 633 "name": "some-var", 634 "file": "some-file" 635 } 636 }, 637 "on_success": { 638 "id": "(unique)", 639 "load_var": { 640 "name": "some-other-var", 641 "file": "some-other-file" 642 } 643 } 644 } 645 }`, 646 }, 647 { 648 Title: "on_failure step", 649 650 Config: &atc.OnFailureStep{ 651 Step: &atc.LoadVarStep{ 652 Name: "some-var", 653 File: "some-file", 654 }, 655 Hook: atc.Step{ 656 Config: &atc.LoadVarStep{ 657 Name: "some-other-var", 658 File: "some-other-file", 659 }, 660 }, 661 }, 662 663 PlanJSON: `{ 664 "id": "(unique)", 665 "on_failure": { 666 "step": { 667 "id": "(unique)", 668 "load_var": { 669 "name": "some-var", 670 "file": "some-file" 671 } 672 }, 673 "on_failure": { 674 "id": "(unique)", 675 "load_var": { 676 "name": "some-other-var", 677 "file": "some-other-file" 678 } 679 } 680 } 681 }`, 682 }, 683 { 684 Title: "on_error step", 685 686 Config: &atc.OnErrorStep{ 687 Step: &atc.LoadVarStep{ 688 Name: "some-var", 689 File: "some-file", 690 }, 691 Hook: atc.Step{ 692 Config: &atc.LoadVarStep{ 693 Name: "some-other-var", 694 File: "some-other-file", 695 }, 696 }, 697 }, 698 699 PlanJSON: `{ 700 "id": "(unique)", 701 "on_error": { 702 "step": { 703 "id": "(unique)", 704 "load_var": { 705 "name": "some-var", 706 "file": "some-file" 707 } 708 }, 709 "on_error": { 710 "id": "(unique)", 711 "load_var": { 712 "name": "some-other-var", 713 "file": "some-other-file" 714 } 715 } 716 } 717 }`, 718 }, 719 { 720 Title: "on_abort step", 721 722 Config: &atc.OnAbortStep{ 723 Step: &atc.LoadVarStep{ 724 Name: "some-var", 725 File: "some-file", 726 }, 727 Hook: atc.Step{ 728 Config: &atc.LoadVarStep{ 729 Name: "some-other-var", 730 File: "some-other-file", 731 }, 732 }, 733 }, 734 735 PlanJSON: `{ 736 "id": "(unique)", 737 "on_abort": { 738 "step": { 739 "id": "(unique)", 740 "load_var": { 741 "name": "some-var", 742 "file": "some-file" 743 } 744 }, 745 "on_abort": { 746 "id": "(unique)", 747 "load_var": { 748 "name": "some-other-var", 749 "file": "some-other-file" 750 } 751 } 752 } 753 }`, 754 }, 755 { 756 Title: "ensure step", 757 758 Config: &atc.EnsureStep{ 759 Step: &atc.LoadVarStep{ 760 Name: "some-var", 761 File: "some-file", 762 }, 763 Hook: atc.Step{ 764 Config: &atc.LoadVarStep{ 765 Name: "some-other-var", 766 File: "some-other-file", 767 }, 768 }, 769 }, 770 771 PlanJSON: `{ 772 "id": "(unique)", 773 "ensure": { 774 "step": { 775 "id": "(unique)", 776 "load_var": { 777 "name": "some-var", 778 "file": "some-file" 779 } 780 }, 781 "ensure": { 782 "id": "(unique)", 783 "load_var": { 784 "name": "some-other-var", 785 "file": "some-other-file" 786 } 787 } 788 } 789 }`, 790 }, 791 } 792 793 func (test PlannerTest) Run(s *PlannerSuite) { 794 factory := builds.NewPlanner(atc.NewPlanFactory(0)) 795 796 actualPlan, actualErr := factory.Create(test.Config, resources, resourceTypes, test.Inputs) 797 798 if test.Err != nil { 799 s.Equal(test.Err, actualErr) 800 return 801 } else { 802 s.NoError(actualErr) 803 } 804 805 seenIDs := map[atc.PlanID]bool{} 806 actualPlan.Each(func(p *atc.Plan) { 807 s.NotEmpty(p.ID) 808 809 // make sure all IDs are unique 810 s.False(seenIDs[p.ID], "duplicate plan id: %s", p.ID) 811 seenIDs[p.ID] = true 812 813 // strip out the IDs, we don't really care what their value is 814 if !test.CompareIDs { 815 p.ID = "(unique)" 816 } 817 }) 818 s.NotEmpty(seenIDs) 819 820 actualJSON, err := json.Marshal(actualPlan) 821 s.NoError(err) 822 823 s.JSONEq(test.PlanJSON, string(actualJSON)) 824 } 825 826 func (s *PlannerSuite) TestFactory() { 827 atc.LoadBaseResourceTypeDefaults(baseResourceTypeDefaults) 828 for _, test := range factoryTests { 829 s.Run(test.Title, func() { 830 test.Run(s) 831 }) 832 } 833 atc.LoadBaseResourceTypeDefaults(map[string]atc.Source{}) 834 }