1package db 2 3import ( 4 "errors" 5 "time" 6 7 sq "github.com/Masterminds/squirrel" 8 "github.com/concourse/concourse/atc" 9) 10 11var ErrContainerDisappeared = errors.New("container disappeared from db") 12 13type ContainerState string 14 15//go:generate counterfeiter . Container 16 17type Container interface { 18 ID() int 19 State() string 20 Handle() string 21 WorkerName() string 22 Metadata() ContainerMetadata 23} 24 25//go:generate counterfeiter . CreatingContainer 26 27type CreatingContainer interface { 28 Container 29 30 Created() (CreatedContainer, error) 31 Failed() (FailedContainer, error) 32} 33 34type creatingContainer struct { 35 id int 36 handle string 37 workerName string 38 metadata ContainerMetadata 39 conn Conn 40} 41 42func newCreatingContainer( 43 id int, 44 handle string, 45 workerName string, 46 metadata ContainerMetadata, 47 conn Conn, 48) *creatingContainer { 49 return &creatingContainer{ 50 id: id, 51 handle: handle, 52 workerName: workerName, 53 metadata: metadata, 54 conn: conn, 55 } 56} 57 58func (container *creatingContainer) ID() int { return container.id } 59func (container *creatingContainer) State() string { return atc.ContainerStateCreating } 60func (container *creatingContainer) Handle() string { return container.handle } 61func (container *creatingContainer) WorkerName() string { return container.workerName } 62func (container *creatingContainer) Metadata() ContainerMetadata { return container.metadata } 63 64func (container *creatingContainer) Created() (CreatedContainer, error) { 65 rows, err := psql.Update("containers"). 66 Set("state", atc.ContainerStateCreated). 67 Where(sq.And{ 68 sq.Eq{"id": container.id}, 69 sq.Or{ 70 sq.Eq{"state": string(atc.ContainerStateCreating)}, 71 sq.Eq{"state": string(atc.ContainerStateCreated)}, 72 }, 73 }). 74 RunWith(container.conn). 75 Exec() 76 if err != nil { 77 return nil, err 78 } 79 80 affected, err := rows.RowsAffected() 81 if err != nil { 82 return nil, err 83 } 84 85 if affected == 0 { 86 return nil, ErrContainerDisappeared 87 } 88 89 return newCreatedContainer( 90 container.id, 91 container.handle, 92 container.workerName, 93 container.metadata, 94 time.Time{}, 95 container.conn, 96 ), nil 97} 98 99func (container *creatingContainer) Failed() (FailedContainer, error) { 100 rows, err := psql.Update("containers"). 101 Set("state", atc.ContainerStateFailed). 102 Where(sq.And{ 103 sq.Eq{"id": container.id}, 104 sq.Or{ 105 sq.Eq{"state": string(atc.ContainerStateCreating)}, 106 sq.Eq{"state": string(atc.ContainerStateFailed)}, 107 }, 108 }). 109 RunWith(container.conn). 110 Exec() 111 if err != nil { 112 return nil, err 113 } 114 115 affected, err := rows.RowsAffected() 116 if err != nil { 117 return nil, err 118 } 119 120 if affected == 0 { 121 return nil, ErrContainerDisappeared 122 } 123 124 return newFailedContainer( 125 container.id, 126 container.handle, 127 container.workerName, 128 container.metadata, 129 container.conn, 130 ), nil 131} 132 133//go:generate counterfeiter . CreatedContainer 134 135type CreatedContainer interface { 136 Container 137 138 Destroying() (DestroyingContainer, error) 139 LastHijack() time.Time 140 UpdateLastHijack() error 141} 142 143type createdContainer struct { 144 id int 145 handle string 146 workerName string 147 metadata ContainerMetadata 148 149 lastHijack time.Time 150 151 conn Conn 152} 153 154func newCreatedContainer( 155 id int, 156 handle string, 157 workerName string, 158 metadata ContainerMetadata, 159 lastHijack time.Time, 160 conn Conn, 161) *createdContainer { 162 return &createdContainer{ 163 id: id, 164 handle: handle, 165 workerName: workerName, 166 metadata: metadata, 167 lastHijack: lastHijack, 168 conn: conn, 169 } 170} 171 172func (container *createdContainer) ID() int { return container.id } 173func (container *createdContainer) State() string { return atc.ContainerStateCreated } 174func (container *createdContainer) Handle() string { return container.handle } 175func (container *createdContainer) WorkerName() string { return container.workerName } 176func (container *createdContainer) Metadata() ContainerMetadata { return container.metadata } 177 178func (container *createdContainer) LastHijack() time.Time { return container.lastHijack } 179 180func (container *createdContainer) Destroying() (DestroyingContainer, error) { 181 182 rows, err := psql.Update("containers"). 183 Set("state", atc.ContainerStateDestroying). 184 Where(sq.And{ 185 sq.Eq{"id": container.id}, 186 sq.Or{ 187 sq.Eq{"state": string(atc.ContainerStateDestroying)}, 188 sq.Eq{"state": string(atc.ContainerStateCreated)}, 189 }, 190 }). 191 RunWith(container.conn). 192 Exec() 193 if err != nil { 194 return nil, err 195 } 196 197 affected, err := rows.RowsAffected() 198 if err != nil { 199 return nil, err 200 } 201 202 if affected == 0 { 203 return nil, ErrContainerDisappeared 204 } 205 206 return newDestroyingContainer( 207 container.id, 208 container.handle, 209 container.workerName, 210 container.metadata, 211 container.conn, 212 ), nil 213} 214 215func (container *createdContainer) UpdateLastHijack() error { 216 217 rows, err := psql.Update("containers"). 218 Set("last_hijack", sq.Expr("now()")). 219 Where(sq.Eq{ 220 "id": container.id, 221 "state": atc.ContainerStateCreated, 222 }). 223 RunWith(container.conn). 224 Exec() 225 if err != nil { 226 return err 227 } 228 229 affected, err := rows.RowsAffected() 230 if err != nil { 231 return err 232 } 233 234 if affected == 0 { 235 return ErrContainerDisappeared 236 } 237 238 return nil 239} 240 241//go:generate counterfeiter . DestroyingContainer 242 243type DestroyingContainer interface { 244 Container 245 246 Destroy() (bool, error) 247} 248 249type destroyingContainer struct { 250 id int 251 handle string 252 workerName string 253 metadata ContainerMetadata 254 255 conn Conn 256} 257 258func newDestroyingContainer( 259 id int, 260 handle string, 261 workerName string, 262 metadata ContainerMetadata, 263 conn Conn, 264) *destroyingContainer { 265 return &destroyingContainer{ 266 id: id, 267 handle: handle, 268 workerName: workerName, 269 metadata: metadata, 270 conn: conn, 271 } 272} 273 274func (container *destroyingContainer) ID() int { return container.id } 275func (container *destroyingContainer) State() string { return atc.ContainerStateDestroying } 276func (container *destroyingContainer) Handle() string { return container.handle } 277func (container *destroyingContainer) WorkerName() string { return container.workerName } 278func (container *destroyingContainer) Metadata() ContainerMetadata { return container.metadata } 279 280func (container *destroyingContainer) Destroy() (bool, error) { 281 rows, err := psql.Delete("containers"). 282 Where(sq.Eq{ 283 "id": container.id, 284 "state": atc.ContainerStateDestroying, 285 }). 286 RunWith(container.conn). 287 Exec() 288 if err != nil { 289 return false, err 290 } 291 292 affected, err := rows.RowsAffected() 293 if err != nil { 294 return false, err 295 } 296 297 if affected == 0 { 298 return false, ErrContainerDisappeared 299 } 300 301 return true, nil 302} 303 304//go:generate counterfeiter . FailedContainer 305 306type FailedContainer interface { 307 Container 308 309 Destroy() (bool, error) 310} 311 312type failedContainer struct { 313 id int 314 handle string 315 workerName string 316 metadata ContainerMetadata 317 conn Conn 318} 319 320func newFailedContainer( 321 id int, 322 handle string, 323 workerName string, 324 metadata ContainerMetadata, 325 conn Conn, 326) *failedContainer { 327 return &failedContainer{ 328 id: id, 329 handle: handle, 330 workerName: workerName, 331 metadata: metadata, 332 conn: conn, 333 } 334} 335 336func (container *failedContainer) ID() int { return container.id } 337func (container *failedContainer) State() string { return atc.ContainerStateFailed } 338func (container *failedContainer) Handle() string { return container.handle } 339func (container *failedContainer) WorkerName() string { return container.workerName } 340func (container *failedContainer) Metadata() ContainerMetadata { return container.metadata } 341 342func (container *failedContainer) Destroy() (bool, error) { 343 rows, err := psql.Delete("containers"). 344 Where(sq.Eq{ 345 "id": container.id, 346 "state": atc.ContainerStateFailed, 347 }). 348 RunWith(container.conn). 349 Exec() 350 if err != nil { 351 return false, err 352 } 353 354 affected, err := rows.RowsAffected() 355 if err != nil { 356 return false, err 357 } 358 359 if affected == 0 { 360 return false, ErrContainerDisappeared 361 } 362 363 return true, nil 364} 365