github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/scheduler/algorithm/table_helpers_test.go (about) 1 package algorithm_test 2 3 import ( 4 "compress/gzip" 5 "context" 6 "crypto/sha256" 7 "database/sql" 8 "encoding/json" 9 "fmt" 10 "os" 11 "strconv" 12 "time" 13 14 sq "github.com/Masterminds/squirrel" 15 "github.com/pf-qiu/concourse/v6/atc" 16 "github.com/pf-qiu/concourse/v6/atc/db" 17 "github.com/pf-qiu/concourse/v6/atc/scheduler/algorithm" 18 "github.com/pf-qiu/concourse/v6/tracing" 19 "github.com/lib/pq" 20 "github.com/onsi/ginkgo" 21 . "github.com/onsi/gomega" 22 gocache "github.com/patrickmn/go-cache" 23 "go.opentelemetry.io/otel/label" 24 ) 25 26 type DB struct { 27 BuildInputs []DBRow 28 BuildOutputs []DBRow 29 BuildPipes []DBRow 30 Resources []DBRow 31 NeedsV6Migration bool 32 } 33 34 type DBRow struct { 35 Job string 36 BuildID int 37 Resource string 38 Version string 39 CheckOrder int 40 VersionID int 41 Disabled bool 42 FromBuildID int 43 ToBuildID int 44 RerunOfBuildID int 45 BuildStatus string 46 NoResourceConfigScope bool 47 DoNotInsertVersion bool 48 } 49 50 type Example struct { 51 LoadDB string 52 DB DB 53 Inputs Inputs 54 Result Result 55 Iterations int 56 Error error 57 } 58 59 type Inputs []Input 60 61 type Input struct { 62 Name string 63 Resource string 64 Passed []string 65 Version Version 66 NoResourceConfigScope bool 67 } 68 69 type Version struct { 70 Every bool 71 Latest bool 72 Pinned string 73 } 74 75 type Result struct { 76 OK bool 77 Values map[string]string 78 PassedBuildIDs map[string][]int 79 Errors map[string]string 80 ExpectedMigrated map[int]map[int][]string 81 HasNext bool 82 NoNext bool 83 } 84 85 type StringMapping map[string]int 86 87 func (mapping StringMapping) ID(str string) int { 88 id, found := mapping[str] 89 if !found { 90 id = len(mapping) + 1 91 mapping[str] = id 92 } 93 94 return id 95 } 96 97 func (mapping StringMapping) Name(id int) string { 98 for mappingName, mappingID := range mapping { 99 if id == mappingID { 100 return mappingName 101 } 102 } 103 104 panic(fmt.Sprintf("no name found for %d", id)) 105 } 106 107 const CurrentJobName = "current" 108 109 func (example Example) Run() { 110 currentTest := ginkgo.CurrentGinkgoTestDescription() 111 112 ctx, span := tracing.StartSpan(context.Background(), currentTest.TestText, tracing.Attrs{}) 113 defer span.End() 114 115 setup := setupDB{ 116 teamID: 1, 117 pipelineID: 1, 118 psql: sq.StatementBuilder.PlaceholderFormat(sq.Dollar).RunWith(dbConn), 119 jobIDs: StringMapping{}, 120 resourceIDs: StringMapping{}, 121 versionIDs: StringMapping{}, 122 } 123 124 team, err := teamFactory.CreateTeam(atc.Team{Name: "algorithm"}) 125 Expect(err).NotTo(HaveOccurred()) 126 127 pipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "algorithm"}, atc.Config{}, db.ConfigVersion(0), false) 128 Expect(err).NotTo(HaveOccurred()) 129 130 setupTx, err := dbConn.Begin() 131 Expect(err).ToNot(HaveOccurred()) 132 133 brt := db.BaseResourceType{ 134 Name: "some-base-type", 135 } 136 137 _, err = brt.FindOrCreate(setupTx, false) 138 Expect(err).NotTo(HaveOccurred()) 139 Expect(setupTx.Commit()).To(Succeed()) 140 141 resources := map[string]atc.ResourceConfig{} 142 143 cache := gocache.New(10*time.Second, 10*time.Second) 144 145 var versionsDB db.VersionsDB 146 if example.LoadDB != "" { 147 if os.Getenv("ALGORITHM_REGRESSION") == "" { 148 ginkgo.Skip("skipping; to run, set $ALGORITHM_REGRESSION") 149 } 150 151 versionsDB = example.importVersionsDB(ctx, setup, cache, resources) 152 } else { 153 versionsDB = example.setupVersionsDB(ctx, setup, cache, resources) 154 } 155 156 inputConfigs := make(db.InputConfigs, len(example.Inputs)) 157 for i, input := range example.Inputs { 158 passed := db.JobSet{} 159 for _, jobName := range input.Passed { 160 setup.insertJob(jobName) 161 passed[setup.jobIDs.ID(jobName)] = true 162 } 163 164 inputConfigs[i] = db.InputConfig{ 165 Name: input.Name, 166 Passed: passed, 167 ResourceID: setup.resourceIDs.ID(input.Resource), 168 UseEveryVersion: input.Version.Every, 169 JobID: setup.jobIDs.ID(CurrentJobName), 170 } 171 172 if len(input.Version.Pinned) != 0 { 173 inputConfigs[i].PinnedVersion = atc.Version{"ver": input.Version.Pinned} 174 175 setup.insertPinned(setup.resourceIDs.ID(input.Resource), atc.Version{"ver": input.Version.Pinned}) 176 } 177 } 178 179 setup.insertJob("current") 180 181 job, found, err := pipeline.Job("current") 182 Expect(err).ToNot(HaveOccurred()) 183 Expect(found).To(BeTrue()) 184 185 alg := algorithm.New(versionsDB) 186 187 iterations := 1 188 if example.Iterations != 0 { 189 iterations = example.Iterations 190 } 191 192 for i := 0; i < iterations; i++ { 193 example.assert(ctx, setup, alg, job, inputConfigs) 194 cache.Flush() 195 } 196 } 197 198 func (example Example) importVersionsDB(ctx context.Context, setup setupDB, cache *gocache.Cache, resources map[string]atc.ResourceConfig) db.VersionsDB { 199 ctx, span := tracing.StartSpan(ctx, "importVersionsDB", tracing.Attrs{ 200 "db": example.LoadDB, 201 }) 202 defer span.End() 203 204 versionsDB := db.NewVersionsDB(dbConn, 100, cache) 205 206 dbFile, err := os.Open(example.LoadDB) 207 Expect(err).ToNot(HaveOccurred()) 208 209 gr, err := gzip.NewReader(dbFile) 210 Expect(err).ToNot(HaveOccurred()) 211 212 var debugDB atc.DebugVersionsDB 213 err = func(ctx context.Context) error { 214 ctx, span = tracing.StartSpan(ctx, "Decode", tracing.Attrs{}) 215 defer span.End() 216 return json.NewDecoder(gr).Decode(&debugDB) 217 }(ctx) 218 Expect(err).ToNot(HaveOccurred()) 219 220 span.AddEvent( 221 ctx, 222 "decoded", 223 label.Int("Jobs", len(debugDB.Jobs)), 224 label.Int("Resources", len(debugDB.Resources)), 225 label.Int("LegacyJobIDs", len(debugDB.LegacyJobIDs)), 226 label.Int("LegacyResourceIDs", len(debugDB.LegacyResourceIDs)), 227 label.Int("ResourceVersions", len(debugDB.ResourceVersions)), 228 label.Int("BuildInputs", len(debugDB.BuildInputs)), 229 label.Int("BuildOutputs", len(debugDB.BuildOutputs)), 230 label.Int("BuildReruns", len(debugDB.BuildReruns)), 231 ) 232 233 // legacy, pre-6.0 234 for name, id := range debugDB.LegacyJobIDs { 235 setup.jobIDs[name] = id 236 setup.insertJob(name) 237 } 238 239 for name, id := range debugDB.LegacyResourceIDs { 240 setup.resourceIDs[name] = id 241 242 setup.insertResource(name, &id) 243 resources[name] = atc.ResourceConfig{ 244 Name: name, 245 Type: "some-base-type", 246 Source: atc.Source{ 247 name: "source", 248 }, 249 } 250 } 251 252 // 6.0+ 253 for _, job := range debugDB.Jobs { 254 setup.jobIDs[job.Name] = job.ID 255 setup.insertJob(job.Name) 256 } 257 258 for _, resource := range debugDB.Resources { 259 setup.resourceIDs[resource.Name] = resource.ID 260 261 setup.insertResource(resource.Name, resource.ScopeID) 262 resources[resource.Name] = atc.ResourceConfig{ 263 Name: resource.Name, 264 Type: "some-base-type", 265 Source: atc.Source{ 266 resource.Name: "source", 267 }, 268 } 269 } 270 271 err = func(ctx context.Context) error { 272 ctx, span = tracing.StartSpan(ctx, "import versions", tracing.Attrs{}) 273 defer span.End() 274 275 tx, err := dbConn.Begin() 276 Expect(err).ToNot(HaveOccurred()) 277 278 stmt, err := tx.Prepare(pq.CopyIn("resource_config_versions", "id", "resource_config_scope_id", "version", "version_md5", "check_order")) 279 Expect(err).ToNot(HaveOccurred()) 280 281 for _, row := range debugDB.ResourceVersions { 282 name := fmt.Sprintf("imported-r%dv%d", row.ResourceID, row.VersionID) 283 setup.versionIDs[name] = row.VersionID 284 285 scope := row.ScopeID 286 if scope == 0 { 287 // pre-6.0 288 scope = row.ResourceID 289 } 290 291 _, err := stmt.Exec(row.VersionID, scope, "{}", strconv.Itoa(row.VersionID), row.CheckOrder) 292 Expect(err).ToNot(HaveOccurred()) 293 } 294 295 _, err = stmt.Exec() 296 Expect(err).ToNot(HaveOccurred()) 297 298 err = stmt.Close() 299 Expect(err).ToNot(HaveOccurred()) 300 301 err = tx.Commit() 302 Expect(err).ToNot(HaveOccurred()) 303 304 return nil 305 }(ctx) 306 Expect(err).ToNot(HaveOccurred()) 307 308 err = func(ctx context.Context) error { 309 ctx, span = tracing.StartSpan(ctx, "import builds", tracing.Attrs{}) 310 defer span.End() 311 312 tx, err := dbConn.Begin() 313 Expect(err).ToNot(HaveOccurred()) 314 315 stmt, err := tx.Prepare(pq.CopyIn("builds", "team_id", "id", "job_id", "name", "status")) 316 Expect(err).ToNot(HaveOccurred()) 317 318 imported := map[int]bool{} 319 320 for _, row := range debugDB.BuildOutputs { 321 if imported[row.BuildID] { 322 continue 323 } 324 325 _, err := stmt.Exec(setup.teamID, row.BuildID, row.JobID, row.BuildID, "succeeded") 326 Expect(err).ToNot(HaveOccurred()) 327 328 imported[row.BuildID] = true 329 } 330 331 for _, row := range debugDB.BuildInputs { 332 if imported[row.BuildID] { 333 continue 334 } 335 336 // any builds not created at this point must have failed as they weren't 337 // present via outputs 338 _, err := stmt.Exec(setup.teamID, row.BuildID, row.JobID, row.BuildID, "failed") 339 Expect(err).ToNot(HaveOccurred()) 340 341 imported[row.BuildID] = true 342 } 343 344 for _, row := range debugDB.BuildReruns { 345 if imported[row.RerunOf] { 346 continue 347 } 348 349 // any builds not created at this point must have failed as they weren't 350 // present via outputs 351 _, err := stmt.Exec(setup.teamID, row.RerunOf, row.JobID, "some-name", "failed") 352 Expect(err).ToNot(HaveOccurred()) 353 354 imported[row.RerunOf] = true 355 } 356 357 _, err = stmt.Exec() 358 Expect(err).ToNot(HaveOccurred()) 359 360 err = stmt.Close() 361 Expect(err).ToNot(HaveOccurred()) 362 363 err = tx.Commit() 364 Expect(err).ToNot(HaveOccurred()) 365 366 for _, row := range debugDB.BuildReruns { 367 if !imported[row.BuildID] { 368 // probably a build we don't care about 369 continue 370 } 371 372 _, err = setup.psql.Update("builds"). 373 Set("rerun_of", row.RerunOf). 374 Where(sq.Eq{"id": row.BuildID}). 375 Exec() 376 Expect(err).ToNot(HaveOccurred()) 377 } 378 379 return nil 380 }(ctx) 381 Expect(err).ToNot(HaveOccurred()) 382 383 err = func(ctx context.Context) error { 384 ctx, span = tracing.StartSpan(ctx, "import inputs", tracing.Attrs{}) 385 defer span.End() 386 387 tx, err := dbConn.Begin() 388 Expect(err).ToNot(HaveOccurred()) 389 390 stmt, err := tx.Prepare(pq.CopyIn("build_resource_config_version_inputs", "build_id", "resource_id", "version_md5", "name", "first_occurrence")) 391 Expect(err).ToNot(HaveOccurred()) 392 393 for i, row := range debugDB.BuildInputs { 394 _, err := stmt.Exec(row.BuildID, row.ResourceID, strconv.Itoa(row.VersionID), strconv.Itoa(i), false) 395 Expect(err).ToNot(HaveOccurred()) 396 } 397 398 _, err = stmt.Exec() 399 Expect(err).ToNot(HaveOccurred()) 400 401 err = stmt.Close() 402 Expect(err).ToNot(HaveOccurred()) 403 404 err = tx.Commit() 405 Expect(err).ToNot(HaveOccurred()) 406 407 return nil 408 }(ctx) 409 Expect(err).ToNot(HaveOccurred()) 410 411 err = func(ctx context.Context) error { 412 ctx, span = tracing.StartSpan(ctx, "import outputs", tracing.Attrs{}) 413 defer span.End() 414 415 tx, err := dbConn.Begin() 416 Expect(err).ToNot(HaveOccurred()) 417 418 stmt, err := tx.Prepare(pq.CopyIn("build_resource_config_version_outputs", "build_id", "resource_id", "version_md5", "name")) 419 Expect(err).ToNot(HaveOccurred()) 420 421 for i, row := range debugDB.BuildOutputs { 422 _, err := stmt.Exec(row.BuildID, row.ResourceID, strconv.Itoa(row.VersionID), strconv.Itoa(i)) 423 Expect(err).ToNot(HaveOccurred()) 424 } 425 426 _, err = stmt.Exec() 427 Expect(err).ToNot(HaveOccurred()) 428 429 err = stmt.Close() 430 Expect(err).ToNot(HaveOccurred()) 431 432 err = tx.Commit() 433 Expect(err).ToNot(HaveOccurred()) 434 435 return nil 436 }(ctx) 437 Expect(err).ToNot(HaveOccurred()) 438 439 return versionsDB 440 } 441 442 func (example Example) setupVersionsDB(ctx context.Context, setup setupDB, cache *gocache.Cache, resources map[string]atc.ResourceConfig) db.VersionsDB { 443 ctx, span := tracing.StartSpan(ctx, "setupVersionsDB", tracing.Attrs{}) 444 defer span.End() 445 446 versionsDB := db.NewVersionsDB(dbConn, 2, cache) 447 448 for _, row := range example.DB.Resources { 449 setup.insertRowVersion(resources, row) 450 } 451 452 buildOutputs := map[int]map[string][]string{} 453 buildToJobID := map[int]int{} 454 buildToRerunOf := map[int]int{} 455 for _, row := range example.DB.BuildOutputs { 456 setup.insertRowVersion(resources, row) 457 setup.insertRowBuild(row, example.DB.NeedsV6Migration) 458 459 resourceID := setup.resourceIDs.ID(row.Resource) 460 461 versionJSON, err := json.Marshal(atc.Version{"ver": row.Version}) 462 Expect(err).ToNot(HaveOccurred()) 463 464 _, err = setup.psql.Insert("build_resource_config_version_outputs"). 465 Columns("build_id", "resource_id", "version_md5", "name"). 466 Values(row.BuildID, resourceID, sq.Expr("md5(?)", versionJSON), row.Resource). 467 Exec() 468 Expect(err).ToNot(HaveOccurred()) 469 470 if !example.DB.NeedsV6Migration { 471 outputs, ok := buildOutputs[row.BuildID] 472 if !ok { 473 outputs = map[string][]string{} 474 buildOutputs[row.BuildID] = outputs 475 } 476 477 key := strconv.Itoa(resourceID) 478 479 outputs[key] = append(outputs[key], convertToMD5(row.Version)) 480 buildToJobID[row.BuildID] = setup.jobIDs.ID(row.Job) 481 482 if row.RerunOfBuildID != 0 { 483 buildToRerunOf[row.BuildID] = row.RerunOfBuildID 484 } 485 } 486 } 487 488 for _, row := range example.DB.BuildInputs { 489 setup.insertRowVersion(resources, row) 490 setup.insertRowBuild(row, example.DB.NeedsV6Migration) 491 492 resourceID := setup.resourceIDs.ID(row.Resource) 493 494 versionJSON, err := json.Marshal(atc.Version{"ver": row.Version}) 495 Expect(err).ToNot(HaveOccurred()) 496 497 _, err = setup.psql.Insert("build_resource_config_version_inputs"). 498 Columns("build_id", "resource_id", "version_md5", "name", "first_occurrence"). 499 Values(row.BuildID, resourceID, sq.Expr("md5(?)", versionJSON), row.Resource, false). 500 Exec() 501 Expect(err).ToNot(HaveOccurred()) 502 503 if !example.DB.NeedsV6Migration { 504 outputs, ok := buildOutputs[row.BuildID] 505 if !ok { 506 outputs = map[string][]string{} 507 buildOutputs[row.BuildID] = outputs 508 } 509 510 key := strconv.Itoa(resourceID) 511 512 outputs[key] = append(outputs[key], convertToMD5(row.Version)) 513 buildToJobID[row.BuildID] = setup.jobIDs.ID(row.Job) 514 515 if row.RerunOfBuildID != 0 { 516 buildToRerunOf[row.BuildID] = row.RerunOfBuildID 517 } 518 } 519 } 520 521 for buildID, outputs := range buildOutputs { 522 outputsJSON, err := json.Marshal(outputs) 523 Expect(err).ToNot(HaveOccurred()) 524 525 var rerunOf sql.NullInt64 526 if buildToRerunOf[buildID] != 0 { 527 rerunOf.Int64 = int64(buildToRerunOf[buildID]) 528 } 529 530 _, err = setup.psql.Insert("successful_build_outputs"). 531 Columns("build_id", "job_id", "rerun_of", "outputs"). 532 Values(buildID, buildToJobID[buildID], rerunOf, outputsJSON). 533 Suffix("ON CONFLICT DO NOTHING"). 534 Exec() 535 Expect(err).ToNot(HaveOccurred()) 536 } 537 538 for _, row := range example.DB.BuildPipes { 539 setup.insertBuildPipe(row) 540 } 541 542 for _, input := range example.Inputs { 543 resourceID := setup.resourceIDs.ID(input.Resource) 544 545 var scope *int 546 if !input.NoResourceConfigScope { 547 scope = &resourceID 548 } 549 550 setup.insertResource(input.Resource, scope) 551 552 resources[input.Resource] = atc.ResourceConfig{ 553 Name: input.Resource, 554 Type: "some-base-type", 555 Source: atc.Source{ 556 input.Resource: "source", 557 }, 558 } 559 } 560 561 return versionsDB 562 } 563 564 func (example Example) assert( 565 ctx context.Context, 566 setup setupDB, 567 alg *algorithm.Algorithm, 568 job db.Job, 569 inputConfigs db.InputConfigs, 570 ) { 571 ctx, span := tracing.StartSpan(ctx, "assert", tracing.Attrs{}) 572 defer span.End() 573 574 span.SetAttributes(label.Int64("seed", ginkgo.GinkgoRandomSeed())) 575 576 resolved, ok, hasNext, resolvedErr := alg.Compute(ctx, job, inputConfigs) 577 if example.Error != nil { 578 Expect(resolvedErr).To(Equal(example.Error)) 579 } else { 580 Expect(resolvedErr).ToNot(HaveOccurred()) 581 582 prettyValues := map[string]string{} 583 erroredValues := map[string]string{} 584 passedJobs := map[string][]int{} 585 for name, inputSource := range resolved { 586 if inputSource.ResolveError != "" { 587 erroredValues[name] = string(inputSource.ResolveError) 588 } else { 589 if ok { 590 var versionID int 591 err := setup.psql.Select("v.id"). 592 From("resource_config_versions v"). 593 Join("resources r ON r.resource_config_scope_id = v.resource_config_scope_id"). 594 Where(sq.Eq{ 595 "v.version_md5": inputSource.Input.AlgorithmVersion.Version, 596 "r.id": inputSource.Input.ResourceID, 597 }). 598 QueryRow(). 599 Scan(&versionID) 600 Expect(err).ToNot(HaveOccurred()) 601 602 prettyValues[name] = setup.versionIDs.Name(versionID) 603 604 passedJobs[name] = inputSource.PassedBuildIDs 605 } 606 } 607 } 608 609 actualResult := Result{OK: ok} 610 if len(erroredValues) != 0 { 611 actualResult.Errors = erroredValues 612 } 613 614 if example.Result.PassedBuildIDs != nil { 615 actualResult.PassedBuildIDs = passedJobs 616 } 617 618 if ok { 619 actualResult.Values = prettyValues 620 } 621 622 Expect(actualResult.OK).To(Equal(example.Result.OK)) 623 Expect(actualResult.Errors).To(Equal(example.Result.Errors)) 624 Expect(actualResult.Values).To(Equal(example.Result.Values)) 625 626 for input, buildIDs := range example.Result.PassedBuildIDs { 627 Expect(actualResult.PassedBuildIDs[input]).To(ConsistOf(buildIDs)) 628 } 629 630 if example.Result.ExpectedMigrated != nil { 631 rows, err := setup.psql.Select("build_id", "job_id", "outputs", "rerun_of"). 632 From("successful_build_outputs"). 633 Query() 634 Expect(err).ToNot(HaveOccurred()) 635 636 actualMigrated := map[int]map[int][]string{} 637 jobToBuilds := map[int]int{} 638 rerunOfBuilds := map[int]int{} 639 for rows.Next() { 640 var buildID, jobID int 641 var rerunOf sql.NullInt64 642 var outputs string 643 644 err = rows.Scan(&buildID, &jobID, &outputs, &rerunOf) 645 Expect(err).ToNot(HaveOccurred()) 646 647 _, exists := actualMigrated[buildID] 648 Expect(exists).To(BeFalse()) 649 650 buildOutputs := map[int][]string{} 651 err = json.Unmarshal([]byte(outputs), &buildOutputs) 652 actualMigrated[buildID] = buildOutputs 653 654 jobToBuilds[buildID] = jobID 655 656 if rerunOf.Valid { 657 rerunOfBuilds[buildID] = int(rerunOf.Int64) 658 } 659 } 660 661 Expect(actualMigrated).To(Equal(example.Result.ExpectedMigrated)) 662 663 for buildID, jobID := range jobToBuilds { 664 var actualJobID int 665 666 err = setup.psql.Select("job_id"). 667 From("builds"). 668 Where(sq.Eq{ 669 "id": buildID, 670 }). 671 QueryRow(). 672 Scan(&actualJobID) 673 Expect(err).ToNot(HaveOccurred()) 674 Expect(jobID).To(Equal(actualJobID)) 675 } 676 677 for buildID, rerunBuildID := range rerunOfBuilds { 678 var actualRerunOfBuildID int 679 680 err = setup.psql.Select("rerun_of"). 681 From("builds"). 682 Where(sq.Eq{ 683 "id": buildID, 684 }). 685 QueryRow(). 686 Scan(&actualRerunOfBuildID) 687 Expect(err).ToNot(HaveOccurred()) 688 Expect(rerunBuildID).To(Equal(actualRerunOfBuildID)) 689 } 690 } 691 692 if example.Result.HasNext == true { 693 Expect(hasNext).To(Equal(true)) 694 } 695 696 if example.Result.NoNext == true { 697 Expect(hasNext).To(Equal(false)) 698 } 699 } 700 } 701 702 type setupDB struct { 703 teamID int 704 pipelineID int 705 706 jobIDs StringMapping 707 resourceIDs StringMapping 708 versionIDs StringMapping 709 710 psql sq.StatementBuilderType 711 } 712 713 func (s setupDB) insertJob(jobName string) int { 714 id := s.jobIDs.ID(jobName) 715 _, err := s.psql.Insert("jobs"). 716 Columns("id", "pipeline_id", "name", "config", "active"). 717 Values(id, s.pipelineID, jobName, "{}", true). 718 Suffix("ON CONFLICT DO NOTHING"). 719 Exec() 720 Expect(err).ToNot(HaveOccurred()) 721 722 return id 723 } 724 725 func (s setupDB) insertResource(name string, scope *int) int { 726 resourceID := s.resourceIDs.ID(name) 727 728 // just make them one-to-one 729 resourceConfigID := resourceID 730 731 j, err := json.Marshal(atc.Source{name: "source"}) 732 Expect(err).ToNot(HaveOccurred()) 733 734 _, err = s.psql.Insert("resource_configs"). 735 Columns("id", "source_hash", "base_resource_type_id"). 736 Values(resourceConfigID, fmt.Sprintf("%x", sha256.Sum256(j)), 1). 737 Suffix("ON CONFLICT DO NOTHING"). 738 Exec() 739 Expect(err).ToNot(HaveOccurred()) 740 741 var rcsID sql.NullInt64 742 if scope != nil { 743 _, err = s.psql.Insert("resource_config_scopes"). 744 Columns("id", "resource_config_id"). 745 Values(*scope, resourceConfigID). 746 Suffix("ON CONFLICT DO NOTHING"). 747 Exec() 748 Expect(err).ToNot(HaveOccurred()) 749 750 rcsID = sql.NullInt64{ 751 Int64: int64(*scope), 752 Valid: true, 753 } 754 } 755 756 _, err = s.psql.Insert("resources"). 757 Columns("id", "name", "type", "config", "pipeline_id", "resource_config_id", "resource_config_scope_id"). 758 Values(resourceID, name, fmt.Sprintf("%s-type", name), "{}", s.pipelineID, resourceID, rcsID). 759 Suffix("ON CONFLICT (name, pipeline_id) DO UPDATE SET id = EXCLUDED.id, resource_config_id = EXCLUDED.resource_config_id, resource_config_scope_id = EXCLUDED.resource_config_scope_id"). 760 Exec() 761 Expect(err).ToNot(HaveOccurred()) 762 763 return resourceID 764 } 765 766 func (s setupDB) insertRowVersion(resources map[string]atc.ResourceConfig, row DBRow) { 767 resourceID := s.resourceIDs.ID(row.Resource) 768 versionID := s.versionIDs.ID(row.Version) 769 770 var scope *int 771 if !row.NoResourceConfigScope { 772 scope = &resourceID 773 } 774 775 s.insertResource(row.Resource, scope) 776 resources[row.Resource] = atc.ResourceConfig{ 777 Name: row.Resource, 778 Type: "some-base-type", 779 Source: atc.Source{ 780 row.Resource: "source", 781 }, 782 } 783 784 if row.NoResourceConfigScope || row.DoNotInsertVersion { 785 // there's no version to insert if it has no scope 786 return 787 } 788 789 versionJSON, err := json.Marshal(atc.Version{"ver": row.Version}) 790 Expect(err).ToNot(HaveOccurred()) 791 792 _, err = s.psql.Insert("resource_config_versions"). 793 Columns("id", "resource_config_scope_id", "version", "version_md5", "check_order"). 794 Values(versionID, resourceID, versionJSON, sq.Expr("md5(?)", versionJSON), row.CheckOrder). 795 Suffix("ON CONFLICT DO NOTHING"). 796 Exec() 797 Expect(err).ToNot(HaveOccurred()) 798 799 if row.Disabled { 800 _, err = s.psql.Insert("resource_disabled_versions"). 801 Columns("resource_id", "version_md5"). 802 Values(resourceID, sq.Expr("md5(?)", versionJSON)). 803 Suffix("ON CONFLICT DO NOTHING"). 804 Exec() 805 Expect(err).ToNot(HaveOccurred()) 806 } 807 } 808 809 func (s setupDB) insertRowBuild(row DBRow, needsV6Migration bool) { 810 jobID := s.insertJob(row.Job) 811 812 var rerunOf sql.NullInt64 813 if row.RerunOfBuildID != 0 { 814 rerunOf = sql.NullInt64{Int64: int64(row.RerunOfBuildID), Valid: true} 815 } 816 817 buildStatus := "succeeded" 818 if len(row.BuildStatus) != 0 { 819 buildStatus = row.BuildStatus 820 } 821 822 var existingJobID int 823 err := s.psql.Insert("builds"). 824 Columns("team_id", "id", "job_id", "name", "status", "scheduled", "inputs_ready", "rerun_of", "needs_v6_migration"). 825 Values(s.teamID, row.BuildID, jobID, row.BuildID, buildStatus, true, true, rerunOf, needsV6Migration). 826 Suffix("ON CONFLICT (id) DO UPDATE SET name = excluded.name"). 827 Suffix("RETURNING job_id"). 828 QueryRow(). 829 Scan(&existingJobID) 830 Expect(err).ToNot(HaveOccurred()) 831 832 Expect(existingJobID).To(Equal(jobID), fmt.Sprintf("build ID %d already used by job other than %s", row.BuildID, row.Job)) 833 834 _, err = s.psql.Update("jobs"). 835 Set("latest_completed_build_id", row.BuildID). 836 Where(sq.Eq{ 837 "id": jobID, 838 }). 839 Exec() 840 Expect(err).ToNot(HaveOccurred()) 841 } 842 843 func (s setupDB) insertBuildPipe(row DBRow) { 844 _, err := s.psql.Insert("build_pipes"). 845 Columns("from_build_id", "to_build_id"). 846 Values(row.FromBuildID, row.ToBuildID). 847 Suffix("ON CONFLICT DO NOTHING"). 848 Exec() 849 Expect(err).ToNot(HaveOccurred()) 850 } 851 852 func (s setupDB) insertPinned(resourceID int, version atc.Version) { 853 versionJSON, err := json.Marshal(version) 854 Expect(err).ToNot(HaveOccurred()) 855 856 _, err = s.psql.Insert("resource_pins"). 857 Columns("resource_id", "version", "comment_text"). 858 Values(resourceID, versionJSON, ""). 859 Suffix("ON CONFLICT DO NOTHING"). 860 Exec() 861 Expect(err).ToNot(HaveOccurred()) 862 }