1package db 2 3import ( 4 "database/sql" 5 "encoding/json" 6 7 sq "github.com/Masterminds/squirrel" 8 "github.com/concourse/concourse/atc" 9 "github.com/concourse/concourse/atc/db/lock" 10) 11 12//go:generate counterfeiter . ResourceCacheFactory 13 14type ResourceCacheFactory interface { 15 FindOrCreateResourceCache( 16 resourceCacheUser ResourceCacheUser, 17 resourceTypeName string, 18 version atc.Version, 19 source atc.Source, 20 params atc.Params, 21 resourceTypes atc.VersionedResourceTypes, 22 ) (UsedResourceCache, error) 23 24 // changing resource cache to interface to allow updates on object is not feasible. 25 // Since we need to pass it recursively in ResourceConfig. 26 // Also, metadata will be available to us before we create resource cache so this 27 // method can be removed at that point. See https://github.com/concourse/concourse/issues/534 28 UpdateResourceCacheMetadata(UsedResourceCache, []atc.MetadataField) error 29 ResourceCacheMetadata(UsedResourceCache) (ResourceConfigMetadataFields, error) 30 31 FindResourceCacheByID(id int) (UsedResourceCache, bool, error) 32} 33 34type resourceCacheFactory struct { 35 conn Conn 36 lockFactory lock.LockFactory 37} 38 39func NewResourceCacheFactory(conn Conn, lockFactory lock.LockFactory) ResourceCacheFactory { 40 return &resourceCacheFactory{ 41 conn: conn, 42 lockFactory: lockFactory, 43 } 44} 45 46func (f *resourceCacheFactory) FindOrCreateResourceCache( 47 resourceCacheUser ResourceCacheUser, 48 resourceTypeName string, 49 version atc.Version, 50 source atc.Source, 51 params atc.Params, 52 resourceTypes atc.VersionedResourceTypes, 53) (UsedResourceCache, error) { 54 resourceConfigDescriptor, err := constructResourceConfigDescriptor(resourceTypeName, source, resourceTypes) 55 if err != nil { 56 return nil, err 57 } 58 59 resourceCache := ResourceCacheDescriptor{ 60 ResourceConfigDescriptor: resourceConfigDescriptor, 61 Version: version, 62 Params: params, 63 } 64 65 tx, err := f.conn.Begin() 66 if err != nil { 67 return nil, err 68 } 69 70 defer Rollback(tx) 71 72 usedResourceCache, err := resourceCache.findOrCreate(tx, f.lockFactory, f.conn) 73 if err != nil { 74 return nil, err 75 } 76 77 err = resourceCache.use(tx, usedResourceCache, resourceCacheUser) 78 if err != nil { 79 return nil, err 80 } 81 82 err = tx.Commit() 83 if err != nil { 84 return nil, err 85 } 86 87 return usedResourceCache, nil 88} 89 90func (f *resourceCacheFactory) UpdateResourceCacheMetadata(resourceCache UsedResourceCache, metadata []atc.MetadataField) error { 91 metadataJSON, err := json.Marshal(metadata) 92 if err != nil { 93 return err 94 } 95 _, err = psql.Update("resource_caches"). 96 Set("metadata", metadataJSON). 97 Where(sq.Eq{"id": resourceCache.ID()}). 98 RunWith(f.conn). 99 Exec() 100 return err 101} 102 103func (f *resourceCacheFactory) ResourceCacheMetadata(resourceCache UsedResourceCache) (ResourceConfigMetadataFields, error) { 104 var metadataJSON sql.NullString 105 err := psql.Select("metadata"). 106 From("resource_caches"). 107 Where(sq.Eq{"id": resourceCache.ID()}). 108 RunWith(f.conn). 109 QueryRow(). 110 Scan(&metadataJSON) 111 if err != nil { 112 return nil, err 113 } 114 115 var metadata []ResourceConfigMetadataField 116 if metadataJSON.Valid { 117 err = json.Unmarshal([]byte(metadataJSON.String), &metadata) 118 if err != nil { 119 return nil, err 120 } 121 } 122 123 return metadata, nil 124} 125 126func (f *resourceCacheFactory) FindResourceCacheByID(id int) (UsedResourceCache, bool, error) { 127 tx, err := f.conn.Begin() 128 if err != nil { 129 return nil, false, err 130 } 131 132 defer Rollback(tx) 133 134 return findResourceCacheByID(tx, id, f.lockFactory, f.conn) 135} 136 137func findResourceCacheByID(tx Tx, resourceCacheID int, lock lock.LockFactory, conn Conn) (UsedResourceCache, bool, error) { 138 var rcID int 139 var versionBytes string 140 141 err := psql.Select("resource_config_id", "version"). 142 From("resource_caches"). 143 Where(sq.Eq{"id": resourceCacheID}). 144 RunWith(tx). 145 QueryRow(). 146 Scan(&rcID, &versionBytes) 147 148 if err != nil { 149 if err == sql.ErrNoRows { 150 return nil, false, nil 151 } 152 return nil, false, err 153 } 154 155 var version atc.Version 156 err = json.Unmarshal([]byte(versionBytes), &version) 157 if err != nil { 158 return nil, false, err 159 } 160 161 rc, found, err := findResourceConfigByID(tx, rcID, lock, conn) 162 if err != nil { 163 return nil, false, err 164 } 165 166 if !found { 167 return nil, false, nil 168 } 169 170 usedResourceCache := &usedResourceCache{ 171 id: resourceCacheID, 172 version: version, 173 resourceConfig: rc, 174 lockFactory: lock, 175 conn: conn, 176 } 177 178 return usedResourceCache, true, nil 179} 180