github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/builder/builder-next/adapters/snapshot/snapshot.go (about) 1 package snapshot 2 3 import ( 4 "context" 5 "path/filepath" 6 "strconv" 7 "strings" 8 "sync" 9 10 "github.com/Prakhar-Agarwal-byte/moby/daemon/graphdriver" 11 "github.com/Prakhar-Agarwal-byte/moby/layer" 12 "github.com/Prakhar-Agarwal-byte/moby/pkg/idtools" 13 cerrdefs "github.com/containerd/containerd/errdefs" 14 "github.com/containerd/containerd/leases" 15 "github.com/containerd/containerd/mount" 16 "github.com/containerd/containerd/snapshots" 17 "github.com/moby/buildkit/identity" 18 "github.com/moby/buildkit/snapshot" 19 "github.com/moby/buildkit/util/leaseutil" 20 "github.com/opencontainers/go-digest" 21 "github.com/pkg/errors" 22 bolt "go.etcd.io/bbolt" 23 ) 24 25 var ( 26 keyParent = []byte("parent") 27 keyCommitted = []byte("committed") 28 keyIsCommitted = []byte("iscommitted") 29 keyChainID = []byte("chainid") 30 keySize = []byte("size") 31 ) 32 33 // Opt defines options for creating the snapshotter 34 type Opt struct { 35 GraphDriver graphdriver.Driver 36 LayerStore layer.Store 37 Root string 38 IdentityMapping idtools.IdentityMapping 39 } 40 41 type graphIDRegistrar interface { 42 RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error) 43 Release(layer.Layer) ([]layer.Metadata, error) 44 checksumCalculator 45 } 46 47 type checksumCalculator interface { 48 ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error) 49 } 50 51 type snapshotter struct { 52 opt Opt 53 54 refs map[string]layer.Layer 55 db *bolt.DB 56 mu sync.Mutex 57 reg graphIDRegistrar 58 } 59 60 // NewSnapshotter creates a new snapshotter 61 func NewSnapshotter(opt Opt, prevLM leases.Manager, ns string) (snapshot.Snapshotter, *leaseutil.Manager, error) { 62 dbPath := filepath.Join(opt.Root, "snapshots.db") 63 db, err := bolt.Open(dbPath, 0o600, nil) 64 if err != nil { 65 return nil, nil, errors.Wrapf(err, "failed to open database file %s", dbPath) 66 } 67 68 reg, ok := opt.LayerStore.(graphIDRegistrar) 69 if !ok { 70 return nil, nil, errors.Errorf("layerstore doesn't support graphID registration") 71 } 72 73 s := &snapshotter{ 74 opt: opt, 75 db: db, 76 refs: map[string]layer.Layer{}, 77 reg: reg, 78 } 79 80 slm := newLeaseManager(s, prevLM) 81 lm := leaseutil.WithNamespace(slm, ns) 82 83 ll, err := lm.List(context.TODO()) 84 if err != nil { 85 return nil, nil, err 86 } 87 for _, l := range ll { 88 rr, err := lm.ListResources(context.TODO(), l) 89 if err != nil { 90 return nil, nil, err 91 } 92 for _, r := range rr { 93 if r.Type == "snapshots/default" { 94 slm.addRef(l.ID, r.ID) 95 } 96 } 97 } 98 99 return s, lm, nil 100 } 101 102 func (s *snapshotter) Name() string { 103 return "default" 104 } 105 106 func (s *snapshotter) IdentityMapping() *idtools.IdentityMapping { 107 // Returning a non-nil but empty *IdentityMapping breaks BuildKit: 108 // https://github.com/moby/moby/pull/39444 109 if s.opt.IdentityMapping.Empty() { 110 return nil 111 } 112 return &s.opt.IdentityMapping 113 } 114 115 func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { 116 origParent := parent 117 if parent != "" { 118 if l, err := s.getLayer(parent, false); err != nil { 119 return errors.Wrapf(err, "failed to get parent layer %s", parent) 120 } else if l != nil { 121 parent, err = getGraphID(l) 122 if err != nil { 123 return errors.Wrapf(err, "failed to get parent graphid %s", l.ChainID()) 124 } 125 } else { 126 parent, _ = s.getGraphDriverID(parent) 127 } 128 } 129 if err := s.opt.GraphDriver.Create(key, parent, nil); err != nil { 130 return err 131 } 132 return s.db.Update(func(tx *bolt.Tx) error { 133 b, err := tx.CreateBucketIfNotExists([]byte(key)) 134 if err != nil { 135 return err 136 } 137 return b.Put(keyParent, []byte(origParent)) 138 }) 139 } 140 141 func (s *snapshotter) chainID(key string) (layer.ChainID, bool) { 142 if strings.HasPrefix(key, "sha256:") { 143 dgst, err := digest.Parse(key) 144 if err != nil { 145 return "", false 146 } 147 return layer.ChainID(dgst), true 148 } 149 return "", false 150 } 151 152 func (s *snapshotter) GetLayer(key string) (layer.Layer, error) { 153 return s.getLayer(key, true) 154 } 155 156 func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, error) { 157 s.mu.Lock() 158 l, ok := s.refs[key] 159 if !ok { 160 id, ok := s.chainID(key) 161 if !ok { 162 if !withCommitted { 163 s.mu.Unlock() 164 return nil, nil 165 } 166 if err := s.db.View(func(tx *bolt.Tx) error { 167 b := tx.Bucket([]byte(key)) 168 if b == nil { 169 return nil 170 } 171 v := b.Get(keyChainID) 172 if v != nil { 173 id = layer.ChainID(v) 174 } 175 return nil 176 }); err != nil { 177 s.mu.Unlock() 178 return nil, errors.WithStack(err) 179 } 180 s.mu.Unlock() 181 if id == "" { 182 return nil, nil 183 } 184 return s.getLayer(string(id), withCommitted) 185 } 186 var err error 187 l, err = s.opt.LayerStore.Get(id) 188 if err != nil { 189 s.mu.Unlock() 190 return nil, errors.WithStack(err) 191 } 192 s.refs[key] = l 193 if err := s.db.Update(func(tx *bolt.Tx) error { 194 _, err := tx.CreateBucketIfNotExists([]byte(key)) 195 return errors.WithStack(err) 196 }); err != nil { 197 s.mu.Unlock() 198 return nil, err 199 } 200 } 201 s.mu.Unlock() 202 203 return l, nil 204 } 205 206 func (s *snapshotter) getGraphDriverID(key string) (string, bool) { 207 var gdID string 208 if err := s.db.View(func(tx *bolt.Tx) error { 209 b := tx.Bucket([]byte(key)) 210 if b == nil { 211 return errors.Wrapf(cerrdefs.ErrNotFound, "key %s", key) 212 } 213 v := b.Get(keyCommitted) 214 if v != nil { 215 gdID = string(v) 216 } 217 return nil 218 }); err != nil || gdID == "" { 219 return key, false 220 } 221 return gdID, true 222 } 223 224 func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { 225 inf := snapshots.Info{ 226 Kind: snapshots.KindActive, 227 } 228 229 l, err := s.getLayer(key, false) 230 if err != nil { 231 return snapshots.Info{}, err 232 } 233 if l != nil { 234 if p := l.Parent(); p != nil { 235 inf.Parent = p.ChainID().String() 236 } 237 inf.Kind = snapshots.KindCommitted 238 inf.Name = key 239 return inf, nil 240 } 241 242 l, err = s.getLayer(key, true) 243 if err != nil { 244 return snapshots.Info{}, err 245 } 246 247 id, committed := s.getGraphDriverID(key) 248 if committed { 249 inf.Kind = snapshots.KindCommitted 250 } 251 252 if err := s.db.View(func(tx *bolt.Tx) error { 253 b := tx.Bucket([]byte(id)) 254 if b == nil && l == nil { 255 return errors.Wrapf(cerrdefs.ErrNotFound, "snapshot %s", id) 256 } 257 inf.Name = key 258 if b != nil { 259 v := b.Get(keyParent) 260 if v != nil { 261 inf.Parent = string(v) 262 return nil 263 } 264 } 265 if l != nil { 266 if p := l.Parent(); p != nil { 267 inf.Parent = p.ChainID().String() 268 } 269 inf.Kind = snapshots.KindCommitted 270 } 271 return nil 272 }); err != nil { 273 return snapshots.Info{}, err 274 } 275 return inf, nil 276 } 277 278 func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountable, error) { 279 l, err := s.getLayer(key, true) 280 if err != nil { 281 return nil, err 282 } 283 if l != nil { 284 id := identity.NewID() 285 var rwlayer layer.RWLayer 286 return &mountable{ 287 idmap: s.opt.IdentityMapping, 288 acquire: func() ([]mount.Mount, func() error, error) { 289 rwlayer, err = s.opt.LayerStore.CreateRWLayer(id, l.ChainID(), nil) 290 if err != nil { 291 return nil, nil, err 292 } 293 rootfs, err := rwlayer.Mount("") 294 if err != nil { 295 return nil, nil, err 296 } 297 return []mount.Mount{{ 298 Source: rootfs, 299 Type: "bind", 300 Options: []string{"rbind"}, 301 }}, func() error { 302 _, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer) 303 return err 304 }, nil 305 }, 306 }, nil 307 } 308 309 id, _ := s.getGraphDriverID(key) 310 311 return &mountable{ 312 idmap: s.opt.IdentityMapping, 313 acquire: func() ([]mount.Mount, func() error, error) { 314 rootfs, err := s.opt.GraphDriver.Get(id, "") 315 if err != nil { 316 return nil, nil, err 317 } 318 return []mount.Mount{{ 319 Source: rootfs, 320 Type: "bind", 321 Options: []string{"rbind"}, 322 }}, func() error { 323 return s.opt.GraphDriver.Put(id) 324 }, nil 325 }, 326 }, nil 327 } 328 329 func (s *snapshotter) Remove(ctx context.Context, key string) error { 330 return errors.Errorf("calling snapshot.remove is forbidden") 331 } 332 333 func (s *snapshotter) remove(ctx context.Context, key string) error { 334 l, err := s.getLayer(key, true) 335 if err != nil { 336 return err 337 } 338 339 id, _ := s.getGraphDriverID(key) 340 341 var found bool 342 var alreadyCommitted bool 343 if err := s.db.Update(func(tx *bolt.Tx) error { 344 b := tx.Bucket([]byte(key)) 345 found = b != nil 346 347 if b != nil { 348 if b.Get(keyIsCommitted) != nil { 349 alreadyCommitted = true 350 return nil 351 } 352 } 353 if found { 354 tx.DeleteBucket([]byte(key)) 355 if id != key { 356 tx.DeleteBucket([]byte(id)) 357 } 358 } 359 return nil 360 }); err != nil { 361 return err 362 } 363 364 if alreadyCommitted { 365 return nil 366 } 367 368 if l != nil { 369 s.mu.Lock() 370 delete(s.refs, key) 371 s.mu.Unlock() 372 _, err := s.opt.LayerStore.Release(l) 373 return err 374 } 375 376 if !found { // this happens when removing views 377 return nil 378 } 379 380 return s.opt.GraphDriver.Remove(id) 381 } 382 383 func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { 384 return s.db.Update(func(tx *bolt.Tx) error { 385 b, err := tx.CreateBucketIfNotExists([]byte(name)) 386 if err != nil { 387 return err 388 } 389 if err := b.Put(keyCommitted, []byte(key)); err != nil { 390 return err 391 } 392 b, err = tx.CreateBucketIfNotExists([]byte(key)) 393 if err != nil { 394 return err 395 } 396 return b.Put(keyIsCommitted, []byte{}) 397 }) 398 } 399 400 func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (snapshot.Mountable, error) { 401 return s.Mounts(ctx, parent) 402 } 403 404 func (s *snapshotter) Walk(context.Context, snapshots.WalkFunc, ...string) error { 405 return nil 406 } 407 408 func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { 409 // not implemented 410 return s.Stat(ctx, info.Name) 411 } 412 413 func (s *snapshotter) Usage(ctx context.Context, key string) (us snapshots.Usage, retErr error) { 414 usage := snapshots.Usage{} 415 if l, err := s.getLayer(key, true); err != nil { 416 return usage, err 417 } else if l != nil { 418 usage.Size = l.DiffSize() 419 return usage, nil 420 } 421 422 size := int64(-1) 423 if err := s.db.View(func(tx *bolt.Tx) error { 424 b := tx.Bucket([]byte(key)) 425 if b == nil { 426 return nil 427 } 428 v := b.Get(keySize) 429 if v != nil { 430 s, err := strconv.Atoi(string(v)) 431 if err != nil { 432 return err 433 } 434 size = int64(s) 435 } 436 return nil 437 }); err != nil { 438 return usage, err 439 } 440 441 if size != -1 { 442 usage.Size = size 443 return usage, nil 444 } 445 446 id, _ := s.getGraphDriverID(key) 447 448 info, err := s.Stat(ctx, key) 449 if err != nil { 450 return usage, err 451 } 452 var parent string 453 if info.Parent != "" { 454 if l, err := s.getLayer(info.Parent, false); err != nil { 455 return usage, err 456 } else if l != nil { 457 parent, err = getGraphID(l) 458 if err != nil { 459 return usage, err 460 } 461 } else { 462 parent, _ = s.getGraphDriverID(info.Parent) 463 } 464 } 465 466 diffSize, err := s.opt.GraphDriver.DiffSize(id, parent) 467 if err != nil { 468 return usage, err 469 } 470 471 if err := s.db.Update(func(tx *bolt.Tx) error { 472 b, err := tx.CreateBucketIfNotExists([]byte(key)) 473 if err != nil { 474 return err 475 } 476 return b.Put(keySize, []byte(strconv.Itoa(int(diffSize)))) 477 }); err != nil { 478 return usage, err 479 } 480 usage.Size = diffSize 481 return usage, nil 482 } 483 484 func (s *snapshotter) Close() error { 485 return s.db.Close() 486 } 487 488 type mountable struct { 489 mu sync.Mutex 490 mounts []mount.Mount 491 acquire func() ([]mount.Mount, func() error, error) 492 release func() error 493 refCount int 494 idmap idtools.IdentityMapping 495 } 496 497 func (m *mountable) Mount() ([]mount.Mount, func() error, error) { 498 m.mu.Lock() 499 defer m.mu.Unlock() 500 501 if m.mounts != nil { 502 m.refCount++ 503 return m.mounts, m.releaseMount, nil 504 } 505 506 mounts, release, err := m.acquire() 507 if err != nil { 508 return nil, nil, err 509 } 510 m.mounts = mounts 511 m.release = release 512 m.refCount = 1 513 514 return m.mounts, m.releaseMount, nil 515 } 516 517 func (m *mountable) releaseMount() error { 518 m.mu.Lock() 519 defer m.mu.Unlock() 520 521 if m.refCount > 1 { 522 m.refCount-- 523 return nil 524 } 525 526 m.refCount = 0 527 if m.release == nil { 528 return nil 529 } 530 531 m.mounts = nil 532 defer func() { 533 m.release = nil 534 }() 535 return m.release() 536 } 537 538 func (m *mountable) IdentityMapping() *idtools.IdentityMapping { 539 // Returning a non-nil but empty *IdentityMapping breaks BuildKit: 540 // https://github.com/moby/moby/pull/39444 541 if m.idmap.Empty() { 542 return nil 543 } 544 return &m.idmap 545 }