github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/dbtest/builder.go (about) 1 package dbtest 2 3 import ( 4 "crypto/md5" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "time" 9 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/atc/db" 12 "github.com/pf-qiu/concourse/v6/atc/db/lock" 13 uuid "github.com/nu7hatch/gouuid" 14 ) 15 16 const BaseResourceType = "global-base-type" 17 const UniqueBaseResourceType = "unique-base-type" 18 19 type JobInputs []JobInput 20 21 type JobInput struct { 22 Name string 23 Version atc.Version 24 PassedBuilds []db.Build 25 FirstOccurrence bool 26 27 ResolveError string 28 } 29 30 func (inputs JobInputs) Lookup(name string) (JobInput, bool) { 31 for _, i := range inputs { 32 if i.Name == name { 33 return i, true 34 } 35 } 36 37 return JobInput{}, false 38 } 39 40 type JobOutputs map[string]atc.Version 41 42 type Builder struct { 43 TeamFactory db.TeamFactory 44 WorkerFactory db.WorkerFactory 45 ResourceConfigFactory db.ResourceConfigFactory 46 } 47 48 func NewBuilder(conn db.Conn, lockFactory lock.LockFactory) Builder { 49 return Builder{ 50 TeamFactory: db.NewTeamFactory(conn, lockFactory), 51 WorkerFactory: db.NewWorkerFactory(conn), 52 ResourceConfigFactory: db.NewResourceConfigFactory(conn, lockFactory), 53 } 54 } 55 56 func (builder Builder) WithTeam(teamName string) SetupFunc { 57 return func(scenario *Scenario) error { 58 t, err := builder.TeamFactory.CreateTeam(atc.Team{Name: teamName}) 59 if err != nil { 60 return err 61 } 62 63 scenario.Team = t 64 return nil 65 } 66 } 67 68 func (builder Builder) WithWorker(worker atc.Worker) SetupFunc { 69 return func(scenario *Scenario) error { 70 var w db.Worker 71 var err error 72 if worker.Team != "" { 73 team, found, err := builder.TeamFactory.FindTeam(worker.Team) 74 if err != nil { 75 return err 76 } 77 78 if !found { 79 return fmt.Errorf("team does not exist: %s", worker.Team) 80 } 81 82 w, err = team.SaveWorker(worker, 0) 83 } else { 84 w, err = builder.WorkerFactory.SaveWorker(worker, 0) 85 } 86 if err != nil { 87 return err 88 } 89 90 scenario.Workers = append(scenario.Workers, w) 91 return nil 92 } 93 } 94 95 func (builder Builder) WithPipeline(config atc.Config) SetupFunc { 96 return func(scenario *Scenario) error { 97 if scenario.Team == nil { 98 err := builder.WithTeam(unique("team"))(scenario) 99 if err != nil { 100 return fmt.Errorf("bootstrap team: %w", err) 101 } 102 } 103 104 var from db.ConfigVersion 105 if scenario.Pipeline != nil { 106 from = scenario.Pipeline.ConfigVersion() 107 } 108 109 p, _, err := scenario.Team.SavePipeline(atc.PipelineRef{Name: "some-pipeline"}, config, from, false) 110 if err != nil { 111 return err 112 } 113 114 scenario.Pipeline = p 115 return nil 116 } 117 } 118 119 func (builder Builder) WithBaseWorker() SetupFunc { 120 return builder.WithWorker(atc.Worker{ 121 Name: unique("worker"), 122 123 GardenAddr: unique("garden-addr"), 124 BaggageclaimURL: unique("baggageclaim-url"), 125 126 ResourceTypes: []atc.WorkerResourceType{ 127 { 128 Type: BaseResourceType, 129 Image: "/path/to/global/image", 130 Version: "some-global-type-version", 131 }, 132 { 133 Type: UniqueBaseResourceType, 134 Image: "/path/to/unique/image", 135 Version: "some-unique-type-version", 136 UniqueVersionHistory: true, 137 }, 138 }, 139 }) 140 } 141 142 func (builder Builder) WithResourceVersions(resourceName string, versions ...atc.Version) SetupFunc { 143 return func(scenario *Scenario) error { 144 if scenario.Pipeline == nil { 145 err := builder.WithPipeline(atc.Config{ 146 Resources: atc.ResourceConfigs{ 147 { 148 Name: resourceName, 149 Type: BaseResourceType, 150 Source: atc.Source{"some": "source"}, 151 }, 152 }, 153 })(scenario) 154 if err != nil { 155 return fmt.Errorf("bootstrap pipeline: %w", err) 156 } 157 } 158 159 if len(scenario.Workers) == 0 { 160 err := builder.WithBaseWorker()(scenario) 161 if err != nil { 162 return fmt.Errorf("bootstrap workers: %w", err) 163 } 164 } 165 166 resource, found, err := scenario.Pipeline.Resource(resourceName) 167 if err != nil { 168 return err 169 } 170 171 if !found { 172 return fmt.Errorf("resource '%s' not configured in pipeline", resourceName) 173 } 174 175 resourceTypes, err := scenario.Pipeline.ResourceTypes() 176 if err != nil { 177 return fmt.Errorf("get pipeline resource types: %w", err) 178 } 179 180 resourceConfig, err := builder.ResourceConfigFactory.FindOrCreateResourceConfig( 181 resource.Type(), 182 resource.Source(), 183 resourceTypes.Deserialize(), 184 ) 185 if err != nil { 186 return fmt.Errorf("find or create resource config: %w", err) 187 } 188 189 scope, err := resourceConfig.FindOrCreateScope(resource) 190 if err != nil { 191 return fmt.Errorf("find or create scope: %w", err) 192 } 193 194 err = scope.SaveVersions(scenario.SpanContext, versions) 195 if err != nil { 196 return fmt.Errorf("save versions: %w", err) 197 } 198 199 _, err = scope.UpdateLastCheckEndTime() 200 if err != nil { 201 return fmt.Errorf("update last check end time: %w", err) 202 } 203 204 err = resource.SetResourceConfigScope(scope) 205 if err != nil { 206 return fmt.Errorf("set resource scope: %w", err) 207 } 208 209 return nil 210 } 211 } 212 213 func (builder Builder) WithResourceTypeVersions(resourceTypeName string, versions ...atc.Version) SetupFunc { 214 return func(scenario *Scenario) error { 215 if scenario.Pipeline == nil { 216 err := builder.WithPipeline(atc.Config{ 217 ResourceTypes: atc.ResourceTypes{ 218 { 219 Name: resourceTypeName, 220 Type: BaseResourceType, 221 Source: atc.Source{"some": "source"}, 222 }, 223 }, 224 })(scenario) 225 if err != nil { 226 return fmt.Errorf("bootstrap pipeline: %w", err) 227 } 228 } 229 230 if len(scenario.Workers) == 0 { 231 err := builder.WithBaseWorker()(scenario) 232 if err != nil { 233 return fmt.Errorf("bootstrap workers: %w", err) 234 } 235 } 236 237 resourceType, found, err := scenario.Pipeline.ResourceType(resourceTypeName) 238 if err != nil { 239 return err 240 } 241 242 if !found { 243 return fmt.Errorf("resource type '%s' not configured in pipeline", resourceTypeName) 244 } 245 246 resourceTypes, err := scenario.Pipeline.ResourceTypes() 247 if err != nil { 248 return fmt.Errorf("get pipeline resource types: %w", err) 249 } 250 251 resourceConfig, err := builder.ResourceConfigFactory.FindOrCreateResourceConfig( 252 resourceType.Type(), 253 resourceType.Source(), 254 resourceTypes.Filter(resourceType).Deserialize(), 255 ) 256 if err != nil { 257 return fmt.Errorf("find or create resource config: %w", err) 258 } 259 260 scope, err := resourceConfig.FindOrCreateScope(nil) 261 if err != nil { 262 return fmt.Errorf("find or create scope: %w", err) 263 } 264 265 err = scope.SaveVersions(db.SpanContext{}, versions) 266 if err != nil { 267 return fmt.Errorf("save versions: %w", err) 268 } 269 270 resourceType.SetResourceConfigScope(scope) 271 if err != nil { 272 return fmt.Errorf("set resource scope: %w", err) 273 } 274 275 return nil 276 } 277 } 278 279 func (builder Builder) WithPendingJobBuild(assign *db.Build, jobName string) SetupFunc { 280 return func(scenario *Scenario) error { 281 if scenario.Pipeline == nil { 282 return fmt.Errorf("no pipeline set in scenario") 283 } 284 285 job, found, err := scenario.Pipeline.Job(jobName) 286 if err != nil { 287 return err 288 } 289 290 if !found { 291 return fmt.Errorf("job '%s' not configured in pipeline", jobName) 292 } 293 294 build, err := job.CreateBuild() 295 if err != nil { 296 return fmt.Errorf("create build: %w", err) 297 } 298 299 *assign = build 300 301 return nil 302 } 303 } 304 305 func (builder Builder) WithNextInputMapping(jobName string, inputs JobInputs) SetupFunc { 306 return func(scenario *Scenario) error { 307 if scenario.Pipeline == nil { 308 return fmt.Errorf("no pipeline set in scenario") 309 } 310 311 job, found, err := scenario.Pipeline.Job(jobName) 312 if err != nil { 313 return err 314 } 315 316 if !found { 317 return fmt.Errorf("job '%s' not configured in pipeline", jobName) 318 } 319 320 jobInputs, err := job.AlgorithmInputs() 321 if err != nil { 322 return fmt.Errorf("get job inputs: %w", err) 323 } 324 325 var hasErrors bool 326 mapping := db.InputMapping{} 327 for _, input := range jobInputs { 328 i, found := inputs.Lookup(input.Name) 329 if !found { 330 return fmt.Errorf("no version specified for input '%s'", input.Name) 331 } 332 333 buildIDs := []int{} 334 for _, build := range i.PassedBuilds { 335 buildIDs = append(buildIDs, build.ID()) 336 } 337 338 mapping[input.Name] = db.InputResult{ 339 Input: &db.AlgorithmInput{ 340 AlgorithmVersion: db.AlgorithmVersion{ 341 Version: db.ResourceVersion(md5Version(i.Version)), 342 ResourceID: input.ResourceID, 343 }, 344 FirstOccurrence: i.FirstOccurrence, 345 }, 346 PassedBuildIDs: buildIDs, 347 ResolveError: db.ResolutionFailure(i.ResolveError), 348 } 349 350 if i.ResolveError != "" { 351 hasErrors = true 352 } 353 } 354 355 err = job.SaveNextInputMapping(mapping, !hasErrors) 356 if err != nil { 357 return fmt.Errorf("save job input mapping: %w", err) 358 } 359 360 return nil 361 } 362 } 363 364 func (builder Builder) WithJobBuild(assign *db.Build, jobName string, inputs JobInputs, outputs JobOutputs) SetupFunc { 365 return func(scenario *Scenario) error { 366 var build db.Build 367 scenario.Run( 368 builder.WithPendingJobBuild(&build, jobName), 369 builder.WithNextInputMapping(jobName, inputs), 370 ) 371 372 _, inputsReady, err := build.AdoptInputsAndPipes() 373 if err != nil { 374 return fmt.Errorf("adopt inputs and pipes: %w", err) 375 } 376 377 if !inputsReady { 378 return fmt.Errorf("inputs not available?") 379 } 380 381 job, found, err := scenario.Pipeline.Job(jobName) 382 if err != nil { 383 return err 384 } 385 386 if !found { 387 return fmt.Errorf("job '%s' not configured in pipeline", jobName) 388 } 389 390 resourceTypes, err := scenario.Pipeline.ResourceTypes() 391 if err != nil { 392 return fmt.Errorf("get pipeline resource types: %w", err) 393 } 394 395 jobOutputs, err := job.Outputs() 396 if err != nil { 397 return fmt.Errorf("get job outputs: %w", err) 398 } 399 400 for _, output := range jobOutputs { 401 version, found := outputs[output.Name] 402 if !found { 403 return fmt.Errorf("no version specified for output '%s'", output.Name) 404 } 405 406 resource, found, err := scenario.Pipeline.Resource(output.Resource) 407 if err != nil { 408 return fmt.Errorf("get output resource: %w", err) 409 } 410 411 if !found { 412 return fmt.Errorf("output '%s' refers to unknown resource '%s'", output.Name, output.Resource) 413 } 414 415 err = build.SaveOutput( 416 resource.Type(), 417 resource.Source(), 418 resourceTypes.Deserialize(), 419 version, 420 nil, // metadata 421 output.Name, 422 output.Resource, 423 ) 424 if err != nil { 425 return fmt.Errorf("save build output: %w", err) 426 } 427 } 428 429 found, err = build.Reload() 430 if err != nil { 431 return fmt.Errorf("reload build: %w", err) 432 } 433 434 if !found { 435 return fmt.Errorf("build disappeared") 436 } 437 438 *assign = build 439 440 return nil 441 } 442 } 443 444 func (builder Builder) WithCheckContainer(resourceName string, workerName string) SetupFunc { 445 return func(scenario *Scenario) error { 446 if scenario.Pipeline == nil { 447 return fmt.Errorf("no pipeline set in scenario") 448 } 449 450 if len(scenario.Workers) == 0 { 451 return fmt.Errorf("no workers set in scenario") 452 } 453 454 resource, found, err := scenario.Pipeline.Resource(resourceName) 455 if err != nil { 456 return err 457 } 458 459 if !found { 460 return fmt.Errorf("resource '%s' not configured in pipeline", resourceName) 461 } 462 463 rc, found, err := builder.ResourceConfigFactory.FindResourceConfigByID(resource.ResourceConfigID()) 464 if err != nil { 465 return err 466 } 467 468 if !found { 469 return fmt.Errorf("resource config '%d' not found", rc.ID()) 470 } 471 472 owner := db.NewResourceConfigCheckSessionContainerOwner( 473 rc.ID(), 474 rc.OriginBaseResourceType().ID, 475 db.ContainerOwnerExpiries{ 476 Min: 5 * time.Minute, 477 Max: 5 * time.Minute, 478 }, 479 ) 480 481 worker, found, err := builder.WorkerFactory.GetWorker(workerName) 482 if err != nil { 483 return err 484 } 485 486 if !found { 487 return fmt.Errorf("worker '%d' not set in the scenario", rc.ID()) 488 } 489 490 containerMetadata := db.ContainerMetadata{ 491 Type: "check", 492 } 493 494 _, err = worker.CreateContainer(owner, containerMetadata) 495 if err != nil { 496 return err 497 } 498 499 return nil 500 } 501 } 502 503 func (builder Builder) WithSpanContext(spanContext db.SpanContext) SetupFunc { 504 return func(scenario *Scenario) error { 505 scenario.SpanContext = spanContext 506 return nil 507 } 508 } 509 510 func (builder Builder) WithVersionMetadata(resourceName string, version atc.Version, metadata db.ResourceConfigMetadataFields) SetupFunc { 511 return func(scenario *Scenario) error { 512 resource, found, err := scenario.Pipeline.Resource(resourceName) 513 if err != nil { 514 return err 515 } 516 517 if !found { 518 return fmt.Errorf("resource '%s' not configured in pipeline", resourceName) 519 } 520 521 _, err = resource.UpdateMetadata(version, metadata) 522 if err != nil { 523 return err 524 } 525 526 return nil 527 } 528 } 529 530 func (builder Builder) WithPinnedVersion(resourceName string, pinnedVersion atc.Version) SetupFunc { 531 return func(scenario *Scenario) error { 532 resource, found, err := scenario.Pipeline.Resource(resourceName) 533 if err != nil { 534 return err 535 } 536 537 if !found { 538 return fmt.Errorf("resource '%s' not configured in pipeline", resourceName) 539 } 540 541 version, found, err := resource.FindVersion(pinnedVersion) 542 if err != nil { 543 return err 544 } 545 546 if !found { 547 scenario.Run(builder.WithResourceVersions(resourceName, pinnedVersion)) 548 549 reloaded, err := resource.Reload() 550 if err != nil { 551 return err 552 } 553 554 if !reloaded { 555 return fmt.Errorf("resource '%s' not reloaded", resourceName) 556 } 557 558 version, found, err = resource.FindVersion(pinnedVersion) 559 if err != nil { 560 return err 561 } 562 563 if !found { 564 return fmt.Errorf("version '%v' not able to be saved", pinnedVersion) 565 } 566 } 567 568 _, err = resource.PinVersion(version.ID()) 569 if err != nil { 570 return err 571 } 572 573 if !found { 574 return fmt.Errorf("version '%v' not pinned", version) 575 } 576 577 return nil 578 } 579 } 580 581 func (builder Builder) WithDisabledVersion(resourceName string, disabledVersion atc.Version) SetupFunc { 582 return func(scenario *Scenario) error { 583 resource, found, err := scenario.Pipeline.Resource(resourceName) 584 if err != nil { 585 return err 586 } 587 588 if !found { 589 return fmt.Errorf("resource '%s' not configured in pipeline", resourceName) 590 } 591 592 version, found, err := resource.FindVersion(disabledVersion) 593 if err != nil { 594 return err 595 } 596 597 if !found { 598 scenario.Run(builder.WithResourceVersions(resourceName, disabledVersion)) 599 600 reloaded, err := resource.Reload() 601 if err != nil { 602 return err 603 } 604 605 if !reloaded { 606 return fmt.Errorf("resource '%s' not reloaded", resourceName) 607 } 608 609 version, found, err = resource.FindVersion(disabledVersion) 610 if err != nil { 611 return err 612 } 613 614 if !found { 615 return fmt.Errorf("version '%v' not able to be saved", disabledVersion) 616 } 617 } 618 619 err = resource.DisableVersion(version.ID()) 620 if err != nil { 621 return err 622 } 623 624 return nil 625 } 626 } 627 628 func (builder Builder) WithEnabledVersion(resourceName string, enabledVersion atc.Version) SetupFunc { 629 return func(scenario *Scenario) error { 630 resource, found, err := scenario.Pipeline.Resource(resourceName) 631 if err != nil { 632 return err 633 } 634 635 if !found { 636 return fmt.Errorf("resource '%s' not configured in pipeline", resourceName) 637 } 638 639 version, found, err := resource.FindVersion(enabledVersion) 640 if err != nil { 641 return err 642 } 643 644 if found { 645 err = resource.EnableVersion(version.ID()) 646 if err != nil { 647 return err 648 } 649 } else { 650 scenario.Run(builder.WithResourceVersions(resourceName, enabledVersion)) 651 } 652 653 return nil 654 } 655 } 656 657 func (builder Builder) WithBaseResourceType(dbConn db.Conn, resourceTypeName string) SetupFunc { 658 return func(scenario *Scenario) error { 659 setupTx, err := dbConn.Begin() 660 if err != nil { 661 return err 662 } 663 664 brt := db.BaseResourceType{ 665 Name: resourceTypeName, 666 } 667 668 _, err = brt.FindOrCreate(setupTx, false) 669 if err != nil { 670 return err 671 } 672 673 return setupTx.Commit() 674 } 675 } 676 677 func unique(kind string) string { 678 id, err := uuid.NewV4() 679 if err != nil { 680 panic(err) 681 } 682 683 return kind + "-" + id.String() 684 } 685 686 func md5Version(version atc.Version) string { 687 versionJSON, err := json.Marshal(version) 688 if err != nil { 689 panic(err) 690 } 691 692 hasher := md5.New() 693 hasher.Write([]byte(versionJSON)) 694 return hex.EncodeToString(hasher.Sum(nil)) 695 }