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