snapshot.go 12 KB

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