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  }