github.com/wrgl/wrgl@v0.14.0/pkg/ref/fs/store.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright © 2022 Wrangle Ltd 3 4 package reffs 5 6 import ( 7 "container/list" 8 "fmt" 9 "io" 10 "os" 11 "path" 12 "time" 13 14 "github.com/google/uuid" 15 "github.com/wrgl/wrgl/pkg/ref" 16 ) 17 18 type Store struct { 19 rootDir string 20 } 21 22 func NewStore(rootDir string) *Store { 23 return &Store{ 24 rootDir: rootDir, 25 } 26 } 27 28 func (s *Store) createParentDir(p string) error { 29 return os.MkdirAll(path.Dir(p), 0755) 30 } 31 32 func (s *Store) writeFile(p string, b []byte) error { 33 err := s.createParentDir(p) 34 if err != nil { 35 return err 36 } 37 return os.WriteFile(p, b, 0666) 38 } 39 40 func (s *Store) openFileToAppend(p string) (*os.File, error) { 41 err := s.createParentDir(p) 42 if err != nil { 43 return nil, err 44 } 45 return os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) 46 } 47 48 func (s *Store) refPath(key string) string { 49 return path.Join(s.rootDir, "refs", key) 50 } 51 52 func (s *Store) logPath(key string) string { 53 return path.Join(s.rootDir, "logs", key) 54 } 55 56 func (s *Store) Set(key string, val []byte) error { 57 return s.writeFile(s.refPath(key), val) 58 } 59 60 func (s *Store) SetWithLog(key string, val []byte, log *ref.Reflog) error { 61 f, err := s.openFileToAppend(s.logPath(key)) 62 if err != nil { 63 return err 64 } 65 _, err = log.WriteTo(f) 66 if err != nil { 67 return err 68 } 69 _, err = f.Write([]byte{'\n'}) 70 if err != nil { 71 return err 72 } 73 err = f.Close() 74 if err != nil { 75 return err 76 } 77 return s.Set(key, val) 78 } 79 80 func (s *Store) Get(key string) (val []byte, err error) { 81 val, err = os.ReadFile(s.refPath(key)) 82 if err != nil { 83 return nil, ref.ErrKeyNotFound 84 } 85 return val, nil 86 } 87 88 func (s *Store) Delete(key string) error { 89 err := os.Remove(s.refPath(key)) 90 if err != nil { 91 return err 92 } 93 p := s.logPath(key) 94 if _, err := os.Stat(p); err == nil { 95 return os.Remove(p) 96 } 97 return nil 98 } 99 100 func (s *Store) Filter(prefixes, notPrefixes []string) (m map[string][]byte, err error) { 101 keys, err := s.FilterKey(prefixes, notPrefixes) 102 if err != nil { 103 return 104 } 105 m = map[string][]byte{} 106 for _, k := range keys { 107 b, err := os.ReadFile(s.refPath(k)) 108 if err != nil { 109 return nil, err 110 } 111 m[k] = b 112 } 113 return 114 } 115 116 func (s *Store) FilterKey(prefixes, notPrefixes []string) (keys []string, err error) { 117 prefix := "" 118 if len(prefixes) > 0 { 119 prefix = prefixes[0] 120 } 121 paths := list.New() 122 paths.PushBack(prefix) 123 for e := paths.Front(); e != nil; e = e.Next() { 124 p := e.Value.(string) 125 entries, err := os.ReadDir(s.refPath(p)) 126 if err != nil { 127 if _, ok := err.(*os.PathError); ok { 128 continue 129 } 130 return nil, err 131 } 132 for _, f := range entries { 133 if f.IsDir() { 134 paths.PushBack(path.Join(p, f.Name())) 135 } else { 136 keys = append(keys, path.Join(p, f.Name())) 137 } 138 } 139 } 140 return 141 } 142 143 func (s *Store) Rename(oldKey, newKey string) (err error) { 144 p := s.refPath(newKey) 145 err = s.createParentDir(p) 146 if err != nil { 147 return 148 } 149 err = os.Rename(s.refPath(oldKey), p) 150 if err != nil { 151 return 152 } 153 p = s.logPath(oldKey) 154 if _, err := os.Stat(p); err == nil { 155 q := s.logPath(newKey) 156 err = s.createParentDir(q) 157 if err != nil { 158 return err 159 } 160 return os.Rename(p, q) 161 } 162 return 163 } 164 165 func (s *Store) Copy(srcKey, dstKey string) (err error) { 166 srcRef, err := os.Open(s.refPath(srcKey)) 167 if err != nil { 168 return 169 } 170 defer srcRef.Close() 171 p := s.refPath(dstKey) 172 err = s.createParentDir(p) 173 if err != nil { 174 return 175 } 176 dstRef, err := os.Create(p) 177 if err != nil { 178 return 179 } 180 defer dstRef.Close() 181 _, err = io.Copy(dstRef, srcRef) 182 if err != nil { 183 return 184 } 185 186 srcLog, err := os.Open(s.logPath(srcKey)) 187 if err != nil { 188 return 189 } 190 defer srcLog.Close() 191 p = s.logPath(dstKey) 192 err = s.createParentDir(p) 193 if err != nil { 194 return 195 } 196 dstLog, err := os.Create(p) 197 if err != nil { 198 return 199 } 200 defer dstLog.Close() 201 _, err = io.Copy(dstLog, srcLog) 202 return err 203 } 204 205 func (s *Store) LogReader(key string) (ref.ReflogReader, error) { 206 f, err := os.Open(s.logPath(key)) 207 if err != nil { 208 return nil, ref.ErrKeyNotFound 209 } 210 return NewReflogReader(f) 211 } 212 213 func (s *Store) NewTransaction(tx *ref.Transaction) (*uuid.UUID, error) { 214 return nil, fmt.Errorf("not implemented") 215 } 216 217 func (s *Store) GetTransaction(id uuid.UUID) (*ref.Transaction, error) { 218 return nil, fmt.Errorf("not implemented") 219 } 220 221 func (s *Store) UpdateTransaction(tx *ref.Transaction) error { 222 return fmt.Errorf("not implemented") 223 } 224 225 func (s *Store) DeleteTransaction(id uuid.UUID) error { 226 return fmt.Errorf("not implemented") 227 } 228 229 func (s *Store) GCTransactions(txTTL time.Duration) (ids []uuid.UUID, err error) { 230 return nil, fmt.Errorf("not implemented") 231 } 232 233 func (s *Store) GetTransactionLogs(txid uuid.UUID) (logs map[string]*ref.Reflog, err error) { 234 return nil, fmt.Errorf("not implemented") 235 } 236 237 func (s *Store) ListTransactions(offset, limit int) (txs []*ref.Transaction, err error) { 238 return nil, fmt.Errorf("not implemented") 239 }