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