github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/container_repository.go (about) 1 package db 2 3 import ( 4 "fmt" 5 "time" 6 7 sq "github.com/Masterminds/squirrel" 8 "github.com/pf-qiu/concourse/v6/atc" 9 "github.com/lib/pq" 10 ) 11 12 //go:generate counterfeiter . ContainerRepository 13 14 type ContainerRepository interface { 15 FindOrphanedContainers() ([]CreatingContainer, []CreatedContainer, []DestroyingContainer, error) 16 DestroyFailedContainers() (int, error) 17 FindDestroyingContainers(workerName string) ([]string, error) 18 RemoveDestroyingContainers(workerName string, currentHandles []string) (int, error) 19 UpdateContainersMissingSince(workerName string, handles []string) error 20 RemoveMissingContainers(time.Duration) (int, error) 21 DestroyUnknownContainers(workerName string, reportedHandles []string) (int, error) 22 } 23 24 type containerRepository struct { 25 conn Conn 26 } 27 28 func NewContainerRepository(conn Conn) ContainerRepository { 29 return &containerRepository{ 30 conn: conn, 31 } 32 } 33 34 func diff(a, b []string) (diff []string) { 35 m := make(map[string]bool) 36 37 for _, item := range b { 38 m[item] = true 39 } 40 41 for _, item := range a { 42 if _, ok := m[item]; !ok { 43 diff = append(diff, item) 44 } 45 } 46 47 return 48 } 49 50 func (repository *containerRepository) queryContainerHandles(tx Tx, cond sq.Eq) ([]string, error) { 51 query, args, err := psql.Select("handle").From("containers").Where(cond).ToSql() 52 if err != nil { 53 return nil, err 54 } 55 56 rows, err := tx.Query(query, args...) 57 if err != nil { 58 return nil, err 59 } 60 61 defer Close(rows) 62 63 handles := []string{} 64 65 for rows.Next() { 66 var handle = "handle" 67 columns := []interface{}{&handle} 68 69 err = rows.Scan(columns...) 70 if err != nil { 71 return nil, err 72 } 73 handles = append(handles, handle) 74 } 75 76 return handles, nil 77 } 78 79 func (repository *containerRepository) UpdateContainersMissingSince(workerName string, reportedHandles []string) error { 80 // clear out missing_since for reported containers 81 query, args, err := psql.Update("containers"). 82 Set("missing_since", nil). 83 Where( 84 sq.And{ 85 sq.NotEq{ 86 "missing_since": nil, 87 }, 88 sq.Eq{ 89 "handle": reportedHandles, 90 }, 91 }, 92 ).ToSql() 93 if err != nil { 94 return err 95 } 96 97 tx, err := repository.conn.Begin() 98 if err != nil { 99 return err 100 } 101 102 defer Rollback(tx) 103 104 rows, err := tx.Query(query, args...) 105 if err != nil { 106 return err 107 } 108 109 Close(rows) 110 111 dbHandles, err := repository.queryContainerHandles(tx, sq.Eq{ 112 "worker_name": workerName, 113 "missing_since": nil, 114 }) 115 if err != nil { 116 return err 117 } 118 119 handles := diff(dbHandles, reportedHandles) 120 121 query, args, err = psql.Update("containers"). 122 Set("missing_since", sq.Expr("now()")). 123 Where(sq.And{ 124 sq.Eq{"handle": handles}, 125 sq.NotEq{"state": atc.ContainerStateCreating}, 126 }).ToSql() 127 if err != nil { 128 return err 129 } 130 131 _, err = tx.Exec(query, args...) 132 if err != nil { 133 return err 134 } 135 136 err = tx.Commit() 137 if err != nil { 138 return err 139 } 140 141 return nil 142 } 143 144 func (repository *containerRepository) FindDestroyingContainers(workerName string) ([]string, error) { 145 tx, err := repository.conn.Begin() 146 if err != nil { 147 return nil, err 148 } 149 150 defer Rollback(tx) 151 152 destroyingContainers, err := repository.queryContainerHandles( 153 tx, 154 sq.Eq{ 155 "state": atc.ContainerStateDestroying, 156 "worker_name": workerName, 157 }, 158 ) 159 160 err = tx.Commit() 161 if err != nil { 162 return nil, err 163 } 164 165 return destroyingContainers, err 166 } 167 168 func (repository *containerRepository) RemoveMissingContainers(gracePeriod time.Duration) (int, error) { 169 result, err := psql.Delete("containers c USING workers w"). 170 Where(sq.Expr("c.worker_name = w.name")). 171 Where( 172 sq.And{ 173 sq.Expr(fmt.Sprintf("c.state='%s'", atc.ContainerStateCreated)), 174 sq.Expr(fmt.Sprintf("w.state!='%s'", WorkerStateStalled)), 175 sq.Expr(fmt.Sprintf("NOW() - missing_since > '%s'", fmt.Sprintf("%.0f seconds", gracePeriod.Seconds()))), 176 }, 177 ).RunWith(repository.conn). 178 Exec() 179 180 if err != nil { 181 return 0, err 182 } 183 184 affected, err := result.RowsAffected() 185 if err != nil { 186 return 0, err 187 } 188 189 return int(affected), nil 190 } 191 192 func (repository *containerRepository) RemoveDestroyingContainers(workerName string, handlesToIgnore []string) (int, error) { 193 rows, err := psql.Delete("containers"). 194 Where( 195 sq.And{ 196 sq.Eq{ 197 "worker_name": workerName, 198 }, 199 sq.NotEq{ 200 "handle": handlesToIgnore, 201 }, 202 sq.Eq{ 203 "state": atc.ContainerStateDestroying, 204 }, 205 }, 206 ).RunWith(repository.conn). 207 Exec() 208 209 if err != nil { 210 return 0, err 211 } 212 213 affected, err := rows.RowsAffected() 214 if err != nil { 215 return 0, err 216 } 217 218 return int(affected), nil 219 } 220 221 func (repository *containerRepository) FindOrphanedContainers() ([]CreatingContainer, []CreatedContainer, []DestroyingContainer, error) { 222 query, args, err := selectContainers("c"). 223 LeftJoin("builds b ON b.id = c.build_id"). 224 LeftJoin("containers icc ON icc.id = c.image_check_container_id"). 225 LeftJoin("containers igc ON igc.id = c.image_get_container_id"). 226 Where(sq.Or{ 227 sq.Eq{ 228 "c.build_id": nil, 229 "c.image_check_container_id": nil, 230 "c.image_get_container_id": nil, 231 "c.resource_config_check_session_id": nil, 232 }, 233 sq.And{ 234 sq.NotEq{"c.build_id": nil}, 235 sq.Eq{"b.interceptible": false}, 236 }, 237 sq.And{ 238 sq.NotEq{"c.image_check_container_id": nil}, 239 sq.NotEq{"icc.state": atc.ContainerStateCreating}, 240 }, 241 sq.And{ 242 sq.NotEq{"c.image_get_container_id": nil}, 243 sq.NotEq{"igc.state": atc.ContainerStateCreating}, 244 }, 245 }). 246 ToSql() 247 if err != nil { 248 return nil, nil, nil, err 249 } 250 251 rows, err := repository.conn.Query(query, args...) 252 if err != nil { 253 return nil, nil, nil, err 254 } 255 256 defer Close(rows) 257 258 creatingContainers := []CreatingContainer{} 259 createdContainers := []CreatedContainer{} 260 destroyingContainers := []DestroyingContainer{} 261 262 var ( 263 creatingContainer CreatingContainer 264 createdContainer CreatedContainer 265 destroyingContainer DestroyingContainer 266 ) 267 268 for rows.Next() { 269 creatingContainer, createdContainer, destroyingContainer, _, err = scanContainer(rows, repository.conn) 270 if err != nil { 271 return nil, nil, nil, err 272 } 273 274 if creatingContainer != nil { 275 creatingContainers = append(creatingContainers, creatingContainer) 276 } 277 278 if createdContainer != nil { 279 createdContainers = append(createdContainers, createdContainer) 280 } 281 282 if destroyingContainer != nil { 283 destroyingContainers = append(destroyingContainers, destroyingContainer) 284 } 285 } 286 287 err = rows.Err() 288 if err != nil { 289 return nil, nil, nil, err 290 } 291 292 return creatingContainers, createdContainers, destroyingContainers, nil 293 } 294 295 func selectContainers(asOptional ...string) sq.SelectBuilder { 296 columns := []string{"id", "handle", "worker_name", "last_hijack", "state"} 297 columns = append(columns, containerMetadataColumns...) 298 299 table := "containers" 300 if len(asOptional) > 0 { 301 as := asOptional[0] 302 for i, c := range columns { 303 columns[i] = as + "." + c 304 } 305 306 table += " " + as 307 } 308 309 return psql.Select(columns...).From(table) 310 } 311 312 func scanContainer(row sq.RowScanner, conn Conn) (CreatingContainer, CreatedContainer, DestroyingContainer, FailedContainer, error) { 313 var ( 314 id int 315 handle string 316 workerName string 317 lastHijack pq.NullTime 318 state string 319 320 metadata ContainerMetadata 321 ) 322 323 columns := []interface{}{&id, &handle, &workerName, &lastHijack, &state} 324 columns = append(columns, metadata.ScanTargets()...) 325 326 err := row.Scan(columns...) 327 if err != nil { 328 return nil, nil, nil, nil, err 329 } 330 331 switch state { 332 case atc.ContainerStateCreating: 333 return newCreatingContainer( 334 id, 335 handle, 336 workerName, 337 metadata, 338 conn, 339 ), nil, nil, nil, nil 340 case atc.ContainerStateCreated: 341 return nil, newCreatedContainer( 342 id, 343 handle, 344 workerName, 345 metadata, 346 lastHijack.Time, 347 conn, 348 ), nil, nil, nil 349 case atc.ContainerStateDestroying: 350 return nil, nil, newDestroyingContainer( 351 id, 352 handle, 353 workerName, 354 metadata, 355 conn, 356 ), nil, nil 357 case atc.ContainerStateFailed: 358 return nil, nil, nil, newFailedContainer( 359 id, 360 handle, 361 workerName, 362 metadata, 363 conn, 364 ), nil 365 } 366 367 return nil, nil, nil, nil, nil 368 } 369 370 func (repository *containerRepository) DestroyFailedContainers() (int, error) { 371 result, err := psql.Update("containers"). 372 Set("state", atc.ContainerStateDestroying). 373 Where(sq.Eq{"state": string(atc.ContainerStateFailed)}). 374 RunWith(repository.conn). 375 Exec() 376 377 if err != nil { 378 return 0, err 379 } 380 381 affected, err := result.RowsAffected() 382 if err != nil { 383 return 0, err 384 } 385 386 return int(affected), nil 387 } 388 389 func (repository *containerRepository) DestroyUnknownContainers(workerName string, reportedHandles []string) (int, error) { 390 tx, err := repository.conn.Begin() 391 if err != nil { 392 return 0, err 393 } 394 395 defer Rollback(tx) 396 397 dbHandles, err := repository.queryContainerHandles(tx, sq.Eq{ 398 "worker_name": workerName, 399 }) 400 if err != nil { 401 return 0, err 402 } 403 404 unknownHandles := diff(reportedHandles, dbHandles) 405 406 if len(unknownHandles) == 0 { 407 return 0, nil 408 } 409 410 insertBuilder := psql.Insert("containers").Columns( 411 "handle", 412 "worker_name", 413 "state", 414 ) 415 for _, unknownHandle := range unknownHandles { 416 insertBuilder = insertBuilder.Values( 417 unknownHandle, 418 workerName, 419 atc.ContainerStateDestroying, 420 ) 421 } 422 _, err = insertBuilder.RunWith(tx).Exec() 423 if err != nil { 424 return 0, err 425 } 426 427 err = tx.Commit() 428 if err != nil { 429 return 0, err 430 } 431 432 return len(unknownHandles), nil 433 }