github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/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/leases" 11 "github.com/containerd/containerd/mount" 12 "github.com/containerd/containerd/snapshots" 13 "github.com/demonoid81/moby/daemon/graphdriver" 14 "github.com/demonoid81/moby/layer" 15 "github.com/demonoid81/moby/pkg/idtools" 16 "github.com/moby/buildkit/identity" 17 "github.com/moby/buildkit/snapshot" 18 digest "github.com/opencontainers/go-digest" 19 "github.com/pkg/errors" 20 bolt "go.etcd.io/bbolt" 21 ) 22 23 var keyParent = []byte("parent") 24 var keyCommitted = []byte("committed") 25 var keyIsCommitted = []byte("iscommitted") 26 var keyChainID = []byte("chainid") 27 var keySize = []byte("size") 28 29 // Opt defines options for creating the snapshotter 30 type Opt struct { 31 GraphDriver graphdriver.Driver 32 LayerStore layer.Store 33 Root string 34 IdentityMapping *idtools.IdentityMapping 35 } 36 37 type graphIDRegistrar interface { 38 RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error) 39 Release(layer.Layer) ([]layer.Metadata, error) 40 checksumCalculator 41 } 42 43 type checksumCalculator interface { 44 ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error) 45 } 46 47 type snapshotter struct { 48 opt Opt 49 50 refs map[string]layer.Layer 51 db *bolt.DB 52 mu sync.Mutex 53 reg graphIDRegistrar 54 } 55 56 // NewSnapshotter creates a new snapshotter 57 func NewSnapshotter(opt Opt, prevLM leases.Manager) (snapshot.Snapshotter, leases.Manager, error) { 58 dbPath := filepath.Join(opt.Root, "snapshots.db") 59 db, err := bolt.Open(dbPath, 0600, nil) 60 if err != nil { 61 return nil, nil, errors.Wrapf(err, "failed to open database file %s", dbPath) 62 } 63 64 reg, ok := opt.LayerStore.(graphIDRegistrar) 65 if !ok { 66 return nil, nil, errors.Errorf("layerstore doesn't support graphID registration") 67 } 68 69 s := &snapshotter{ 70 opt: opt, 71 db: db, 72 refs: map[string]layer.Layer{}, 73 reg: reg, 74 } 75 76 lm := newLeaseManager(s, prevLM) 77 78 ll, err := lm.List(context.TODO()) 79 if err != nil { 80 return nil, nil, err 81 } 82 for _, l := range ll { 83 rr, err := lm.ListResources(context.TODO(), l) 84 if err != nil { 85 return nil, nil, err 86 } 87 for _, r := range rr { 88 if r.Type == "snapshots/default" { 89 lm.addRef(l.ID, r.ID) 90 } 91 } 92 } 93 94 return s, lm, nil 95 } 96 97 func (s *snapshotter) Name() string { 98 return "default" 99 } 100 101 func (s *snapshotter) IdentityMapping() *idtools.IdentityMapping { 102 return s.opt.IdentityMapping 103 } 104 105 func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { 106 origParent := parent 107 if parent != "" { 108 if l, err := s.getLayer(parent, false); err != nil { 109 return errors.Wrapf(err, "failed to get parent layer %s", parent) 110 } else if l != nil { 111 parent, err = getGraphID(l) 112 if err != nil { 113 return errors.Wrapf(err, "failed to get parent graphid %s", l.ChainID()) 114 } 115 } else { 116 parent, _ = s.getGraphDriverID(parent) 117 } 118 } 119 if err := s.opt.GraphDriver.Create(key, parent, nil); err != nil { 120 return err 121 } 122 return s.db.Update(func(tx *bolt.Tx) error { 123 b, err := tx.CreateBucketIfNotExists([]byte(key)) 124 if err != nil { 125 return err 126 } 127 return b.Put(keyParent, []byte(origParent)) 128 }) 129 } 130 131 func (s *snapshotter) chainID(key string) (layer.ChainID, bool) { 132 if strings.HasPrefix(key, "sha256:") { 133 dgst, err := digest.Parse(key) 134 if err != nil { 135 return "", false 136 } 137 return layer.ChainID(dgst), true 138 } 139 return "", false 140 } 141 142 func (s *snapshotter) GetLayer(key string) (layer.Layer, error) { 143 return s.getLayer(key, true) 144 } 145 146 func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, error) { 147 s.mu.Lock() 148 l, ok := s.refs[key] 149 if !ok { 150 id, ok := s.chainID(key) 151 if !ok { 152 if !withCommitted { 153 s.mu.Unlock() 154 return nil, nil 155 } 156 if err := s.db.View(func(tx *bolt.Tx) error { 157 b := tx.Bucket([]byte(key)) 158 if b == nil { 159 return nil 160 } 161 v := b.Get(keyChainID) 162 if v != nil { 163 id = layer.ChainID(v) 164 } 165 return nil 166 }); err != nil { 167 s.mu.Unlock() 168 return nil, errors.WithStack(err) 169 } 170 if id == "" { 171 s.mu.Unlock() 172 return nil, nil 173 } 174 } 175 var err error 176 l, err = s.opt.LayerStore.Get(id) 177 if err != nil { 178 s.mu.Unlock() 179 return nil, errors.WithStack(err) 180 } 181 s.refs[key] = l 182 if err := s.db.Update(func(tx *bolt.Tx) error { 183 _, err := tx.CreateBucketIfNotExists([]byte(key)) 184 return errors.WithStack(err) 185 }); err != nil { 186 s.mu.Unlock() 187 return nil, err 188 } 189 } 190 s.mu.Unlock() 191 192 return l, nil 193 } 194 195 func (s *snapshotter) getGraphDriverID(key string) (string, bool) { 196 var gdID string 197 if err := s.db.View(func(tx *bolt.Tx) error { 198 b := tx.Bucket([]byte(key)) 199 if b == nil { 200 return errors.Errorf("not found") // TODO: typed 201 } 202 v := b.Get(keyCommitted) 203 if v != nil { 204 gdID = string(v) 205 } 206 return nil 207 }); err != nil || gdID == "" { 208 return key, false 209 } 210 return gdID, true 211 } 212 213 func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { 214 inf := snapshots.Info{ 215 Kind: snapshots.KindActive, 216 } 217 218 l, err := s.getLayer(key, false) 219 if err != nil { 220 return snapshots.Info{}, err 221 } 222 if l != nil { 223 if p := l.Parent(); p != nil { 224 inf.Parent = p.ChainID().String() 225 } 226 inf.Kind = snapshots.KindCommitted 227 inf.Name = key 228 return inf, nil 229 } 230 231 l, err = s.getLayer(key, true) 232 if err != nil { 233 return snapshots.Info{}, err 234 } 235 236 id, committed := s.getGraphDriverID(key) 237 if committed { 238 inf.Kind = snapshots.KindCommitted 239 } 240 241 if err := s.db.View(func(tx *bolt.Tx) error { 242 b := tx.Bucket([]byte(id)) 243 if b == nil && l == nil { 244 return errors.Errorf("snapshot %s not found", id) // TODO: typed 245 } 246 inf.Name = key 247 if b != nil { 248 v := b.Get(keyParent) 249 if v != nil { 250 inf.Parent = string(v) 251 return nil 252 } 253 } 254 if l != nil { 255 if p := l.Parent(); p != nil { 256 inf.Parent = p.ChainID().String() 257 } 258 inf.Kind = snapshots.KindCommitted 259 } 260 return nil 261 }); err != nil { 262 return snapshots.Info{}, err 263 } 264 return inf, nil 265 } 266 267 func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountable, error) { 268 l, err := s.getLayer(key, true) 269 if err != nil { 270 return nil, err 271 } 272 if l != nil { 273 id := identity.NewID() 274 var rwlayer layer.RWLayer 275 return &mountable{ 276 idmap: s.opt.IdentityMapping, 277 acquire: func() ([]mount.Mount, func() error, error) { 278 rwlayer, err = s.opt.LayerStore.CreateRWLayer(id, l.ChainID(), nil) 279 if err != nil { 280 return nil, nil, err 281 } 282 rootfs, err := rwlayer.Mount("") 283 if err != nil { 284 return nil, nil, err 285 } 286 return []mount.Mount{{ 287 Source: rootfs.Path(), 288 Type: "bind", 289 Options: []string{"rbind"}, 290 }}, func() error { 291 _, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer) 292 return err 293 }, nil 294 }, 295 }, nil 296 } 297 298 id, _ := s.getGraphDriverID(key) 299 300 return &mountable{ 301 idmap: s.opt.IdentityMapping, 302 acquire: func() ([]mount.Mount, func() error, error) { 303 rootfs, err := s.opt.GraphDriver.Get(id, "") 304 if err != nil { 305 return nil, nil, err 306 } 307 return []mount.Mount{{ 308 Source: rootfs.Path(), 309 Type: "bind", 310 Options: []string{"rbind"}, 311 }}, func() error { 312 return s.opt.GraphDriver.Put(id) 313 }, nil 314 }, 315 }, nil 316 } 317 318 func (s *snapshotter) Remove(ctx context.Context, key string) error { 319 return errors.Errorf("calling snapshot.remove is forbidden") 320 } 321 322 func (s *snapshotter) remove(ctx context.Context, key string) error { 323 l, err := s.getLayer(key, true) 324 if err != nil { 325 return err 326 } 327 328 id, _ := s.getGraphDriverID(key) 329 330 var found bool 331 var alreadyCommitted bool 332 if err := s.db.Update(func(tx *bolt.Tx) error { 333 b := tx.Bucket([]byte(key)) 334 found = b != nil 335 336 if b != nil { 337 if b.Get(keyIsCommitted) != nil { 338 alreadyCommitted = true 339 return nil 340 } 341 } 342 if found { 343 tx.DeleteBucket([]byte(key)) 344 if id != key { 345 tx.DeleteBucket([]byte(id)) 346 } 347 } 348 return nil 349 }); err != nil { 350 return err 351 } 352 353 if alreadyCommitted { 354 return nil 355 } 356 357 if l != nil { 358 s.mu.Lock() 359 delete(s.refs, key) 360 s.mu.Unlock() 361 _, err := s.opt.LayerStore.Release(l) 362 return err 363 } 364 365 if !found { // this happens when removing views 366 return nil 367 } 368 369 return s.opt.GraphDriver.Remove(id) 370 } 371 372 func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { 373 return s.db.Update(func(tx *bolt.Tx) error { 374 b, err := tx.CreateBucketIfNotExists([]byte(name)) 375 if err != nil { 376 return err 377 } 378 if err := b.Put(keyCommitted, []byte(key)); err != nil { 379 return err 380 } 381 b, err = tx.CreateBucketIfNotExists([]byte(key)) 382 if err != nil { 383 return err 384 } 385 if err := b.Put(keyIsCommitted, []byte{}); err != nil { 386 return err 387 } 388 return nil 389 }) 390 } 391 392 func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (snapshot.Mountable, error) { 393 return s.Mounts(ctx, parent) 394 } 395 396 func (s *snapshotter) Walk(context.Context, snapshots.WalkFunc, ...string) error { 397 return errors.Errorf("not-implemented") 398 } 399 400 func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { 401 // not implemented 402 return s.Stat(ctx, info.Name) 403 } 404 405 func (s *snapshotter) Usage(ctx context.Context, key string) (us snapshots.Usage, retErr error) { 406 usage := snapshots.Usage{} 407 if l, err := s.getLayer(key, true); err != nil { 408 return usage, err 409 } else if l != nil { 410 s, err := l.DiffSize() 411 if err != nil { 412 return usage, err 413 } 414 usage.Size = s 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 return m.idmap 536 }