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