github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/acme/certmagic/storage.go (about)

     1  // Copyright 2020 Asim Aslam
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // Original source: github.com/micro/go-micro/v3/api/server/acme/certmagic/storage.go
    16  
    17  package certmagic
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/gob"
    22  	"errors"
    23  	"fmt"
    24  	"path"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/caddyserver/certmagic"
    29  	"github.com/tickoalcantara12/micro/v3/service/store"
    30  	"github.com/tickoalcantara12/micro/v3/util/sync"
    31  )
    32  
    33  // File represents a "File" that will be stored in store.Store - the contents and last modified time
    34  type File struct {
    35  	// last modified time
    36  	LastModified time.Time
    37  	// Contents
    38  	Contents []byte
    39  }
    40  
    41  // storage is an implementation of certmagic.Storage using micro's sync.Map and store.Store interfaces.
    42  // As certmagic storage expects a filesystem (with stat() abilities) we have to implement
    43  // the bare minimum of metadata.
    44  type storage struct {
    45  	lock  sync.Sync
    46  	store store.Store
    47  }
    48  
    49  func (s *storage) Lock(key string) error {
    50  	return s.lock.Lock(key, sync.LockTTL(10*time.Minute))
    51  }
    52  
    53  func (s *storage) Unlock(key string) error {
    54  	return s.lock.Unlock(key)
    55  }
    56  
    57  func (s *storage) Store(key string, value []byte) error {
    58  	f := File{
    59  		LastModified: time.Now(),
    60  		Contents:     value,
    61  	}
    62  	buf := &bytes.Buffer{}
    63  	e := gob.NewEncoder(buf)
    64  	if err := e.Encode(f); err != nil {
    65  		return err
    66  	}
    67  	r := &store.Record{
    68  		Key:   key,
    69  		Value: buf.Bytes(),
    70  	}
    71  	return s.store.Write(r)
    72  }
    73  
    74  func (s *storage) Load(key string) ([]byte, error) {
    75  	if !s.Exists(key) {
    76  		return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist"))
    77  	}
    78  	records, err := s.store.Read(key)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	if len(records) != 1 {
    83  		return nil, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
    84  	}
    85  	b := bytes.NewBuffer(records[0].Value)
    86  	d := gob.NewDecoder(b)
    87  	var f File
    88  	err = d.Decode(&f)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	return f.Contents, nil
    93  }
    94  
    95  func (s *storage) Delete(key string) error {
    96  	return s.store.Delete(key)
    97  }
    98  
    99  func (s *storage) Exists(key string) bool {
   100  	if _, err := s.store.Read(key); err != nil {
   101  		return false
   102  	}
   103  	return true
   104  }
   105  
   106  func (s *storage) List(prefix string, recursive bool) ([]string, error) {
   107  	keys, err := s.store.List()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	//nolint:prealloc
   113  	var results []string
   114  	for _, k := range keys {
   115  		if strings.HasPrefix(k, prefix) {
   116  			results = append(results, k)
   117  		}
   118  	}
   119  	if recursive {
   120  		return results, nil
   121  	}
   122  	keysMap := make(map[string]bool)
   123  	for _, key := range results {
   124  		dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
   125  		keysMap[dir[0]] = true
   126  	}
   127  	results = make([]string, 0)
   128  	for k := range keysMap {
   129  		results = append(results, path.Join(prefix, k))
   130  	}
   131  	return results, nil
   132  }
   133  
   134  func (s *storage) Stat(key string) (certmagic.KeyInfo, error) {
   135  	records, err := s.store.Read(key)
   136  	if err != nil {
   137  		return certmagic.KeyInfo{}, err
   138  	}
   139  	if len(records) != 1 {
   140  		return certmagic.KeyInfo{}, fmt.Errorf("ACME Storage: multiple records matched key %s", key)
   141  	}
   142  	b := bytes.NewBuffer(records[0].Value)
   143  	d := gob.NewDecoder(b)
   144  	var f File
   145  	err = d.Decode(&f)
   146  	if err != nil {
   147  		return certmagic.KeyInfo{}, err
   148  	}
   149  	return certmagic.KeyInfo{
   150  		Key:        key,
   151  		Modified:   f.LastModified,
   152  		Size:       int64(len(f.Contents)),
   153  		IsTerminal: false,
   154  	}, nil
   155  }
   156  
   157  // NewStorage returns a certmagic.Storage backed by a go-micro/lock and go-micro/store
   158  func NewStorage(lock sync.Sync, store store.Store) certmagic.Storage {
   159  	return &storage{
   160  		lock:  lock,
   161  		store: store,
   162  	}
   163  }