github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/metadata/gc_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package metadata 18 19 import ( 20 "context" 21 "io" 22 "io/ioutil" 23 "math/rand" 24 "os" 25 "path/filepath" 26 "sort" 27 "testing" 28 "time" 29 30 "github.com/containerd/containerd/gc" 31 "github.com/containerd/containerd/metadata/boltutil" 32 digest "github.com/opencontainers/go-digest" 33 bolt "go.etcd.io/bbolt" 34 ) 35 36 func TestResourceMax(t *testing.T) { 37 if ResourceContent != resourceContentFlat&gc.ResourceMax { 38 t.Fatalf("Invalid flat content type: %d (max %d)", resourceContentFlat, gc.ResourceMax) 39 } 40 if ResourceSnapshot != resourceSnapshotFlat&gc.ResourceMax { 41 t.Fatalf("Invalid flat snapshot type: %d (max %d)", resourceSnapshotFlat, gc.ResourceMax) 42 } 43 } 44 45 func TestGCRoots(t *testing.T) { 46 db, cleanup, err := newDatabase() 47 if err != nil { 48 t.Fatal(err) 49 } 50 defer cleanup() 51 52 alters := []alterFunc{ 53 addImage("ns1", "image1", dgst(1), nil), 54 addImage("ns1", "image2", dgst(2), labelmap(string(labelGCSnapRef)+"overlay", "sn2")), 55 addImage("ns2", "image3", dgst(10), labelmap(string(labelGCContentRef), dgst(11).String())), 56 addContainer("ns1", "container1", "overlay", "sn4", nil), 57 addContainer("ns1", "container2", "overlay", "sn5", labelmap(string(labelGCSnapRef)+"overlay", "sn6")), 58 addContainer("ns1", "container3", "overlay", "sn7", labelmap( 59 string(labelGCSnapRef)+"overlay/anything-1", "sn8", 60 string(labelGCSnapRef)+"overlay/anything-2", "sn9", 61 string(labelGCContentRef), dgst(7).String())), 62 addContainer("ns1", "container4", "", "", labelmap( 63 string(labelGCContentRef)+".0", dgst(8).String(), 64 string(labelGCContentRef)+".1", dgst(9).String())), 65 addContent("ns1", dgst(1), nil), 66 addContent("ns1", dgst(2), nil), 67 addContent("ns1", dgst(3), nil), 68 addContent("ns2", dgst(1), nil), 69 addContent("ns2", dgst(2), labelmap(string(labelGCRoot), "always")), 70 addContent("ns2", dgst(8), nil), 71 addContent("ns2", dgst(9), nil), 72 addIngest("ns1", "ingest-1", "", nil), // will be seen as expired 73 addIngest("ns1", "ingest-2", "", timeIn(0)), // expired 74 addIngest("ns1", "ingest-3", "", timeIn(time.Hour)), 75 addIngest("ns2", "ingest-4", "", nil), 76 addIngest("ns2", "ingest-5", dgst(8), nil), 77 addIngest("ns2", "ingest-6", "", nil), // added to expired lease 78 addIngest("ns2", "ingest-7", dgst(9), nil), // added to expired lease 79 addSnapshot("ns1", "overlay", "sn1", "", nil), 80 addSnapshot("ns1", "overlay", "sn2", "", nil), 81 addSnapshot("ns1", "overlay", "sn3", "", labelmap(string(labelGCRoot), "always")), 82 addSnapshot("ns1", "overlay", "sn4", "", nil), 83 addSnapshot("ns1", "overlay", "sn5", "", nil), 84 addSnapshot("ns1", "overlay", "sn6", "", nil), 85 addSnapshot("ns1", "overlay", "sn7", "", nil), 86 addSnapshot("ns1", "overlay", "sn8", "", nil), 87 addSnapshot("ns1", "overlay", "sn9", "", nil), 88 addLeaseSnapshot("ns2", "l1", "overlay", "sn5"), 89 addLeaseSnapshot("ns2", "l2", "overlay", "sn6"), 90 addLeaseContent("ns2", "l1", dgst(4)), 91 addLeaseContent("ns2", "l2", dgst(5)), 92 addLease("ns2", "l3", labelmap(string(labelGCExpire), time.Now().Add(time.Hour).Format(time.RFC3339))), 93 addLeaseContent("ns2", "l3", dgst(6)), 94 addLeaseSnapshot("ns2", "l3", "overlay", "sn7"), 95 addLeaseIngest("ns2", "l3", "ingest-4"), 96 addLeaseIngest("ns2", "l3", "ingest-5"), 97 addLease("ns2", "l4", labelmap(string(labelGCExpire), time.Now().Format(time.RFC3339))), 98 addLeaseContent("ns2", "l4", dgst(7)), 99 addLeaseSnapshot("ns2", "l4", "overlay", "sn8"), 100 addLeaseIngest("ns2", "l4", "ingest-6"), 101 addLeaseIngest("ns2", "l4", "ingest-7"), 102 103 addLease("ns3", "l1", labelmap(string(labelGCFlat), time.Now().Add(time.Hour).Format(time.RFC3339))), 104 addLeaseContent("ns3", "l1", dgst(1)), 105 addLeaseSnapshot("ns3", "l1", "overlay", "sn1"), 106 addLeaseIngest("ns3", "l1", "ingest-1"), 107 } 108 109 expected := []gc.Node{ 110 gcnode(ResourceContent, "ns1", dgst(1).String()), 111 gcnode(ResourceContent, "ns1", dgst(2).String()), 112 gcnode(ResourceContent, "ns1", dgst(7).String()), 113 gcnode(ResourceContent, "ns1", dgst(8).String()), 114 gcnode(ResourceContent, "ns1", dgst(9).String()), 115 gcnode(ResourceContent, "ns2", dgst(2).String()), 116 gcnode(ResourceContent, "ns2", dgst(4).String()), 117 gcnode(ResourceContent, "ns2", dgst(5).String()), 118 gcnode(ResourceContent, "ns2", dgst(6).String()), 119 gcnode(ResourceContent, "ns2", dgst(10).String()), 120 gcnode(ResourceContent, "ns2", dgst(11).String()), 121 gcnode(ResourceSnapshot, "ns1", "overlay/sn2"), 122 gcnode(ResourceSnapshot, "ns1", "overlay/sn3"), 123 gcnode(ResourceSnapshot, "ns1", "overlay/sn4"), 124 gcnode(ResourceSnapshot, "ns1", "overlay/sn5"), 125 gcnode(ResourceSnapshot, "ns1", "overlay/sn6"), 126 gcnode(ResourceSnapshot, "ns1", "overlay/sn7"), 127 gcnode(ResourceSnapshot, "ns1", "overlay/sn8"), 128 gcnode(ResourceSnapshot, "ns1", "overlay/sn9"), 129 gcnode(ResourceSnapshot, "ns2", "overlay/sn5"), 130 gcnode(ResourceSnapshot, "ns2", "overlay/sn6"), 131 gcnode(ResourceSnapshot, "ns2", "overlay/sn7"), 132 gcnode(ResourceLease, "ns2", "l1"), 133 gcnode(ResourceLease, "ns2", "l2"), 134 gcnode(ResourceLease, "ns2", "l3"), 135 gcnode(ResourceIngest, "ns1", "ingest-3"), 136 gcnode(ResourceIngest, "ns2", "ingest-4"), 137 gcnode(ResourceIngest, "ns2", "ingest-5"), 138 gcnode(ResourceLease, "ns3", "l1"), 139 gcnode(ResourceIngest, "ns3", "ingest-1"), 140 gcnode(resourceContentFlat, "ns3", dgst(1).String()), 141 gcnode(resourceSnapshotFlat, "ns3", "overlay/sn1"), 142 } 143 144 if err := db.Update(func(tx *bolt.Tx) error { 145 v1bkt, err := tx.CreateBucketIfNotExists(bucketKeyVersion) 146 if err != nil { 147 return err 148 } 149 for _, alter := range alters { 150 if err := alter(v1bkt); err != nil { 151 return err 152 } 153 } 154 return nil 155 }); err != nil { 156 t.Fatalf("Update failed: %+v", err) 157 } 158 159 ctx := context.Background() 160 161 checkNodeC(ctx, t, db, expected, func(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { 162 return scanRoots(ctx, tx, nc) 163 }) 164 } 165 166 func TestGCRemove(t *testing.T) { 167 db, cleanup, err := newDatabase() 168 if err != nil { 169 t.Fatal(err) 170 } 171 defer cleanup() 172 173 alters := []alterFunc{ 174 addImage("ns1", "image1", dgst(1), nil), 175 addImage("ns1", "image2", dgst(2), labelmap(string(labelGCSnapRef)+"overlay", "sn2")), 176 addContainer("ns1", "container1", "overlay", "sn4", nil), 177 addContent("ns1", dgst(1), nil), 178 addContent("ns1", dgst(2), nil), 179 addContent("ns1", dgst(3), nil), 180 addContent("ns2", dgst(1), nil), 181 addContent("ns2", dgst(2), labelmap(string(labelGCRoot), "always")), 182 addIngest("ns1", "ingest-1", "", nil), 183 addIngest("ns2", "ingest-2", "", timeIn(0)), 184 addSnapshot("ns1", "overlay", "sn1", "", nil), 185 addSnapshot("ns1", "overlay", "sn2", "", nil), 186 addSnapshot("ns1", "overlay", "sn3", "", labelmap(string(labelGCRoot), "always")), 187 addSnapshot("ns1", "overlay", "sn4", "", nil), 188 addSnapshot("ns2", "overlay", "sn1", "", nil), 189 addLease("ns1", "l1", labelmap(string(labelGCExpire), time.Now().Add(time.Hour).Format(time.RFC3339))), 190 addLease("ns2", "l2", labelmap(string(labelGCExpire), time.Now().Format(time.RFC3339))), 191 } 192 193 all := []gc.Node{ 194 gcnode(ResourceContent, "ns1", dgst(1).String()), 195 gcnode(ResourceContent, "ns1", dgst(2).String()), 196 gcnode(ResourceContent, "ns1", dgst(3).String()), 197 gcnode(ResourceContent, "ns2", dgst(1).String()), 198 gcnode(ResourceContent, "ns2", dgst(2).String()), 199 gcnode(ResourceSnapshot, "ns1", "overlay/sn1"), 200 gcnode(ResourceSnapshot, "ns1", "overlay/sn2"), 201 gcnode(ResourceSnapshot, "ns1", "overlay/sn3"), 202 gcnode(ResourceSnapshot, "ns1", "overlay/sn4"), 203 gcnode(ResourceSnapshot, "ns2", "overlay/sn1"), 204 gcnode(ResourceLease, "ns1", "l1"), 205 gcnode(ResourceLease, "ns2", "l2"), 206 gcnode(ResourceIngest, "ns1", "ingest-1"), 207 gcnode(ResourceIngest, "ns2", "ingest-2"), 208 } 209 210 var deleted, remaining []gc.Node 211 for i, n := range all { 212 if i%2 == 0 { 213 deleted = append(deleted, n) 214 } else { 215 remaining = append(remaining, n) 216 } 217 } 218 219 if err := db.Update(func(tx *bolt.Tx) error { 220 v1bkt, err := tx.CreateBucketIfNotExists(bucketKeyVersion) 221 if err != nil { 222 return err 223 } 224 for _, alter := range alters { 225 if err := alter(v1bkt); err != nil { 226 return err 227 } 228 } 229 return nil 230 }); err != nil { 231 t.Fatalf("Update failed: %+v", err) 232 } 233 234 ctx := context.Background() 235 236 checkNodes(ctx, t, db, all, func(ctx context.Context, tx *bolt.Tx, fn func(context.Context, gc.Node) error) error { 237 return scanAll(ctx, tx, fn) 238 }) 239 if t.Failed() { 240 t.Fatal("Scan all failed") 241 } 242 243 if err := db.Update(func(tx *bolt.Tx) error { 244 for _, n := range deleted { 245 if err := remove(ctx, tx, n); err != nil { 246 return err 247 } 248 } 249 return nil 250 }); err != nil { 251 t.Fatalf("Update failed: %+v", err) 252 } 253 254 checkNodes(ctx, t, db, remaining, func(ctx context.Context, tx *bolt.Tx, fn func(context.Context, gc.Node) error) error { 255 return scanAll(ctx, tx, fn) 256 }) 257 } 258 259 func TestGCRefs(t *testing.T) { 260 db, cleanup, err := newDatabase() 261 if err != nil { 262 t.Fatal(err) 263 } 264 defer cleanup() 265 266 alters := []alterFunc{ 267 addContent("ns1", dgst(1), nil), 268 addContent("ns1", dgst(2), nil), 269 addContent("ns1", dgst(3), nil), 270 addContent("ns1", dgst(4), labelmap(string(labelGCContentRef), dgst(1).String())), 271 addContent("ns1", dgst(5), labelmap(string(labelGCContentRef)+".anything-1", dgst(2).String(), string(labelGCContentRef)+".anything-2", dgst(3).String())), 272 addContent("ns1", dgst(6), labelmap(string(labelGCContentRef)+"bad", dgst(1).String())), 273 addContent("ns1", dgst(7), labelmap(string(labelGCContentRef)+"/anything-1", dgst(2).String(), string(labelGCContentRef)+"/anything-2", dgst(3).String())), 274 addContent("ns2", dgst(1), nil), 275 addContent("ns2", dgst(2), nil), 276 addIngest("ns1", "ingest-1", "", nil), 277 addIngest("ns2", "ingest-2", dgst(8), nil), 278 addSnapshot("ns1", "overlay", "sn1", "", nil), 279 addSnapshot("ns1", "overlay", "sn2", "sn1", nil), 280 addSnapshot("ns1", "overlay", "sn3", "sn2", nil), 281 addSnapshot("ns1", "overlay", "sn4", "", labelmap(string(labelGCSnapRef)+"btrfs", "sn1", string(labelGCSnapRef)+"overlay", "sn1")), 282 addSnapshot("ns1", "overlay", "sn5", "", labelmap(string(labelGCSnapRef)+"overlay/anything-1", "sn1", string(labelGCSnapRef)+"overlay/anything-2", "sn2")), 283 addSnapshot("ns1", "btrfs", "sn1", "", nil), 284 addSnapshot("ns2", "overlay", "sn1", "", nil), 285 addSnapshot("ns2", "overlay", "sn2", "sn1", nil), 286 addSnapshot("ns2", "overlay", "sn3", "", labelmap( 287 string(labelGCContentRef), dgst(1).String(), 288 string(labelGCContentRef)+".keep-me", dgst(6).String())), 289 290 // Test flat references don't follow label references 291 addContent("ns3", dgst(1), nil), 292 addContent("ns3", dgst(2), labelmap(string(labelGCContentRef)+".0", dgst(1).String())), 293 294 addSnapshot("ns3", "overlay", "sn1", "", nil), 295 addSnapshot("ns3", "overlay", "sn2", "sn1", nil), 296 addSnapshot("ns3", "overlay", "sn3", "", labelmap(string(labelGCSnapRef)+"btrfs", "sn1", string(labelGCSnapRef)+"overlay", "sn1")), 297 } 298 299 refs := map[gc.Node][]gc.Node{ 300 gcnode(ResourceContent, "ns1", dgst(1).String()): nil, 301 gcnode(ResourceContent, "ns1", dgst(2).String()): nil, 302 gcnode(ResourceContent, "ns1", dgst(3).String()): nil, 303 gcnode(ResourceContent, "ns1", dgst(4).String()): { 304 gcnode(ResourceContent, "ns1", dgst(1).String()), 305 }, 306 gcnode(ResourceContent, "ns1", dgst(5).String()): { 307 gcnode(ResourceContent, "ns1", dgst(2).String()), 308 gcnode(ResourceContent, "ns1", dgst(3).String()), 309 }, 310 gcnode(ResourceContent, "ns1", dgst(6).String()): nil, 311 gcnode(ResourceContent, "ns1", dgst(7).String()): { 312 gcnode(ResourceContent, "ns1", dgst(2).String()), 313 gcnode(ResourceContent, "ns1", dgst(3).String()), 314 }, 315 gcnode(ResourceContent, "ns2", dgst(1).String()): nil, 316 gcnode(ResourceContent, "ns2", dgst(2).String()): nil, 317 gcnode(ResourceSnapshot, "ns1", "overlay/sn1"): nil, 318 gcnode(ResourceSnapshot, "ns1", "overlay/sn2"): { 319 gcnode(ResourceSnapshot, "ns1", "overlay/sn1"), 320 }, 321 gcnode(ResourceSnapshot, "ns1", "overlay/sn3"): { 322 gcnode(ResourceSnapshot, "ns1", "overlay/sn2"), 323 }, 324 gcnode(ResourceSnapshot, "ns1", "overlay/sn4"): { 325 gcnode(ResourceSnapshot, "ns1", "btrfs/sn1"), 326 gcnode(ResourceSnapshot, "ns1", "overlay/sn1"), 327 }, 328 gcnode(ResourceSnapshot, "ns1", "overlay/sn5"): { 329 gcnode(ResourceSnapshot, "ns1", "overlay/sn1"), 330 gcnode(ResourceSnapshot, "ns1", "overlay/sn2"), 331 }, 332 gcnode(ResourceSnapshot, "ns1", "btrfs/sn1"): nil, 333 gcnode(ResourceSnapshot, "ns2", "overlay/sn1"): nil, 334 gcnode(ResourceSnapshot, "ns2", "overlay/sn2"): { 335 gcnode(ResourceSnapshot, "ns2", "overlay/sn1"), 336 }, 337 gcnode(ResourceSnapshot, "ns2", "overlay/sn3"): { 338 gcnode(ResourceContent, "ns2", dgst(1).String()), 339 gcnode(ResourceContent, "ns2", dgst(6).String()), 340 }, 341 gcnode(ResourceIngest, "ns1", "ingest-1"): nil, 342 gcnode(ResourceIngest, "ns2", "ingest-2"): { 343 gcnode(ResourceContent, "ns2", dgst(8).String()), 344 }, 345 gcnode(resourceSnapshotFlat, "ns3", "overlay/sn2"): { 346 gcnode(resourceSnapshotFlat, "ns3", "overlay/sn1"), 347 }, 348 gcnode(ResourceSnapshot, "ns3", "overlay/sn2"): { 349 gcnode(ResourceSnapshot, "ns3", "overlay/sn1"), 350 }, 351 gcnode(resourceSnapshotFlat, "ns3", "overlay/sn1"): nil, 352 gcnode(resourceSnapshotFlat, "ns3", "overlay/sn3"): nil, 353 gcnode(ResourceSnapshot, "ns3", "overlay/sn3"): { 354 gcnode(ResourceSnapshot, "ns3", "btrfs/sn1"), 355 gcnode(ResourceSnapshot, "ns3", "overlay/sn1"), 356 }, 357 } 358 359 if err := db.Update(func(tx *bolt.Tx) error { 360 v1bkt, err := tx.CreateBucketIfNotExists(bucketKeyVersion) 361 if err != nil { 362 return err 363 } 364 for _, alter := range alters { 365 if err := alter(v1bkt); err != nil { 366 return err 367 } 368 } 369 return nil 370 }); err != nil { 371 t.Fatalf("Update failed: %+v", err) 372 } 373 374 ctx := context.Background() 375 376 for n, nodes := range refs { 377 checkNodeC(ctx, t, db, nodes, func(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { 378 return references(ctx, tx, n, func(n gc.Node) { 379 select { 380 case nc <- n: 381 case <-ctx.Done(): 382 } 383 }) 384 }) 385 if t.Failed() { 386 t.Fatalf("Failure scanning %v", n) 387 } 388 } 389 } 390 391 func newDatabase() (*bolt.DB, func(), error) { 392 td, err := ioutil.TempDir("", "gc-roots-") 393 if err != nil { 394 return nil, nil, err 395 } 396 397 db, err := bolt.Open(filepath.Join(td, "test.db"), 0777, nil) 398 if err != nil { 399 os.RemoveAll(td) 400 return nil, nil, err 401 } 402 403 return db, func() { 404 db.Close() 405 os.RemoveAll(td) 406 }, nil 407 } 408 409 func checkNodeC(ctx context.Context, t *testing.T, db *bolt.DB, expected []gc.Node, fn func(context.Context, *bolt.Tx, chan<- gc.Node) error) { 410 var actual []gc.Node 411 nc := make(chan gc.Node) 412 done := make(chan struct{}) 413 go func() { 414 defer close(done) 415 for n := range nc { 416 actual = append(actual, n) 417 } 418 }() 419 if err := db.View(func(tx *bolt.Tx) error { 420 defer close(nc) 421 return fn(ctx, tx, nc) 422 }); err != nil { 423 t.Fatal(err) 424 } 425 426 <-done 427 checkNodesEqual(t, actual, expected) 428 } 429 430 func checkNodes(ctx context.Context, t *testing.T, db *bolt.DB, expected []gc.Node, fn func(context.Context, *bolt.Tx, func(context.Context, gc.Node) error) error) { 431 var actual []gc.Node 432 scanFn := func(ctx context.Context, n gc.Node) error { 433 actual = append(actual, n) 434 return nil 435 } 436 437 if err := db.View(func(tx *bolt.Tx) error { 438 return fn(ctx, tx, scanFn) 439 }); err != nil { 440 t.Fatal(err) 441 } 442 443 checkNodesEqual(t, actual, expected) 444 } 445 446 func checkNodesEqual(t *testing.T, n1, n2 []gc.Node) { 447 sort.Sort(nodeList(n1)) 448 sort.Sort(nodeList(n2)) 449 450 if len(n1) != len(n2) { 451 t.Fatalf("Nodes do not match\n\tExpected:\n\t%v\n\tActual:\n\t%v", n2, n1) 452 } 453 454 for i := range n1 { 455 if n1[i] != n2[i] { 456 t.Errorf("[%d] root does not match expected: expected %v, got %v", i, n2[i], n1[i]) 457 } 458 } 459 } 460 461 type nodeList []gc.Node 462 463 func (nodes nodeList) Len() int { 464 return len(nodes) 465 } 466 467 func (nodes nodeList) Less(i, j int) bool { 468 if nodes[i].Type != nodes[j].Type { 469 return nodes[i].Type < nodes[j].Type 470 } 471 if nodes[i].Namespace != nodes[j].Namespace { 472 return nodes[i].Namespace < nodes[j].Namespace 473 } 474 return nodes[i].Key < nodes[j].Key 475 } 476 477 func (nodes nodeList) Swap(i, j int) { 478 nodes[i], nodes[j] = nodes[j], nodes[i] 479 } 480 481 type alterFunc func(bkt *bolt.Bucket) error 482 483 func addImage(ns, name string, dgst digest.Digest, labels map[string]string) alterFunc { 484 return func(bkt *bolt.Bucket) error { 485 ibkt, err := createBuckets(bkt, ns, string(bucketKeyObjectImages), name) 486 if err != nil { 487 return err 488 } 489 490 tbkt, err := ibkt.CreateBucket(bucketKeyTarget) 491 if err != nil { 492 return err 493 } 494 if err := tbkt.Put(bucketKeyDigest, []byte(dgst.String())); err != nil { 495 return err 496 } 497 498 return boltutil.WriteLabels(ibkt, labels) 499 } 500 } 501 502 func addSnapshot(ns, snapshotter, name, parent string, labels map[string]string) alterFunc { 503 return func(bkt *bolt.Bucket) error { 504 sbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectSnapshots), snapshotter, name) 505 if err != nil { 506 return err 507 } 508 if parent != "" { 509 if err := sbkt.Put(bucketKeyParent, []byte(parent)); err != nil { 510 return err 511 } 512 } 513 return boltutil.WriteLabels(sbkt, labels) 514 } 515 } 516 517 func addContent(ns string, dgst digest.Digest, labels map[string]string) alterFunc { 518 return func(bkt *bolt.Bucket) error { 519 cbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectContent), string(bucketKeyObjectBlob), dgst.String()) 520 if err != nil { 521 return err 522 } 523 return boltutil.WriteLabels(cbkt, labels) 524 } 525 } 526 527 func addIngest(ns, ref string, expected digest.Digest, expires *time.Time) alterFunc { 528 return func(bkt *bolt.Bucket) error { 529 cbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectContent), string(bucketKeyObjectIngests), ref) 530 if err != nil { 531 return err 532 } 533 if expected != "" { 534 if err := cbkt.Put(bucketKeyExpected, []byte(expected)); err != nil { 535 return err 536 } 537 } 538 if expires != nil { 539 if err := writeExpireAt(*expires, cbkt); err != nil { 540 return err 541 } 542 } 543 return nil 544 } 545 } 546 547 func addLease(ns, lid string, labels map[string]string) alterFunc { 548 return func(bkt *bolt.Bucket) error { 549 lbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectLeases), lid) 550 if err != nil { 551 return err 552 } 553 return boltutil.WriteLabels(lbkt, labels) 554 } 555 } 556 557 func addLeaseSnapshot(ns, lid, snapshotter, name string) alterFunc { 558 return func(bkt *bolt.Bucket) error { 559 sbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectLeases), lid, string(bucketKeyObjectSnapshots), snapshotter) 560 if err != nil { 561 return err 562 } 563 return sbkt.Put([]byte(name), nil) 564 } 565 } 566 567 func addLeaseContent(ns, lid string, dgst digest.Digest) alterFunc { 568 return func(bkt *bolt.Bucket) error { 569 cbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectLeases), lid, string(bucketKeyObjectContent)) 570 if err != nil { 571 return err 572 } 573 return cbkt.Put([]byte(dgst.String()), nil) 574 } 575 } 576 577 func addLeaseIngest(ns, lid, ref string) alterFunc { 578 return func(bkt *bolt.Bucket) error { 579 cbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectLeases), lid, string(bucketKeyObjectIngests)) 580 if err != nil { 581 return err 582 } 583 return cbkt.Put([]byte(ref), nil) 584 } 585 } 586 587 func addContainer(ns, name, snapshotter, snapshot string, labels map[string]string) alterFunc { 588 return func(bkt *bolt.Bucket) error { 589 cbkt, err := createBuckets(bkt, ns, string(bucketKeyObjectContainers), name) 590 if err != nil { 591 return err 592 } 593 if err := cbkt.Put(bucketKeySnapshotter, []byte(snapshotter)); err != nil { 594 return err 595 } 596 if err := cbkt.Put(bucketKeySnapshotKey, []byte(snapshot)); err != nil { 597 return err 598 } 599 return boltutil.WriteLabels(cbkt, labels) 600 } 601 } 602 603 func createBuckets(bkt *bolt.Bucket, names ...string) (*bolt.Bucket, error) { 604 for _, name := range names { 605 nbkt, err := bkt.CreateBucketIfNotExists([]byte(name)) 606 if err != nil { 607 return nil, err 608 } 609 bkt = nbkt 610 } 611 return bkt, nil 612 } 613 614 func labelmap(kv ...string) map[string]string { 615 if len(kv)%2 != 0 { 616 panic("bad labels argument") 617 } 618 l := map[string]string{} 619 for i := 0; i < len(kv); i = i + 2 { 620 l[kv[i]] = kv[i+1] 621 } 622 return l 623 } 624 625 func dgst(i int64) digest.Digest { 626 r := rand.New(rand.NewSource(i)) 627 dgstr := digest.SHA256.Digester() 628 if _, err := io.CopyN(dgstr.Hash(), r, 256); err != nil { 629 panic(err) 630 } 631 return dgstr.Digest() 632 } 633 634 func timeIn(d time.Duration) *time.Time { 635 t := time.Now().UTC().Add(d) 636 return &t 637 }