github.com/anuvu/zot@v1.3.4/pkg/storage/cache.go (about) 1 package storage 2 3 import ( 4 "path" 5 "path/filepath" 6 "strings" 7 "time" 8 9 "github.com/anuvu/zot/errors" 10 zlog "github.com/anuvu/zot/pkg/log" 11 "go.etcd.io/bbolt" 12 ) 13 14 const ( 15 BlobsCache = "blobs" 16 dbCacheLockCheckTimeout = 10 * time.Second 17 ) 18 19 type Cache struct { 20 rootDir string 21 db *bbolt.DB 22 log zlog.Logger 23 } 24 25 // Blob is a blob record. 26 type Blob struct { 27 Path string 28 } 29 30 func NewCache(rootDir string, name string, log zlog.Logger) *Cache { 31 dbPath := path.Join(rootDir, name+".db") 32 dbOpts := &bbolt.Options{ 33 Timeout: dbCacheLockCheckTimeout, 34 FreelistType: bbolt.FreelistArrayType, 35 } 36 db, err := bbolt.Open(dbPath, 0600, dbOpts) 37 38 if err != nil { 39 log.Error().Err(err).Str("dbPath", dbPath).Msg("unable to create cache db") 40 return nil 41 } 42 43 if err := db.Update(func(tx *bbolt.Tx) error { 44 if _, err := tx.CreateBucketIfNotExists([]byte(BlobsCache)); err != nil { 45 // this is a serious failure 46 log.Error().Err(err).Str("dbPath", dbPath).Msg("unable to create a root bucket") 47 return err 48 } 49 return nil 50 }); err != nil { 51 // something went wrong 52 log.Error().Err(err).Msg("unable to create a cache") 53 return nil 54 } 55 56 return &Cache{rootDir: rootDir, db: db, log: log} 57 } 58 59 func (c *Cache) PutBlob(digest string, path string) error { 60 if path == "" { 61 c.log.Error().Err(errors.ErrEmptyValue).Str("digest", digest).Msg("empty path provided") 62 63 return errors.ErrEmptyValue 64 } 65 66 // use only relative (to rootDir) paths on blobs 67 relp, err := filepath.Rel(c.rootDir, path) 68 if err != nil { 69 c.log.Error().Err(err).Str("path", path).Msg("unable to get relative path") 70 } 71 72 if err := c.db.Update(func(tx *bbolt.Tx) error { 73 root := tx.Bucket([]byte(BlobsCache)) 74 if root == nil { 75 // this is a serious failure 76 err := errors.ErrCacheRootBucket 77 c.log.Error().Err(err).Msg("unable to access root bucket") 78 return err 79 } 80 b, err := root.CreateBucketIfNotExists([]byte(digest)) 81 if err != nil { 82 // this is a serious failure 83 c.log.Error().Err(err).Str("bucket", digest).Msg("unable to create a bucket") 84 return err 85 } 86 if err := b.Put([]byte(relp), nil); err != nil { 87 c.log.Error().Err(err).Str("bucket", digest).Str("value", relp).Msg("unable to put record") 88 return err 89 } 90 return nil 91 }); err != nil { 92 return err 93 } 94 95 return nil 96 } 97 98 func (c *Cache) GetBlob(digest string) (string, error) { 99 var blobPath strings.Builder 100 101 if err := c.db.View(func(tx *bbolt.Tx) error { 102 root := tx.Bucket([]byte(BlobsCache)) 103 if root == nil { 104 // this is a serious failure 105 err := errors.ErrCacheRootBucket 106 c.log.Error().Err(err).Msg("unable to access root bucket") 107 return err 108 } 109 110 b := root.Bucket([]byte(digest)) 111 if b != nil { 112 // get first key 113 c := b.Cursor() 114 k, _ := c.First() 115 blobPath.WriteString(string(k)) 116 return nil 117 } 118 119 return errors.ErrCacheMiss 120 }); err != nil { 121 return "", err 122 } 123 124 return blobPath.String(), nil 125 } 126 127 func (c *Cache) HasBlob(digest string, blob string) bool { 128 if err := c.db.View(func(tx *bbolt.Tx) error { 129 root := tx.Bucket([]byte(BlobsCache)) 130 if root == nil { 131 // this is a serious failure 132 err := errors.ErrCacheRootBucket 133 c.log.Error().Err(err).Msg("unable to access root bucket") 134 return err 135 } 136 137 b := root.Bucket([]byte(digest)) 138 if b == nil { 139 return errors.ErrCacheMiss 140 } 141 if b.Get([]byte(blob)) == nil { 142 return errors.ErrCacheMiss 143 } 144 145 return nil 146 }); err != nil { 147 return false 148 } 149 150 return true 151 } 152 153 func (c *Cache) DeleteBlob(digest string, path string) error { 154 // use only relative (to rootDir) paths on blobs 155 relp, err := filepath.Rel(c.rootDir, path) 156 if err != nil { 157 c.log.Error().Err(err).Str("path", path).Msg("unable to get relative path") 158 } 159 160 if err := c.db.Update(func(tx *bbolt.Tx) error { 161 root := tx.Bucket([]byte(BlobsCache)) 162 if root == nil { 163 // this is a serious failure 164 err := errors.ErrCacheRootBucket 165 c.log.Error().Err(err).Msg("unable to access root bucket") 166 return err 167 } 168 169 b := root.Bucket([]byte(digest)) 170 if b == nil { 171 return errors.ErrCacheMiss 172 } 173 174 if err := b.Delete([]byte(relp)); err != nil { 175 c.log.Error().Err(err).Str("digest", digest).Str("path", relp).Msg("unable to delete") 176 return err 177 } 178 179 cur := b.Cursor() 180 k, _ := cur.First() 181 182 if k == nil { 183 c.log.Debug().Str("digest", digest).Str("path", relp).Msg("deleting empty bucket") 184 if err := root.DeleteBucket([]byte(digest)); err != nil { 185 c.log.Error().Err(err).Str("digest", digest).Str("path", relp).Msg("unable to delete") 186 return err 187 } 188 } 189 190 return nil 191 }); err != nil { 192 return err 193 } 194 195 return nil 196 }