github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/resource.go (about) 1 package db 2 3 import ( 4 "context" 5 "database/sql" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "strconv" 10 "time" 11 12 sq "github.com/Masterminds/squirrel" 13 "github.com/lib/pq" 14 15 "github.com/pf-qiu/concourse/v6/atc" 16 "github.com/pf-qiu/concourse/v6/atc/db/lock" 17 ) 18 19 var ErrPinnedThroughConfig = errors.New("resource is pinned through config") 20 21 const CheckBuildName = "check" 22 23 //go:generate counterfeiter . Resource 24 25 type Resource interface { 26 PipelineRef 27 28 ID() int 29 Name() string 30 Public() bool 31 TeamID() int 32 TeamName() string 33 Type() string 34 Source() atc.Source 35 CheckEvery() string 36 CheckTimeout() string 37 LastCheckStartTime() time.Time 38 LastCheckEndTime() time.Time 39 Tags() atc.Tags 40 WebhookToken() string 41 Config() atc.ResourceConfig 42 ConfigPinnedVersion() atc.Version 43 APIPinnedVersion() atc.Version 44 PinComment() string 45 SetPinComment(string) error 46 ResourceConfigID() int 47 ResourceConfigScopeID() int 48 Icon() string 49 50 HasWebhook() bool 51 52 CurrentPinnedVersion() atc.Version 53 54 BuildSummary() *atc.BuildSummary 55 56 Versions(page Page, versionFilter atc.Version) ([]atc.ResourceVersion, Pagination, bool, error) 57 FindVersion(filter atc.Version) (ResourceConfigVersion, bool, error) // Only used in tests!! 58 UpdateMetadata(atc.Version, ResourceConfigMetadataFields) (bool, error) 59 60 EnableVersion(rcvID int) error 61 DisableVersion(rcvID int) error 62 63 PinVersion(rcvID int) (bool, error) 64 UnpinVersion() error 65 66 SetResourceConfigScope(ResourceConfigScope) error 67 68 CheckPlan(atc.Version, time.Duration, ResourceTypes, atc.Source) atc.CheckPlan 69 CreateBuild(context.Context, bool, atc.Plan) (Build, bool, error) 70 71 NotifyScan() error 72 73 Reload() (bool, error) 74 } 75 76 var resourcesQuery = psql.Select( 77 "r.id", 78 "r.name", 79 "r.type", 80 "r.config", 81 "rs.last_check_start_time", 82 "rs.last_check_end_time", 83 "r.pipeline_id", 84 "r.nonce", 85 "r.resource_config_id", 86 "r.resource_config_scope_id", 87 "p.name", 88 "p.instance_vars", 89 "t.id", 90 "t.name", 91 "rp.version", 92 "rp.comment_text", 93 "rp.config", 94 "b.id", 95 "b.name", 96 "b.status", 97 "b.start_time", 98 "b.end_time", 99 ). 100 From("resources r"). 101 Join("pipelines p ON p.id = r.pipeline_id"). 102 Join("teams t ON t.id = p.team_id"). 103 LeftJoin("builds b ON b.id = r.build_id"). 104 LeftJoin("resource_config_scopes rs ON r.resource_config_scope_id = rs.id"). 105 LeftJoin("resource_pins rp ON rp.resource_id = r.id"). 106 Where(sq.Eq{"r.active": true}) 107 108 type resource struct { 109 pipelineRef 110 111 id int 112 name string 113 teamID int 114 teamName string 115 type_ string 116 lastCheckStartTime time.Time 117 lastCheckEndTime time.Time 118 config atc.ResourceConfig 119 configPinnedVersion atc.Version 120 apiPinnedVersion atc.Version 121 pinComment string 122 resourceConfigID int 123 resourceConfigScopeID int 124 buildSummary *atc.BuildSummary 125 } 126 127 func newEmptyResource(conn Conn, lockFactory lock.LockFactory) *resource { 128 return &resource{pipelineRef: pipelineRef{conn: conn, lockFactory: lockFactory}} 129 } 130 131 type ResourceNotFoundError struct { 132 ID int 133 } 134 135 func (e ResourceNotFoundError) Error() string { 136 return fmt.Sprintf("resource '%d' not found", e.ID) 137 } 138 139 type Resources []Resource 140 141 func (resources Resources) Lookup(name string) (Resource, bool) { 142 for _, resource := range resources { 143 if resource.Name() == name { 144 return resource, true 145 } 146 } 147 148 return nil, false 149 } 150 151 func (resources Resources) Configs() atc.ResourceConfigs { 152 var configs atc.ResourceConfigs 153 for _, r := range resources { 154 configs = append(configs, r.Config()) 155 } 156 return configs 157 } 158 159 func (r *resource) ID() int { return r.id } 160 func (r *resource) Name() string { return r.name } 161 func (r *resource) Public() bool { return r.config.Public } 162 func (r *resource) TeamID() int { return r.teamID } 163 func (r *resource) TeamName() string { return r.teamName } 164 func (r *resource) Type() string { return r.type_ } 165 func (r *resource) Source() atc.Source { return r.config.Source } 166 func (r *resource) CheckEvery() string { return r.config.CheckEvery } 167 func (r *resource) CheckTimeout() string { return r.config.CheckTimeout } 168 func (r *resource) LastCheckStartTime() time.Time { return r.lastCheckStartTime } 169 func (r *resource) LastCheckEndTime() time.Time { return r.lastCheckEndTime } 170 func (r *resource) Tags() atc.Tags { return r.config.Tags } 171 func (r *resource) WebhookToken() string { return r.config.WebhookToken } 172 func (r *resource) Config() atc.ResourceConfig { return r.config } 173 func (r *resource) ConfigPinnedVersion() atc.Version { return r.configPinnedVersion } 174 func (r *resource) APIPinnedVersion() atc.Version { return r.apiPinnedVersion } 175 func (r *resource) PinComment() string { return r.pinComment } 176 func (r *resource) ResourceConfigID() int { return r.resourceConfigID } 177 func (r *resource) ResourceConfigScopeID() int { return r.resourceConfigScopeID } 178 func (r *resource) Icon() string { return r.config.Icon } 179 180 func (r *resource) HasWebhook() bool { return r.WebhookToken() != "" } 181 182 func (r *resource) Reload() (bool, error) { 183 row := resourcesQuery.Where(sq.Eq{"r.id": r.id}). 184 RunWith(r.conn). 185 QueryRow() 186 187 err := scanResource(r, row) 188 if err != nil { 189 if err == sql.ErrNoRows { 190 return false, nil 191 } 192 return false, err 193 } 194 195 return true, nil 196 } 197 198 func (r *resource) SetResourceConfig(atc.Source, atc.VersionedResourceTypes) (ResourceConfigScope, error) { 199 return nil, fmt.Errorf("not implemented") 200 } 201 202 func (r *resource) SetResourceConfigScope(scope ResourceConfigScope) error { 203 tx, err := r.conn.Begin() 204 if err != nil { 205 return err 206 } 207 208 defer Rollback(tx) 209 210 err = r.setResourceConfigScopeInTransaction(tx, scope) 211 if err != nil { 212 return err 213 } 214 215 err = tx.Commit() 216 if err != nil { 217 return err 218 } 219 220 return nil 221 } 222 223 func (r *resource) setResourceConfigScopeInTransaction(tx Tx, scope ResourceConfigScope) error { 224 results, err := psql.Update("resources"). 225 Set("resource_config_id", scope.ResourceConfig().ID()). 226 Set("resource_config_scope_id", scope.ID()). 227 Where(sq.Eq{"id": r.id}). 228 Where(sq.Or{ 229 sq.Eq{"resource_config_id": nil}, 230 sq.Eq{"resource_config_scope_id": nil}, 231 sq.NotEq{"resource_config_id": scope.ResourceConfig().ID()}, 232 sq.NotEq{"resource_config_scope_id": scope.ID()}, 233 }). 234 RunWith(tx). 235 Exec() 236 if err != nil { 237 return err 238 } 239 240 rowsAffected, err := results.RowsAffected() 241 if err != nil { 242 return err 243 } 244 245 if rowsAffected > 0 { 246 err = requestScheduleForJobsUsingResource(tx, r.id) 247 if err != nil { 248 return err 249 } 250 } 251 252 return nil 253 } 254 255 func (r *resource) CheckPlan(from atc.Version, interval time.Duration, resourceTypes ResourceTypes, sourceDefaults atc.Source) atc.CheckPlan { 256 return atc.CheckPlan{ 257 Name: r.Name(), 258 Type: r.Type(), 259 Source: sourceDefaults.Merge(r.Source()), 260 Tags: r.Tags(), 261 Timeout: r.CheckTimeout(), 262 263 FromVersion: from, 264 Interval: interval.String(), 265 VersionedResourceTypes: resourceTypes.Deserialize(), 266 267 Resource: r.Name(), 268 } 269 } 270 271 func (r *resource) CreateBuild(ctx context.Context, manuallyTriggered bool, plan atc.Plan) (Build, bool, error) { 272 spanContextJSON, err := json.Marshal(NewSpanContext(ctx)) 273 if err != nil { 274 return nil, false, err 275 } 276 277 tx, err := r.conn.Begin() 278 if err != nil { 279 return nil, false, err 280 } 281 282 defer Rollback(tx) 283 284 if !manuallyTriggered { 285 var completed, noBuild bool 286 err = psql.Select("completed"). 287 From("builds"). 288 Where(sq.Eq{"resource_id": r.id}). 289 RunWith(tx). 290 QueryRow(). 291 Scan(&completed) 292 if err != nil { 293 if err == sql.ErrNoRows { 294 noBuild = true 295 } else { 296 return nil, false, err 297 } 298 } 299 300 if !noBuild && !completed { 301 // a build is already running; leave it be 302 return nil, false, nil 303 } 304 305 if completed { 306 // previous build finished; clear it out 307 rows, err := psql.Delete("builds"). 308 Where(sq.Eq{ 309 "resource_id": r.id, 310 "completed": true, 311 }). 312 Suffix("RETURNING id"). 313 RunWith(tx). 314 Query() 315 if err != nil { 316 return nil, false, fmt.Errorf("delete previous build: %w", err) 317 } 318 319 deletedBuildIDs := []int{} 320 for rows.Next() { 321 var id int 322 err := rows.Scan(&id) 323 if err != nil { 324 return nil, false, fmt.Errorf("scan deleted build id: %w", err) 325 } 326 327 deletedBuildIDs = append(deletedBuildIDs, id) 328 } 329 330 _, err = psql.Delete("build_events"). 331 Where(sq.Eq{ 332 "build_id": deletedBuildIDs, 333 }). 334 RunWith(tx). 335 Exec() 336 if err != nil { 337 return nil, false, fmt.Errorf("delete previous build events: %w", err) 338 } 339 } 340 } 341 342 build := newEmptyBuild(r.conn, r.lockFactory) 343 err = createBuild(tx, build, map[string]interface{}{ 344 "name": CheckBuildName, 345 "pipeline_id": r.pipelineID, 346 "team_id": r.teamID, 347 "status": BuildStatusPending, 348 "manually_triggered": manuallyTriggered, 349 "span_context": string(spanContextJSON), 350 "resource_id": r.id, 351 }) 352 if err != nil { 353 return nil, false, err 354 } 355 356 _, err = build.start(tx, plan) 357 if err != nil { 358 return nil, false, err 359 } 360 361 _, err = psql.Update("resources"). 362 Set("build_id", build.ID()). 363 Where(sq.Eq{"id": r.id}). 364 RunWith(tx). 365 Exec() 366 if err != nil { 367 return nil, false, err 368 } 369 370 err = tx.Commit() 371 if err != nil { 372 return nil, false, err 373 } 374 375 err = r.conn.Bus().Notify(atc.ComponentBuildTracker) 376 if err != nil { 377 return nil, false, err 378 } 379 380 _, err = build.Reload() 381 if err != nil { 382 return nil, false, err 383 } 384 385 return build, true, nil 386 } 387 388 func (r *resource) UpdateMetadata(version atc.Version, metadata ResourceConfigMetadataFields) (bool, error) { 389 versionJSON, err := json.Marshal(version) 390 if err != nil { 391 return false, err 392 } 393 394 metadataJSON, err := json.Marshal(metadata) 395 if err != nil { 396 return false, err 397 } 398 399 _, err = psql.Update("resource_config_versions"). 400 Set("metadata", string(metadataJSON)). 401 Where(sq.Eq{ 402 "resource_config_scope_id": r.ResourceConfigScopeID(), 403 }). 404 Where(sq.Expr( 405 "version_md5 = md5(?)", versionJSON, 406 )). 407 RunWith(r.conn). 408 Exec() 409 410 if err != nil { 411 if err == sql.ErrNoRows { 412 return false, nil 413 } 414 return false, err 415 } 416 return true, nil 417 } 418 419 // XXX: Deprecated, only used in tests 420 func (r *resource) FindVersion(v atc.Version) (ResourceConfigVersion, bool, error) { 421 if r.resourceConfigScopeID == 0 { 422 return nil, false, nil 423 } 424 425 ver := &resourceConfigVersion{ 426 conn: r.conn, 427 } 428 429 versionByte, err := json.Marshal(v) 430 if err != nil { 431 return nil, false, err 432 } 433 434 row := resourceConfigVersionQuery. 435 Where(sq.Eq{ 436 "v.resource_config_scope_id": r.resourceConfigScopeID, 437 }). 438 Where(sq.Expr("v.version_md5 = md5(?)", versionByte)). 439 RunWith(r.conn). 440 QueryRow() 441 442 err = scanResourceConfigVersion(ver, row) 443 if err != nil { 444 if err == sql.ErrNoRows { 445 return nil, false, nil 446 } 447 return nil, false, err 448 } 449 450 return ver, true, nil 451 } 452 453 func (r *resource) SetPinComment(comment string) error { 454 _, err := psql.Update("resource_pins"). 455 Set("comment_text", comment). 456 Where(sq.Eq{"resource_id": r.ID()}). 457 RunWith(r.conn). 458 Exec() 459 460 return err 461 } 462 463 func (r *resource) CurrentPinnedVersion() atc.Version { 464 if r.configPinnedVersion != nil { 465 return r.configPinnedVersion 466 } else if r.apiPinnedVersion != nil { 467 return r.apiPinnedVersion 468 } 469 return nil 470 } 471 472 func (r *resource) BuildSummary() *atc.BuildSummary { 473 return r.buildSummary 474 } 475 476 func (r *resource) Versions(page Page, versionFilter atc.Version) ([]atc.ResourceVersion, Pagination, bool, error) { 477 tx, err := r.conn.Begin() 478 if err != nil { 479 return nil, Pagination{}, false, err 480 } 481 482 defer Rollback(tx) 483 484 query := ` 485 SELECT v.id, v.version, v.metadata, v.check_order, 486 NOT EXISTS ( 487 SELECT 1 488 FROM resource_disabled_versions d 489 WHERE v.version_md5 = d.version_md5 490 AND r.resource_config_scope_id = v.resource_config_scope_id 491 AND r.id = d.resource_id 492 ) 493 FROM resource_config_versions v, resources r 494 WHERE r.id = $1 AND r.resource_config_scope_id = v.resource_config_scope_id 495 ` 496 497 filterJSON := "{}" 498 if len(versionFilter) != 0 { 499 filterBytes, err := json.Marshal(versionFilter) 500 if err != nil { 501 return nil, Pagination{}, false, err 502 } 503 504 filterJSON = string(filterBytes) 505 } 506 507 var rows *sql.Rows 508 if page.From != nil { 509 rows, err = tx.Query(fmt.Sprintf(` 510 SELECT sub.* 511 FROM ( 512 %s 513 AND version @> $4 514 AND v.check_order >= (SELECT check_order FROM resource_config_versions WHERE id = $2) 515 ORDER BY v.check_order ASC 516 LIMIT $3 517 ) sub 518 ORDER BY sub.check_order DESC 519 `, query), r.id, *page.From, page.Limit, filterJSON) 520 if err != nil { 521 return nil, Pagination{}, false, err 522 } 523 } else if page.To != nil { 524 rows, err = tx.Query(fmt.Sprintf(` 525 %s 526 AND version @> $4 527 AND v.check_order <= (SELECT check_order FROM resource_config_versions WHERE id = $2) 528 ORDER BY v.check_order DESC 529 LIMIT $3 530 `, query), r.id, *page.To, page.Limit, filterJSON) 531 if err != nil { 532 return nil, Pagination{}, false, err 533 } 534 } else { 535 rows, err = tx.Query(fmt.Sprintf(` 536 %s 537 AND version @> $3 538 ORDER BY v.check_order DESC 539 LIMIT $2 540 `, query), r.id, page.Limit, filterJSON) 541 if err != nil { 542 return nil, Pagination{}, false, err 543 } 544 } 545 546 defer Close(rows) 547 548 type rcvCheckOrder struct { 549 ResourceConfigVersionID int 550 CheckOrder int 551 } 552 553 rvs := make([]atc.ResourceVersion, 0) 554 checkOrderRVs := make([]rcvCheckOrder, 0) 555 for rows.Next() { 556 var ( 557 metadataBytes sql.NullString 558 versionBytes string 559 checkOrder int 560 ) 561 562 rv := atc.ResourceVersion{} 563 err := rows.Scan(&rv.ID, &versionBytes, &metadataBytes, &checkOrder, &rv.Enabled) 564 if err != nil { 565 return nil, Pagination{}, false, err 566 } 567 568 err = json.Unmarshal([]byte(versionBytes), &rv.Version) 569 if err != nil { 570 return nil, Pagination{}, false, err 571 } 572 573 if metadataBytes.Valid { 574 err = json.Unmarshal([]byte(metadataBytes.String), &rv.Metadata) 575 if err != nil { 576 return nil, Pagination{}, false, err 577 } 578 } 579 580 checkOrderRV := rcvCheckOrder{ 581 ResourceConfigVersionID: rv.ID, 582 CheckOrder: checkOrder, 583 } 584 585 rvs = append(rvs, rv) 586 checkOrderRVs = append(checkOrderRVs, checkOrderRV) 587 } 588 589 if len(rvs) == 0 { 590 return nil, Pagination{}, true, nil 591 } 592 593 newestRCVCheckOrder := checkOrderRVs[0] 594 oldestRCVCheckOrder := checkOrderRVs[len(checkOrderRVs)-1] 595 596 var pagination Pagination 597 598 var olderRCVId int 599 err = tx.QueryRow(` 600 SELECT v.id 601 FROM resource_config_versions v, resources r 602 WHERE v.check_order < $2 AND r.id = $1 AND v.resource_config_scope_id = r.resource_config_scope_id 603 ORDER BY v.check_order DESC 604 LIMIT 1 605 `, r.id, oldestRCVCheckOrder.CheckOrder).Scan(&olderRCVId) 606 if err != nil && err != sql.ErrNoRows { 607 return nil, Pagination{}, false, err 608 } else if err == nil { 609 pagination.Older = &Page{ 610 To: &olderRCVId, 611 Limit: page.Limit, 612 } 613 } 614 615 var newerRCVId int 616 err = tx.QueryRow(` 617 SELECT v.id 618 FROM resource_config_versions v, resources r 619 WHERE v.check_order > $2 AND r.id = $1 AND v.resource_config_scope_id = r.resource_config_scope_id 620 ORDER BY v.check_order ASC 621 LIMIT 1 622 `, r.id, newestRCVCheckOrder.CheckOrder).Scan(&newerRCVId) 623 if err != nil && err != sql.ErrNoRows { 624 return nil, Pagination{}, false, err 625 } else if err == nil { 626 pagination.Newer = &Page{ 627 From: &newerRCVId, 628 Limit: page.Limit, 629 } 630 } 631 632 err = tx.Commit() 633 if err != nil { 634 return nil, Pagination{}, false, nil 635 } 636 637 return rvs, pagination, true, nil 638 } 639 640 func (r *resource) EnableVersion(rcvID int) error { 641 return r.toggleVersion(rcvID, true) 642 } 643 644 func (r *resource) DisableVersion(rcvID int) error { 645 return r.toggleVersion(rcvID, false) 646 } 647 648 func (r *resource) PinVersion(rcvID int) (bool, error) { 649 tx, err := r.conn.Begin() 650 if err != nil { 651 return false, err 652 } 653 defer Rollback(tx) 654 var pinnedThroughConfig bool 655 err = tx.QueryRow(` 656 SELECT EXISTS ( 657 SELECT 1 658 FROM resource_pins 659 WHERE resource_id = $1 660 AND config 661 )`, r.id).Scan(&pinnedThroughConfig) 662 if err != nil { 663 return false, err 664 } 665 666 if pinnedThroughConfig { 667 return false, ErrPinnedThroughConfig 668 } 669 670 results, err := tx.Exec(` 671 INSERT INTO resource_pins(resource_id, version, comment_text, config) 672 VALUES ($1, 673 ( SELECT rcv.version 674 FROM resource_config_versions rcv 675 WHERE rcv.id = $2 ), 676 '', false) 677 ON CONFLICT (resource_id) DO UPDATE SET version=EXCLUDED.version`, r.id, rcvID) 678 if err != nil { 679 if err == sql.ErrNoRows { 680 return false, nil 681 } 682 return false, err 683 } 684 685 rowsAffected, err := results.RowsAffected() 686 if err != nil { 687 return false, err 688 } 689 690 if rowsAffected != 1 { 691 return false, nil 692 } 693 694 err = requestScheduleForJobsUsingResource(tx, r.id) 695 if err != nil { 696 return false, err 697 } 698 699 err = tx.Commit() 700 if err != nil { 701 return false, err 702 } 703 704 return true, nil 705 } 706 707 func (r *resource) UnpinVersion() error { 708 tx, err := r.conn.Begin() 709 if err != nil { 710 return err 711 } 712 713 defer tx.Rollback() 714 715 results, err := psql.Delete("resource_pins"). 716 Where(sq.Eq{"resource_pins.resource_id": r.id}). 717 RunWith(tx). 718 Exec() 719 if err != nil { 720 return err 721 } 722 723 rowsAffected, err := results.RowsAffected() 724 if err != nil { 725 return err 726 } 727 728 if rowsAffected != 1 { 729 return NonOneRowAffectedError{rowsAffected} 730 } 731 732 err = requestScheduleForJobsUsingResource(tx, r.id) 733 if err != nil { 734 return err 735 } 736 737 err = tx.Commit() 738 if err != nil { 739 return err 740 } 741 742 return nil 743 } 744 745 func (r *resource) toggleVersion(rcvID int, enable bool) error { 746 tx, err := r.conn.Begin() 747 if err != nil { 748 return err 749 } 750 751 defer Rollback(tx) 752 753 var results sql.Result 754 if enable { 755 results, err = tx.Exec(` 756 DELETE FROM resource_disabled_versions 757 WHERE resource_id = $1 758 AND version_md5 = (SELECT version_md5 FROM resource_config_versions rcv WHERE rcv.id = $2) 759 `, r.id, rcvID) 760 } else { 761 results, err = tx.Exec(` 762 INSERT INTO resource_disabled_versions (resource_id, version_md5) 763 SELECT $1, rcv.version_md5 764 FROM resource_config_versions rcv 765 WHERE rcv.id = $2 766 `, r.id, rcvID) 767 } 768 if err != nil { 769 return err 770 } 771 772 rowsAffected, err := results.RowsAffected() 773 if err != nil { 774 return err 775 } 776 777 if rowsAffected != 1 { 778 return NonOneRowAffectedError{rowsAffected} 779 } 780 781 err = requestScheduleForJobsUsingResource(tx, r.id) 782 if err != nil { 783 return err 784 } 785 786 return tx.Commit() 787 } 788 789 func (r *resource) NotifyScan() error { 790 return r.conn.Bus().Notify(fmt.Sprintf("resource_scan_%d", r.id)) 791 } 792 793 func scanResource(r *resource, row scannable) error { 794 var ( 795 configBlob sql.NullString 796 nonce, rcID, rcScopeID, pinnedVersion, pinComment sql.NullString 797 lastCheckStartTime, lastCheckEndTime pq.NullTime 798 pinnedThroughConfig sql.NullBool 799 pipelineInstanceVars sql.NullString 800 ) 801 802 var build struct { 803 id sql.NullInt64 804 name sql.NullString 805 status sql.NullString 806 startTime pq.NullTime 807 endTime pq.NullTime 808 } 809 810 err := row.Scan(&r.id, &r.name, &r.type_, &configBlob, &lastCheckStartTime, &lastCheckEndTime, &r.pipelineID, &nonce, &rcID, &rcScopeID, &r.pipelineName, &pipelineInstanceVars, &r.teamID, &r.teamName, &pinnedVersion, &pinComment, &pinnedThroughConfig, &build.id, &build.name, &build.status, &build.startTime, &build.endTime) 811 if err != nil { 812 return err 813 } 814 815 r.lastCheckStartTime = lastCheckStartTime.Time 816 r.lastCheckEndTime = lastCheckEndTime.Time 817 818 es := r.conn.EncryptionStrategy() 819 820 var noncense *string 821 if nonce.Valid { 822 noncense = &nonce.String 823 } 824 825 if configBlob.Valid { 826 decryptedConfig, err := es.Decrypt(configBlob.String, noncense) 827 if err != nil { 828 return err 829 } 830 831 err = json.Unmarshal(decryptedConfig, &r.config) 832 if err != nil { 833 return err 834 } 835 } else { 836 r.config = atc.ResourceConfig{} 837 } 838 839 if pinnedVersion.Valid { 840 var version atc.Version 841 err = json.Unmarshal([]byte(pinnedVersion.String), &version) 842 if err != nil { 843 return err 844 } 845 846 if pinnedThroughConfig.Valid && pinnedThroughConfig.Bool { 847 r.configPinnedVersion = version 848 r.apiPinnedVersion = nil 849 } else { 850 r.configPinnedVersion = nil 851 r.apiPinnedVersion = version 852 } 853 } else { 854 r.apiPinnedVersion = nil 855 r.configPinnedVersion = nil 856 } 857 858 if pinComment.Valid { 859 r.pinComment = pinComment.String 860 } else { 861 r.pinComment = "" 862 } 863 864 if rcID.Valid { 865 r.resourceConfigID, err = strconv.Atoi(rcID.String) 866 if err != nil { 867 return err 868 } 869 } 870 871 if rcScopeID.Valid { 872 r.resourceConfigScopeID, err = strconv.Atoi(rcScopeID.String) 873 if err != nil { 874 return err 875 } 876 } 877 878 if pipelineInstanceVars.Valid { 879 err = json.Unmarshal([]byte(pipelineInstanceVars.String), &r.pipelineInstanceVars) 880 if err != nil { 881 return err 882 } 883 } 884 885 if build.id.Valid { 886 r.buildSummary = &atc.BuildSummary{ 887 ID: int(build.id.Int64), 888 Name: build.name.String, 889 890 Status: atc.BuildStatus(build.status.String), 891 892 TeamName: r.teamName, 893 894 PipelineID: r.pipelineID, 895 PipelineName: r.pipelineName, 896 PipelineInstanceVars: r.pipelineInstanceVars, 897 } 898 899 if build.startTime.Valid { 900 r.buildSummary.StartTime = build.startTime.Time.Unix() 901 } 902 903 if build.endTime.Valid { 904 r.buildSummary.EndTime = build.endTime.Time.Unix() 905 } 906 } 907 908 return nil 909 } 910 911 // The SELECT query orders the jobs for updating to prevent deadlocking. 912 // Updating multiple rows using a SELECT subquery does not preserve the same 913 // order for the updates, which can lead to deadlocking. 914 func requestScheduleForJobsUsingResource(tx Tx, resourceID int) error { 915 rows, err := psql.Select("DISTINCT job_id"). 916 From("job_inputs"). 917 Where(sq.Eq{ 918 "resource_id": resourceID, 919 }). 920 OrderBy("job_id DESC"). 921 RunWith(tx). 922 Query() 923 if err != nil { 924 return err 925 } 926 927 var jobs []int 928 for rows.Next() { 929 var jid int 930 err = rows.Scan(&jid) 931 if err != nil { 932 return err 933 } 934 935 jobs = append(jobs, jid) 936 } 937 938 for _, j := range jobs { 939 _, err := psql.Update("jobs"). 940 Set("schedule_requested", sq.Expr("now()")). 941 Where(sq.Eq{ 942 "id": j, 943 }). 944 RunWith(tx). 945 Exec() 946 if err != nil { 947 return err 948 } 949 } 950 951 return nil 952 }