github.com/demonoid81/containerd@v1.3.4/metadata/snapshot.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 "fmt" 22 "strings" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/containerd/containerd/errdefs" 28 "github.com/containerd/containerd/labels" 29 "github.com/containerd/containerd/log" 30 "github.com/containerd/containerd/metadata/boltutil" 31 "github.com/containerd/containerd/mount" 32 "github.com/containerd/containerd/namespaces" 33 "github.com/containerd/containerd/snapshots" 34 "github.com/pkg/errors" 35 bolt "go.etcd.io/bbolt" 36 ) 37 38 const ( 39 inheritedLabelsPrefix = "containerd.io/snapshot/" 40 ) 41 42 type snapshotter struct { 43 snapshots.Snapshotter 44 name string 45 db *DB 46 l sync.RWMutex 47 } 48 49 // newSnapshotter returns a new Snapshotter which namespaces the given snapshot 50 // using the provided name and database. 51 func newSnapshotter(db *DB, name string, sn snapshots.Snapshotter) *snapshotter { 52 return &snapshotter{ 53 Snapshotter: sn, 54 name: name, 55 db: db, 56 } 57 } 58 59 func createKey(id uint64, namespace, key string) string { 60 return fmt.Sprintf("%s/%d/%s", namespace, id, key) 61 } 62 63 func getKey(tx *bolt.Tx, ns, name, key string) string { 64 bkt := getSnapshotterBucket(tx, ns, name) 65 if bkt == nil { 66 return "" 67 } 68 bkt = bkt.Bucket([]byte(key)) 69 if bkt == nil { 70 return "" 71 } 72 v := bkt.Get(bucketKeyName) 73 if len(v) == 0 { 74 return "" 75 } 76 return string(v) 77 } 78 79 func (s *snapshotter) resolveKey(ctx context.Context, key string) (string, error) { 80 ns, err := namespaces.NamespaceRequired(ctx) 81 if err != nil { 82 return "", err 83 } 84 85 var id string 86 if err := view(ctx, s.db, func(tx *bolt.Tx) error { 87 id = getKey(tx, ns, s.name, key) 88 if id == "" { 89 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key) 90 } 91 return nil 92 }); err != nil { 93 return "", err 94 } 95 96 return id, nil 97 } 98 99 func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { 100 ns, err := namespaces.NamespaceRequired(ctx) 101 if err != nil { 102 return snapshots.Info{}, err 103 } 104 105 var ( 106 bkey string 107 local = snapshots.Info{ 108 Name: key, 109 } 110 ) 111 if err := view(ctx, s.db, func(tx *bolt.Tx) error { 112 bkt := getSnapshotterBucket(tx, ns, s.name) 113 if bkt == nil { 114 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key) 115 } 116 sbkt := bkt.Bucket([]byte(key)) 117 if sbkt == nil { 118 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key) 119 } 120 local.Labels, err = boltutil.ReadLabels(sbkt) 121 if err != nil { 122 return errors.Wrap(err, "failed to read labels") 123 } 124 if err := boltutil.ReadTimestamps(sbkt, &local.Created, &local.Updated); err != nil { 125 return errors.Wrap(err, "failed to read timestamps") 126 } 127 bkey = string(sbkt.Get(bucketKeyName)) 128 local.Parent = string(sbkt.Get(bucketKeyParent)) 129 130 return nil 131 }); err != nil { 132 return snapshots.Info{}, err 133 } 134 135 info, err := s.Snapshotter.Stat(ctx, bkey) 136 if err != nil { 137 return snapshots.Info{}, err 138 } 139 140 return overlayInfo(info, local), nil 141 } 142 143 func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { 144 s.l.RLock() 145 defer s.l.RUnlock() 146 147 ns, err := namespaces.NamespaceRequired(ctx) 148 if err != nil { 149 return snapshots.Info{}, err 150 } 151 152 if info.Name == "" { 153 return snapshots.Info{}, errors.Wrap(errdefs.ErrInvalidArgument, "") 154 } 155 156 var ( 157 bkey string 158 local = snapshots.Info{ 159 Name: info.Name, 160 } 161 ) 162 if err := update(ctx, s.db, func(tx *bolt.Tx) error { 163 bkt := getSnapshotterBucket(tx, ns, s.name) 164 if bkt == nil { 165 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", info.Name) 166 } 167 sbkt := bkt.Bucket([]byte(info.Name)) 168 if sbkt == nil { 169 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", info.Name) 170 } 171 172 local.Labels, err = boltutil.ReadLabels(sbkt) 173 if err != nil { 174 return errors.Wrap(err, "failed to read labels") 175 } 176 if err := boltutil.ReadTimestamps(sbkt, &local.Created, &local.Updated); err != nil { 177 return errors.Wrap(err, "failed to read timestamps") 178 } 179 180 // Handle field updates 181 if len(fieldpaths) > 0 { 182 for _, path := range fieldpaths { 183 if strings.HasPrefix(path, "labels.") { 184 if local.Labels == nil { 185 local.Labels = map[string]string{} 186 } 187 188 key := strings.TrimPrefix(path, "labels.") 189 local.Labels[key] = info.Labels[key] 190 continue 191 } 192 193 switch path { 194 case "labels": 195 local.Labels = info.Labels 196 default: 197 return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on snapshot %q", path, info.Name) 198 } 199 } 200 } else { 201 local.Labels = info.Labels 202 } 203 if err := validateSnapshot(&local); err != nil { 204 return err 205 } 206 local.Updated = time.Now().UTC() 207 208 if err := boltutil.WriteTimestamps(sbkt, local.Created, local.Updated); err != nil { 209 return errors.Wrap(err, "failed to read timestamps") 210 } 211 if err := boltutil.WriteLabels(sbkt, local.Labels); err != nil { 212 return errors.Wrap(err, "failed to read labels") 213 } 214 bkey = string(sbkt.Get(bucketKeyName)) 215 local.Parent = string(sbkt.Get(bucketKeyParent)) 216 217 inner := snapshots.Info{ 218 Name: bkey, 219 Labels: filterInheritedLabels(local.Labels), 220 } 221 222 if _, err := s.Snapshotter.Update(ctx, inner, fieldpaths...); err != nil { 223 return err 224 } 225 226 return nil 227 }); err != nil { 228 return snapshots.Info{}, err 229 } 230 231 info, err = s.Snapshotter.Stat(ctx, bkey) 232 if err != nil { 233 return snapshots.Info{}, err 234 } 235 236 return overlayInfo(info, local), nil 237 } 238 239 func overlayInfo(info, overlay snapshots.Info) snapshots.Info { 240 // Merge info 241 info.Name = overlay.Name 242 info.Created = overlay.Created 243 info.Updated = overlay.Updated 244 info.Parent = overlay.Parent 245 if info.Labels == nil { 246 info.Labels = overlay.Labels 247 } else { 248 for k, v := range overlay.Labels { 249 info.Labels[k] = v 250 } 251 } 252 return info 253 } 254 255 func (s *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) { 256 bkey, err := s.resolveKey(ctx, key) 257 if err != nil { 258 return snapshots.Usage{}, err 259 } 260 return s.Snapshotter.Usage(ctx, bkey) 261 } 262 263 func (s *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) { 264 bkey, err := s.resolveKey(ctx, key) 265 if err != nil { 266 return nil, err 267 } 268 return s.Snapshotter.Mounts(ctx, bkey) 269 } 270 271 func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { 272 return s.createSnapshot(ctx, key, parent, false, opts) 273 } 274 275 func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { 276 return s.createSnapshot(ctx, key, parent, true, opts) 277 } 278 279 func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, readonly bool, opts []snapshots.Opt) ([]mount.Mount, error) { 280 s.l.RLock() 281 defer s.l.RUnlock() 282 283 ns, err := namespaces.NamespaceRequired(ctx) 284 if err != nil { 285 return nil, err 286 } 287 288 var base snapshots.Info 289 for _, opt := range opts { 290 if err := opt(&base); err != nil { 291 return nil, err 292 } 293 } 294 295 if err := validateSnapshot(&base); err != nil { 296 return nil, err 297 } 298 299 var m []mount.Mount 300 if err := update(ctx, s.db, func(tx *bolt.Tx) error { 301 bkt, err := createSnapshotterBucket(tx, ns, s.name) 302 if err != nil { 303 return err 304 } 305 306 bbkt, err := bkt.CreateBucket([]byte(key)) 307 if err != nil { 308 if err == bolt.ErrBucketExists { 309 err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", key) 310 } 311 return err 312 } 313 if err := addSnapshotLease(ctx, tx, s.name, key); err != nil { 314 return err 315 } 316 317 var bparent string 318 if parent != "" { 319 pbkt := bkt.Bucket([]byte(parent)) 320 if pbkt == nil { 321 return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", parent) 322 } 323 bparent = string(pbkt.Get(bucketKeyName)) 324 325 cbkt, err := pbkt.CreateBucketIfNotExists(bucketKeyChildren) 326 if err != nil { 327 return err 328 } 329 if err := cbkt.Put([]byte(key), nil); err != nil { 330 return err 331 } 332 333 if err := bbkt.Put(bucketKeyParent, []byte(parent)); err != nil { 334 return err 335 } 336 } 337 338 sid, err := bkt.NextSequence() 339 if err != nil { 340 return err 341 } 342 bkey := createKey(sid, ns, key) 343 if err := bbkt.Put(bucketKeyName, []byte(bkey)); err != nil { 344 return err 345 } 346 347 ts := time.Now().UTC() 348 if err := boltutil.WriteTimestamps(bbkt, ts, ts); err != nil { 349 return err 350 } 351 if err := boltutil.WriteLabels(bbkt, base.Labels); err != nil { 352 return err 353 } 354 355 inheritedOpt := snapshots.WithLabels(filterInheritedLabels(base.Labels)) 356 357 // TODO: Consider doing this outside of transaction to lessen 358 // metadata lock time 359 if readonly { 360 m, err = s.Snapshotter.View(ctx, bkey, bparent, inheritedOpt) 361 } else { 362 m, err = s.Snapshotter.Prepare(ctx, bkey, bparent, inheritedOpt) 363 } 364 return err 365 }); err != nil { 366 return nil, err 367 } 368 return m, nil 369 } 370 371 func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { 372 s.l.RLock() 373 defer s.l.RUnlock() 374 375 ns, err := namespaces.NamespaceRequired(ctx) 376 if err != nil { 377 return err 378 } 379 380 var base snapshots.Info 381 for _, opt := range opts { 382 if err := opt(&base); err != nil { 383 return err 384 } 385 } 386 387 if err := validateSnapshot(&base); err != nil { 388 return err 389 } 390 391 return update(ctx, s.db, func(tx *bolt.Tx) error { 392 bkt := getSnapshotterBucket(tx, ns, s.name) 393 if bkt == nil { 394 return errors.Wrapf(errdefs.ErrNotFound, 395 "can not find snapshotter %q", s.name) 396 } 397 398 bbkt, err := bkt.CreateBucket([]byte(name)) 399 if err != nil { 400 if err == bolt.ErrBucketExists { 401 err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", name) 402 } 403 return err 404 } 405 if err := addSnapshotLease(ctx, tx, s.name, name); err != nil { 406 return err 407 } 408 409 obkt := bkt.Bucket([]byte(key)) 410 if obkt == nil { 411 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key) 412 } 413 414 bkey := string(obkt.Get(bucketKeyName)) 415 416 sid, err := bkt.NextSequence() 417 if err != nil { 418 return err 419 } 420 421 nameKey := createKey(sid, ns, name) 422 423 if err := bbkt.Put(bucketKeyName, []byte(nameKey)); err != nil { 424 return err 425 } 426 427 parent := obkt.Get(bucketKeyParent) 428 if len(parent) > 0 { 429 pbkt := bkt.Bucket(parent) 430 if pbkt == nil { 431 return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", string(parent)) 432 } 433 434 cbkt, err := pbkt.CreateBucketIfNotExists(bucketKeyChildren) 435 if err != nil { 436 return err 437 } 438 if err := cbkt.Delete([]byte(key)); err != nil { 439 return err 440 } 441 if err := cbkt.Put([]byte(name), nil); err != nil { 442 return err 443 } 444 445 if err := bbkt.Put(bucketKeyParent, parent); err != nil { 446 return err 447 } 448 } 449 ts := time.Now().UTC() 450 if err := boltutil.WriteTimestamps(bbkt, ts, ts); err != nil { 451 return err 452 } 453 if err := boltutil.WriteLabels(bbkt, base.Labels); err != nil { 454 return err 455 } 456 457 if err := bkt.DeleteBucket([]byte(key)); err != nil { 458 return err 459 } 460 if err := removeSnapshotLease(ctx, tx, s.name, key); err != nil { 461 return err 462 } 463 464 inheritedOpt := snapshots.WithLabels(filterInheritedLabels(base.Labels)) 465 466 // TODO: Consider doing this outside of transaction to lessen 467 // metadata lock time 468 return s.Snapshotter.Commit(ctx, nameKey, bkey, inheritedOpt) 469 }) 470 471 } 472 473 func (s *snapshotter) Remove(ctx context.Context, key string) error { 474 s.l.RLock() 475 defer s.l.RUnlock() 476 477 ns, err := namespaces.NamespaceRequired(ctx) 478 if err != nil { 479 return err 480 } 481 482 return update(ctx, s.db, func(tx *bolt.Tx) error { 483 var sbkt *bolt.Bucket 484 bkt := getSnapshotterBucket(tx, ns, s.name) 485 if bkt != nil { 486 sbkt = bkt.Bucket([]byte(key)) 487 } 488 if sbkt == nil { 489 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v does not exist", key) 490 } 491 492 cbkt := sbkt.Bucket(bucketKeyChildren) 493 if cbkt != nil { 494 if child, _ := cbkt.Cursor().First(); child != nil { 495 return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot remove snapshot with child") 496 } 497 } 498 499 parent := sbkt.Get(bucketKeyParent) 500 if len(parent) > 0 { 501 pbkt := bkt.Bucket(parent) 502 if pbkt == nil { 503 return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", string(parent)) 504 } 505 cbkt := pbkt.Bucket(bucketKeyChildren) 506 if cbkt != nil { 507 if err := cbkt.Delete([]byte(key)); err != nil { 508 return errors.Wrap(err, "failed to remove child link") 509 } 510 } 511 } 512 513 if err := bkt.DeleteBucket([]byte(key)); err != nil { 514 return err 515 } 516 if err := removeSnapshotLease(ctx, tx, s.name, key); err != nil { 517 return err 518 } 519 520 // Mark snapshotter as dirty for triggering garbage collection 521 atomic.AddUint32(&s.db.dirty, 1) 522 s.db.dirtySS[s.name] = struct{}{} 523 524 return nil 525 }) 526 } 527 528 type infoPair struct { 529 bkey string 530 info snapshots.Info 531 } 532 533 func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error { 534 ns, err := namespaces.NamespaceRequired(ctx) 535 if err != nil { 536 return err 537 } 538 539 var ( 540 batchSize = 100 541 pairs = []infoPair{} 542 lastKey string 543 ) 544 545 for { 546 if err := view(ctx, s.db, func(tx *bolt.Tx) error { 547 bkt := getSnapshotterBucket(tx, ns, s.name) 548 if bkt == nil { 549 return nil 550 } 551 552 c := bkt.Cursor() 553 554 var k, v []byte 555 if lastKey == "" { 556 k, v = c.First() 557 } else { 558 k, v = c.Seek([]byte(lastKey)) 559 } 560 561 for k != nil { 562 if v == nil { 563 if len(pairs) >= batchSize { 564 break 565 } 566 sbkt := bkt.Bucket(k) 567 568 pair := infoPair{ 569 bkey: string(sbkt.Get(bucketKeyName)), 570 info: snapshots.Info{ 571 Name: string(k), 572 Parent: string(sbkt.Get(bucketKeyParent)), 573 }, 574 } 575 576 err := boltutil.ReadTimestamps(sbkt, &pair.info.Created, &pair.info.Updated) 577 if err != nil { 578 return err 579 } 580 pair.info.Labels, err = boltutil.ReadLabels(sbkt) 581 if err != nil { 582 return err 583 } 584 585 pairs = append(pairs, pair) 586 } 587 588 k, v = c.Next() 589 } 590 591 lastKey = string(k) 592 593 return nil 594 }); err != nil { 595 return err 596 } 597 598 for _, pair := range pairs { 599 info, err := s.Snapshotter.Stat(ctx, pair.bkey) 600 if err != nil { 601 if errdefs.IsNotFound(err) { 602 continue 603 } 604 return err 605 } 606 607 if err := fn(ctx, overlayInfo(info, pair.info)); err != nil { 608 return err 609 } 610 } 611 612 if lastKey == "" { 613 break 614 } 615 616 pairs = pairs[:0] 617 618 } 619 620 return nil 621 } 622 623 func validateSnapshot(info *snapshots.Info) error { 624 for k, v := range info.Labels { 625 if err := labels.Validate(k, v); err != nil { 626 return errors.Wrapf(err, "info.Labels") 627 } 628 } 629 630 return nil 631 } 632 633 type cleaner interface { 634 Cleanup(ctx context.Context) error 635 } 636 637 func (s *snapshotter) garbageCollect(ctx context.Context) (d time.Duration, err error) { 638 s.l.Lock() 639 t1 := time.Now() 640 defer func() { 641 s.l.Unlock() 642 if err == nil { 643 if c, ok := s.Snapshotter.(cleaner); ok { 644 err = c.Cleanup(ctx) 645 } 646 } 647 if err == nil { 648 d = time.Since(t1) 649 } 650 }() 651 652 seen := map[string]struct{}{} 653 if err := s.db.View(func(tx *bolt.Tx) error { 654 v1bkt := tx.Bucket(bucketKeyVersion) 655 if v1bkt == nil { 656 return nil 657 } 658 659 // iterate through each namespace 660 v1c := v1bkt.Cursor() 661 662 for k, v := v1c.First(); k != nil; k, v = v1c.Next() { 663 if v != nil { 664 continue 665 } 666 667 sbkt := v1bkt.Bucket(k).Bucket(bucketKeyObjectSnapshots) 668 if sbkt == nil { 669 continue 670 } 671 672 // Load specific snapshotter 673 ssbkt := sbkt.Bucket([]byte(s.name)) 674 if ssbkt == nil { 675 continue 676 } 677 678 if err := ssbkt.ForEach(func(sk, sv []byte) error { 679 if sv == nil { 680 bkey := ssbkt.Bucket(sk).Get(bucketKeyName) 681 if len(bkey) > 0 { 682 seen[string(bkey)] = struct{}{} 683 } 684 } 685 return nil 686 }); err != nil { 687 return err 688 } 689 } 690 691 return nil 692 }); err != nil { 693 return 0, err 694 } 695 696 roots, err := s.walkTree(ctx, seen) 697 if err != nil { 698 return 0, err 699 } 700 701 // TODO: Unlock before removal (once nodes are fully unavailable). 702 // This could be achieved through doing prune inside the lock 703 // and having a cleanup method which actually performs the 704 // deletions on the snapshotters which support it. 705 706 for _, node := range roots { 707 if err := s.pruneBranch(ctx, node); err != nil { 708 return 0, err 709 } 710 } 711 712 return 713 } 714 715 type treeNode struct { 716 info snapshots.Info 717 remove bool 718 children []*treeNode 719 } 720 721 func (s *snapshotter) walkTree(ctx context.Context, seen map[string]struct{}) ([]*treeNode, error) { 722 roots := []*treeNode{} 723 nodes := map[string]*treeNode{} 724 725 if err := s.Snapshotter.Walk(ctx, func(ctx context.Context, info snapshots.Info) error { 726 _, isSeen := seen[info.Name] 727 node, ok := nodes[info.Name] 728 if !ok { 729 node = &treeNode{} 730 nodes[info.Name] = node 731 } 732 733 node.remove = !isSeen 734 node.info = info 735 736 if info.Parent == "" { 737 roots = append(roots, node) 738 } else { 739 parent, ok := nodes[info.Parent] 740 if !ok { 741 parent = &treeNode{} 742 nodes[info.Parent] = parent 743 } 744 parent.children = append(parent.children, node) 745 } 746 747 return nil 748 }); err != nil { 749 return nil, err 750 } 751 752 return roots, nil 753 } 754 755 func (s *snapshotter) pruneBranch(ctx context.Context, node *treeNode) error { 756 for _, child := range node.children { 757 if err := s.pruneBranch(ctx, child); err != nil { 758 return err 759 } 760 } 761 762 if node.remove { 763 logger := log.G(ctx).WithField("snapshotter", s.name) 764 if err := s.Snapshotter.Remove(ctx, node.info.Name); err != nil { 765 if !errdefs.IsFailedPrecondition(err) { 766 return err 767 } 768 logger.WithError(err).WithField("key", node.info.Name).Warnf("failed to remove snapshot") 769 } else { 770 logger.WithField("key", node.info.Name).Debug("removed snapshot") 771 } 772 } 773 774 return nil 775 } 776 777 // Close closes s.Snapshotter but not db 778 func (s *snapshotter) Close() error { 779 return s.Snapshotter.Close() 780 } 781 782 // filterInheritedLabels filters the provided labels by removing any key which doesn't have 783 // a prefix of "containerd.io/snapshot/". 784 func filterInheritedLabels(labels map[string]string) map[string]string { 785 if labels == nil { 786 return nil 787 } 788 789 filtered := make(map[string]string) 790 for k, v := range labels { 791 if strings.HasPrefix(k, inheritedLabelsPrefix) { 792 filtered[k] = v 793 } 794 } 795 return filtered 796 }