github.com/btccom/go-micro/v2@v2.9.3/api/server/acme/certmagic/storage.go (about) 1 package certmagic 2 3 import ( 4 "bytes" 5 "encoding/gob" 6 "errors" 7 "fmt" 8 "path" 9 "strings" 10 "time" 11 12 "github.com/caddyserver/certmagic" 13 "github.com/btccom/go-micro/v2/store" 14 "github.com/btccom/go-micro/v2/sync" 15 ) 16 17 // File represents a "File" that will be stored in store.Store - the contents and last modified time 18 type File struct { 19 // last modified time 20 LastModified time.Time 21 // Contents 22 Contents []byte 23 } 24 25 // storage is an implementation of certmagic.Storage using micro's sync.Map and store.Store interfaces. 26 // As certmagic storage expects a filesystem (with stat() abilities) we have to implement 27 // the bare minimum of metadata. 28 type storage struct { 29 lock sync.Sync 30 store store.Store 31 } 32 33 func (s *storage) Lock(key string) error { 34 return s.lock.Lock(key, sync.LockTTL(10*time.Minute)) 35 } 36 37 func (s *storage) Unlock(key string) error { 38 return s.lock.Unlock(key) 39 } 40 41 func (s *storage) Store(key string, value []byte) error { 42 f := File{ 43 LastModified: time.Now(), 44 Contents: value, 45 } 46 buf := &bytes.Buffer{} 47 e := gob.NewEncoder(buf) 48 if err := e.Encode(f); err != nil { 49 return err 50 } 51 r := &store.Record{ 52 Key: key, 53 Value: buf.Bytes(), 54 } 55 return s.store.Write(r) 56 } 57 58 func (s *storage) Load(key string) ([]byte, error) { 59 if !s.Exists(key) { 60 return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist")) 61 } 62 records, err := s.store.Read(key) 63 if err != nil { 64 return nil, err 65 } 66 if len(records) != 1 { 67 return nil, fmt.Errorf("ACME Storage: multiple records matched key %s", key) 68 } 69 b := bytes.NewBuffer(records[0].Value) 70 d := gob.NewDecoder(b) 71 var f File 72 err = d.Decode(&f) 73 if err != nil { 74 return nil, err 75 } 76 return f.Contents, nil 77 } 78 79 func (s *storage) Delete(key string) error { 80 return s.store.Delete(key) 81 } 82 83 func (s *storage) Exists(key string) bool { 84 if _, err := s.store.Read(key); err != nil { 85 return false 86 } 87 return true 88 } 89 90 func (s *storage) List(prefix string, recursive bool) ([]string, error) { 91 keys, err := s.store.List() 92 if err != nil { 93 return nil, err 94 } 95 96 //nolint:prealloc 97 var results []string 98 for _, k := range keys { 99 if strings.HasPrefix(k, prefix) { 100 results = append(results, k) 101 } 102 } 103 if recursive { 104 return results, nil 105 } 106 keysMap := make(map[string]bool) 107 for _, key := range results { 108 dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/") 109 keysMap[dir[0]] = true 110 } 111 results = make([]string, 0) 112 for k := range keysMap { 113 results = append(results, path.Join(prefix, k)) 114 } 115 return results, nil 116 } 117 118 func (s *storage) Stat(key string) (certmagic.KeyInfo, error) { 119 records, err := s.store.Read(key) 120 if err != nil { 121 return certmagic.KeyInfo{}, err 122 } 123 if len(records) != 1 { 124 return certmagic.KeyInfo{}, fmt.Errorf("ACME Storage: multiple records matched key %s", key) 125 } 126 b := bytes.NewBuffer(records[0].Value) 127 d := gob.NewDecoder(b) 128 var f File 129 err = d.Decode(&f) 130 if err != nil { 131 return certmagic.KeyInfo{}, err 132 } 133 return certmagic.KeyInfo{ 134 Key: key, 135 Modified: f.LastModified, 136 Size: int64(len(f.Contents)), 137 IsTerminal: false, 138 }, nil 139 } 140 141 // NewStorage returns a certmagic.Storage backed by a go-micro/lock and go-micro/store 142 func NewStorage(lock sync.Sync, store store.Store) certmagic.Storage { 143 return &storage{ 144 lock: lock, 145 store: store, 146 } 147 }