github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/snapshots/storage/bolt.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 storage 18 19 import ( 20 "context" 21 "encoding/binary" 22 "fmt" 23 "strings" 24 "time" 25 26 "github.com/containerd/containerd/errdefs" 27 "github.com/containerd/containerd/filters" 28 "github.com/containerd/containerd/metadata/boltutil" 29 "github.com/containerd/containerd/snapshots" 30 "github.com/pkg/errors" 31 bolt "go.etcd.io/bbolt" 32 ) 33 34 var ( 35 bucketKeyStorageVersion = []byte("v1") 36 bucketKeySnapshot = []byte("snapshots") 37 bucketKeyParents = []byte("parents") 38 39 bucketKeyID = []byte("id") 40 bucketKeyParent = []byte("parent") 41 bucketKeyKind = []byte("kind") 42 bucketKeyInodes = []byte("inodes") 43 bucketKeySize = []byte("size") 44 45 // ErrNoTransaction is returned when an operation is attempted with 46 // a context which is not inside of a transaction. 47 ErrNoTransaction = errors.New("no transaction in context") 48 ) 49 50 // parentKey returns a composite key of the parent and child identifiers. The 51 // parts of the key are separated by a zero byte. 52 func parentKey(parent, child uint64) []byte { 53 b := make([]byte, binary.Size([]uint64{parent, child})+1) 54 i := binary.PutUvarint(b, parent) 55 j := binary.PutUvarint(b[i+1:], child) 56 return b[0 : i+j+1] 57 } 58 59 // parentPrefixKey returns the parent part of the composite key with the 60 // zero byte separator. 61 func parentPrefixKey(parent uint64) []byte { 62 b := make([]byte, binary.Size(parent)+1) 63 i := binary.PutUvarint(b, parent) 64 return b[0 : i+1] 65 } 66 67 // getParentPrefix returns the first part of the composite key which 68 // represents the parent identifier. 69 func getParentPrefix(b []byte) uint64 { 70 parent, _ := binary.Uvarint(b) 71 return parent 72 } 73 74 // GetInfo returns the snapshot Info directly from the metadata. Requires a 75 // context with a storage transaction. 76 func GetInfo(ctx context.Context, key string) (string, snapshots.Info, snapshots.Usage, error) { 77 var ( 78 id uint64 79 su snapshots.Usage 80 si = snapshots.Info{ 81 Name: key, 82 } 83 ) 84 err := withSnapshotBucket(ctx, key, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 85 getUsage(bkt, &su) 86 return readSnapshot(bkt, &id, &si) 87 }) 88 if err != nil { 89 return "", snapshots.Info{}, snapshots.Usage{}, err 90 } 91 92 return fmt.Sprintf("%d", id), si, su, nil 93 } 94 95 // UpdateInfo updates an existing snapshot info's data 96 func UpdateInfo(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { 97 updated := snapshots.Info{ 98 Name: info.Name, 99 } 100 err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 101 sbkt := bkt.Bucket([]byte(info.Name)) 102 if sbkt == nil { 103 return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist") 104 } 105 if err := readSnapshot(sbkt, nil, &updated); err != nil { 106 return err 107 } 108 109 if len(fieldpaths) > 0 { 110 for _, path := range fieldpaths { 111 if strings.HasPrefix(path, "labels.") { 112 if updated.Labels == nil { 113 updated.Labels = map[string]string{} 114 } 115 116 key := strings.TrimPrefix(path, "labels.") 117 updated.Labels[key] = info.Labels[key] 118 continue 119 } 120 121 switch path { 122 case "labels": 123 updated.Labels = info.Labels 124 default: 125 return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on snapshot %q", path, info.Name) 126 } 127 } 128 } else { 129 // Set mutable fields 130 updated.Labels = info.Labels 131 } 132 updated.Updated = time.Now().UTC() 133 if err := boltutil.WriteTimestamps(sbkt, updated.Created, updated.Updated); err != nil { 134 return err 135 } 136 137 return boltutil.WriteLabels(sbkt, updated.Labels) 138 }) 139 if err != nil { 140 return snapshots.Info{}, err 141 } 142 return updated, nil 143 } 144 145 // WalkInfo iterates through all metadata Info for the stored snapshots and 146 // calls the provided function for each. Requires a context with a storage 147 // transaction. 148 func WalkInfo(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error { 149 filter, err := filters.ParseAll(fs...) 150 if err != nil { 151 return err 152 } 153 // TODO: allow indexes (name, parent, specific labels) 154 return withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 155 return bkt.ForEach(func(k, v []byte) error { 156 // skip non buckets 157 if v != nil { 158 return nil 159 } 160 var ( 161 sbkt = bkt.Bucket(k) 162 si = snapshots.Info{ 163 Name: string(k), 164 } 165 ) 166 if err := readSnapshot(sbkt, nil, &si); err != nil { 167 return err 168 } 169 if !filter.Match(adaptSnapshot(si)) { 170 return nil 171 } 172 173 return fn(ctx, si) 174 }) 175 }) 176 } 177 178 // GetSnapshot returns the metadata for the active or view snapshot transaction 179 // referenced by the given key. Requires a context with a storage transaction. 180 func GetSnapshot(ctx context.Context, key string) (s Snapshot, err error) { 181 err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 182 sbkt := bkt.Bucket([]byte(key)) 183 if sbkt == nil { 184 return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist") 185 } 186 187 s.ID = fmt.Sprintf("%d", readID(sbkt)) 188 s.Kind = readKind(sbkt) 189 190 if s.Kind != snapshots.KindActive && s.Kind != snapshots.KindView { 191 return errors.Wrapf(errdefs.ErrFailedPrecondition, "requested snapshot %v not active or view", key) 192 } 193 194 if parentKey := sbkt.Get(bucketKeyParent); len(parentKey) > 0 { 195 spbkt := bkt.Bucket(parentKey) 196 if spbkt == nil { 197 return errors.Wrap(errdefs.ErrNotFound, "parent does not exist") 198 } 199 200 s.ParentIDs, err = parents(bkt, spbkt, readID(spbkt)) 201 if err != nil { 202 return errors.Wrap(err, "failed to get parent chain") 203 } 204 } 205 return nil 206 }) 207 if err != nil { 208 return Snapshot{}, err 209 } 210 211 return 212 } 213 214 // CreateSnapshot inserts a record for an active or view snapshot with the provided parent. 215 func CreateSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) (s Snapshot, err error) { 216 switch kind { 217 case snapshots.KindActive, snapshots.KindView: 218 default: 219 return Snapshot{}, errors.Wrapf(errdefs.ErrInvalidArgument, "snapshot type %v invalid; only snapshots of type Active or View can be created", kind) 220 } 221 var base snapshots.Info 222 for _, opt := range opts { 223 if err := opt(&base); err != nil { 224 return Snapshot{}, err 225 } 226 } 227 228 err = createBucketIfNotExists(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 229 var ( 230 spbkt *bolt.Bucket 231 ) 232 if parent != "" { 233 spbkt = bkt.Bucket([]byte(parent)) 234 if spbkt == nil { 235 return errors.Wrapf(errdefs.ErrNotFound, "missing parent %q bucket", parent) 236 } 237 238 if readKind(spbkt) != snapshots.KindCommitted { 239 return errors.Wrapf(errdefs.ErrInvalidArgument, "parent %q is not committed snapshot", parent) 240 } 241 } 242 sbkt, err := bkt.CreateBucket([]byte(key)) 243 if err != nil { 244 if err == bolt.ErrBucketExists { 245 err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %v", key) 246 } 247 return err 248 } 249 250 id, err := bkt.NextSequence() 251 if err != nil { 252 return errors.Wrapf(err, "unable to get identifier for snapshot %q", key) 253 } 254 255 t := time.Now().UTC() 256 si := snapshots.Info{ 257 Parent: parent, 258 Kind: kind, 259 Labels: base.Labels, 260 Created: t, 261 Updated: t, 262 } 263 if err := putSnapshot(sbkt, id, si); err != nil { 264 return err 265 } 266 267 if spbkt != nil { 268 pid := readID(spbkt) 269 270 // Store a backlink from the key to the parent. Store the snapshot name 271 // as the value to allow following the backlink to the snapshot value. 272 if err := pbkt.Put(parentKey(pid, id), []byte(key)); err != nil { 273 return errors.Wrapf(err, "failed to write parent link for snapshot %q", key) 274 } 275 276 s.ParentIDs, err = parents(bkt, spbkt, pid) 277 if err != nil { 278 return errors.Wrapf(err, "failed to get parent chain for snapshot %q", key) 279 } 280 } 281 282 s.ID = fmt.Sprintf("%d", id) 283 s.Kind = kind 284 return nil 285 }) 286 if err != nil { 287 return Snapshot{}, err 288 } 289 290 return 291 } 292 293 // Remove removes a snapshot from the metastore. The string identifier for the 294 // snapshot is returned as well as the kind. The provided context must contain a 295 // writable transaction. 296 func Remove(ctx context.Context, key string) (string, snapshots.Kind, error) { 297 var ( 298 id uint64 299 si snapshots.Info 300 ) 301 302 if err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 303 sbkt := bkt.Bucket([]byte(key)) 304 if sbkt == nil { 305 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key) 306 } 307 308 if err := readSnapshot(sbkt, &id, &si); err != nil { 309 return errors.Wrapf(err, "failed to read snapshot %s", key) 310 } 311 312 if pbkt != nil { 313 k, _ := pbkt.Cursor().Seek(parentPrefixKey(id)) 314 if getParentPrefix(k) == id { 315 return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot remove snapshot with child") 316 } 317 318 if si.Parent != "" { 319 spbkt := bkt.Bucket([]byte(si.Parent)) 320 if spbkt == nil { 321 return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key) 322 } 323 324 if err := pbkt.Delete(parentKey(readID(spbkt), id)); err != nil { 325 return errors.Wrap(err, "failed to delete parent link") 326 } 327 } 328 } 329 330 if err := bkt.DeleteBucket([]byte(key)); err != nil { 331 return errors.Wrap(err, "failed to delete snapshot") 332 } 333 334 return nil 335 }); err != nil { 336 return "", 0, err 337 } 338 339 return fmt.Sprintf("%d", id), si.Kind, nil 340 } 341 342 // CommitActive renames the active snapshot transaction referenced by `key` 343 // as a committed snapshot referenced by `Name`. The resulting snapshot will be 344 // committed and readonly. The `key` reference will no longer be available for 345 // lookup or removal. The returned string identifier for the committed snapshot 346 // is the same identifier of the original active snapshot. The provided context 347 // must contain a writable transaction. 348 func CommitActive(ctx context.Context, key, name string, usage snapshots.Usage, opts ...snapshots.Opt) (string, error) { 349 var ( 350 id uint64 351 base snapshots.Info 352 ) 353 for _, opt := range opts { 354 if err := opt(&base); err != nil { 355 return "", err 356 } 357 } 358 359 if err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error { 360 dbkt, err := bkt.CreateBucket([]byte(name)) 361 if err != nil { 362 if err == bolt.ErrBucketExists { 363 err = errdefs.ErrAlreadyExists 364 } 365 return errors.Wrapf(err, "committed snapshot %v", name) 366 } 367 sbkt := bkt.Bucket([]byte(key)) 368 if sbkt == nil { 369 return errors.Wrapf(errdefs.ErrNotFound, "failed to get active snapshot %q", key) 370 } 371 372 var si snapshots.Info 373 if err := readSnapshot(sbkt, &id, &si); err != nil { 374 return errors.Wrapf(err, "failed to read active snapshot %q", key) 375 } 376 377 if si.Kind != snapshots.KindActive { 378 return errors.Wrapf(errdefs.ErrFailedPrecondition, "snapshot %q is not active", key) 379 } 380 si.Kind = snapshots.KindCommitted 381 si.Created = time.Now().UTC() 382 si.Updated = si.Created 383 384 // Replace labels, do not inherit 385 si.Labels = base.Labels 386 387 if err := putSnapshot(dbkt, id, si); err != nil { 388 return err 389 } 390 if err := putUsage(dbkt, usage); err != nil { 391 return err 392 } 393 if err := bkt.DeleteBucket([]byte(key)); err != nil { 394 return errors.Wrapf(err, "failed to delete active snapshot %q", key) 395 } 396 if si.Parent != "" { 397 spbkt := bkt.Bucket([]byte(si.Parent)) 398 if spbkt == nil { 399 return errors.Wrapf(errdefs.ErrNotFound, "missing parent %q of snapshot %q", si.Parent, key) 400 } 401 pid := readID(spbkt) 402 403 // Updates parent back link to use new key 404 if err := pbkt.Put(parentKey(pid, id), []byte(name)); err != nil { 405 return errors.Wrapf(err, "failed to update parent link %q from %q to %q", pid, key, name) 406 } 407 } 408 409 return nil 410 }); err != nil { 411 return "", err 412 } 413 414 return fmt.Sprintf("%d", id), nil 415 } 416 417 // IDMap returns all the IDs mapped to their key 418 func IDMap(ctx context.Context) (map[string]string, error) { 419 m := map[string]string{} 420 if err := withBucket(ctx, func(ctx context.Context, bkt, _ *bolt.Bucket) error { 421 return bkt.ForEach(func(k, v []byte) error { 422 // skip non buckets 423 if v != nil { 424 return nil 425 } 426 id := readID(bkt.Bucket(k)) 427 m[fmt.Sprintf("%d", id)] = string(k) 428 return nil 429 }) 430 }); err != nil { 431 return nil, err 432 } 433 434 return m, nil 435 } 436 437 func withSnapshotBucket(ctx context.Context, key string, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error { 438 tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) 439 if !ok { 440 return ErrNoTransaction 441 } 442 vbkt := tx.Bucket(bucketKeyStorageVersion) 443 if vbkt == nil { 444 return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist") 445 } 446 bkt := vbkt.Bucket(bucketKeySnapshot) 447 if bkt == nil { 448 return errors.Wrap(errdefs.ErrNotFound, "snapshots bucket does not exist") 449 } 450 bkt = bkt.Bucket([]byte(key)) 451 if bkt == nil { 452 return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist") 453 } 454 455 return fn(ctx, bkt, vbkt.Bucket(bucketKeyParents)) 456 } 457 458 func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error { 459 tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) 460 if !ok { 461 return ErrNoTransaction 462 } 463 bkt := tx.Bucket(bucketKeyStorageVersion) 464 if bkt == nil { 465 return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist") 466 } 467 return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents)) 468 } 469 470 func createBucketIfNotExists(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error { 471 tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) 472 if !ok { 473 return ErrNoTransaction 474 } 475 476 bkt, err := tx.CreateBucketIfNotExists(bucketKeyStorageVersion) 477 if err != nil { 478 return errors.Wrap(err, "failed to create version bucket") 479 } 480 sbkt, err := bkt.CreateBucketIfNotExists(bucketKeySnapshot) 481 if err != nil { 482 return errors.Wrap(err, "failed to create snapshots bucket") 483 } 484 pbkt, err := bkt.CreateBucketIfNotExists(bucketKeyParents) 485 if err != nil { 486 return errors.Wrap(err, "failed to create parents bucket") 487 } 488 return fn(ctx, sbkt, pbkt) 489 } 490 491 func parents(bkt, pbkt *bolt.Bucket, parent uint64) (parents []string, err error) { 492 for { 493 parents = append(parents, fmt.Sprintf("%d", parent)) 494 495 parentKey := pbkt.Get(bucketKeyParent) 496 if len(parentKey) == 0 { 497 return 498 } 499 pbkt = bkt.Bucket(parentKey) 500 if pbkt == nil { 501 return nil, errors.Wrap(errdefs.ErrNotFound, "missing parent") 502 } 503 504 parent = readID(pbkt) 505 } 506 } 507 508 func readKind(bkt *bolt.Bucket) (k snapshots.Kind) { 509 kind := bkt.Get(bucketKeyKind) 510 if len(kind) == 1 { 511 k = snapshots.Kind(kind[0]) 512 } 513 return 514 } 515 516 func readID(bkt *bolt.Bucket) uint64 { 517 id, _ := binary.Uvarint(bkt.Get(bucketKeyID)) 518 return id 519 } 520 521 func readSnapshot(bkt *bolt.Bucket, id *uint64, si *snapshots.Info) error { 522 if id != nil { 523 *id = readID(bkt) 524 } 525 if si != nil { 526 si.Kind = readKind(bkt) 527 si.Parent = string(bkt.Get(bucketKeyParent)) 528 529 if err := boltutil.ReadTimestamps(bkt, &si.Created, &si.Updated); err != nil { 530 return err 531 } 532 533 labels, err := boltutil.ReadLabels(bkt) 534 if err != nil { 535 return err 536 } 537 si.Labels = labels 538 } 539 540 return nil 541 } 542 543 func putSnapshot(bkt *bolt.Bucket, id uint64, si snapshots.Info) error { 544 idEncoded, err := encodeID(id) 545 if err != nil { 546 return err 547 } 548 549 updates := [][2][]byte{ 550 {bucketKeyID, idEncoded}, 551 {bucketKeyKind, []byte{byte(si.Kind)}}, 552 } 553 if si.Parent != "" { 554 updates = append(updates, [2][]byte{bucketKeyParent, []byte(si.Parent)}) 555 } 556 for _, v := range updates { 557 if err := bkt.Put(v[0], v[1]); err != nil { 558 return err 559 } 560 } 561 if err := boltutil.WriteTimestamps(bkt, si.Created, si.Updated); err != nil { 562 return err 563 } 564 return boltutil.WriteLabels(bkt, si.Labels) 565 } 566 567 func getUsage(bkt *bolt.Bucket, usage *snapshots.Usage) { 568 usage.Inodes, _ = binary.Varint(bkt.Get(bucketKeyInodes)) 569 usage.Size, _ = binary.Varint(bkt.Get(bucketKeySize)) 570 } 571 572 func putUsage(bkt *bolt.Bucket, usage snapshots.Usage) error { 573 for _, v := range []struct { 574 key []byte 575 value int64 576 }{ 577 {bucketKeyInodes, usage.Inodes}, 578 {bucketKeySize, usage.Size}, 579 } { 580 e, err := encodeSize(v.value) 581 if err != nil { 582 return err 583 } 584 if err := bkt.Put(v.key, e); err != nil { 585 return err 586 } 587 } 588 return nil 589 } 590 591 func encodeSize(size int64) ([]byte, error) { 592 var ( 593 buf [binary.MaxVarintLen64]byte 594 sizeEncoded = buf[:] 595 ) 596 sizeEncoded = sizeEncoded[:binary.PutVarint(sizeEncoded, size)] 597 598 if len(sizeEncoded) == 0 { 599 return nil, fmt.Errorf("failed encoding size = %v", size) 600 } 601 return sizeEncoded, nil 602 } 603 604 func encodeID(id uint64) ([]byte, error) { 605 var ( 606 buf [binary.MaxVarintLen64]byte 607 idEncoded = buf[:] 608 ) 609 idEncoded = idEncoded[:binary.PutUvarint(idEncoded, id)] 610 611 if len(idEncoded) == 0 { 612 return nil, fmt.Errorf("failed encoding id = %v", id) 613 } 614 return idEncoded, nil 615 } 616 617 func adaptSnapshot(info snapshots.Info) filters.Adaptor { 618 return filters.AdapterFunc(func(fieldpath []string) (string, bool) { 619 if len(fieldpath) == 0 { 620 return "", false 621 } 622 623 switch fieldpath[0] { 624 case "kind": 625 switch info.Kind { 626 case snapshots.KindActive: 627 return "active", true 628 case snapshots.KindView: 629 return "view", true 630 case snapshots.KindCommitted: 631 return "committed", true 632 } 633 case "name": 634 return info.Name, true 635 case "parent": 636 return info.Parent, true 637 case "labels": 638 if len(info.Labels) == 0 { 639 return "", false 640 } 641 642 v, ok := info.Labels[strings.Join(fieldpath[1:], ".")] 643 return v, ok 644 } 645 646 return "", false 647 }) 648 }