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