pomerium/internal/autocert/storage_gcs.go
Caleb Doxsey 8d61575ada
autocert: add support for storage in gcs (#3794)
* autocert: add support for storage in s3

* go mod tidy

* skip on mac

* autocert: add support for storage in gcs
2022-12-09 08:22:32 -07:00

137 lines
2.5 KiB
Go

package autocert
import (
"context"
"errors"
"io"
"io/fs"
"cloud.google.com/go/storage"
"github.com/caddyserver/certmagic"
"google.golang.org/api/iterator"
)
type gcsStorage struct {
client *storage.Client
bucket string
prefix string
*locker
}
func newGCSStorage(client *storage.Client, bucket, prefix string) *gcsStorage {
s := &gcsStorage{
client: client,
bucket: bucket,
prefix: prefix,
}
s.locker = &locker{
store: s.Store,
load: s.Load,
delete: s.Delete,
}
return s
}
func (s *gcsStorage) Store(ctx context.Context, key string, value []byte) error {
obj := s.client.
Bucket(s.bucket).
Object(key)
w := obj.NewWriter(ctx)
_, err := w.Write(value)
if err != nil {
_ = w.CloseWithError(err)
return err
}
err = w.Close()
if err != nil {
return err
}
return nil
}
func (s *gcsStorage) Load(ctx context.Context, key string) ([]byte, error) {
r, err := s.client.
Bucket(s.bucket).
Object(key).
NewReader(ctx)
if errors.Is(err, storage.ErrObjectNotExist) {
return nil, fs.ErrNotExist
} else if err != nil {
return nil, err
}
defer r.Close()
return io.ReadAll(r)
}
func (s *gcsStorage) Delete(ctx context.Context, key string) error {
err := s.client.
Bucket(s.bucket).
Object(key).
Delete(ctx)
if errors.Is(err, storage.ErrObjectNotExist) {
return nil
}
return err
}
func (s *gcsStorage) Exists(ctx context.Context, key string) bool {
_, err := s.client.
Bucket(s.bucket).
Object(key).
Attrs(ctx)
return err == nil
}
func (s *gcsStorage) List(ctx context.Context, prefix string, recursive bool) ([]string, error) {
var delimiter string
if !recursive {
delimiter = "/"
}
it := s.client.
Bucket(s.bucket).
Objects(ctx, &storage.Query{
Delimiter: delimiter,
Prefix: prefix,
})
var keys []string
for {
attrs, err := it.Next()
if errors.Is(err, iterator.Done) {
break
} else if err != nil {
return nil, err
}
if attrs.Prefix != "" {
keys = append(keys, attrs.Prefix)
} else {
keys = append(keys, attrs.Name)
}
}
return keys, nil
}
func (s *gcsStorage) Stat(ctx context.Context, key string) (certmagic.KeyInfo, error) {
attrs, err := s.client.
Bucket(s.bucket).
Object(key).
Attrs(ctx)
if errors.Is(err, storage.ErrObjectNotExist) {
return certmagic.KeyInfo{}, fs.ErrNotExist
} else if err != nil {
return certmagic.KeyInfo{}, err
}
return certmagic.KeyInfo{
Key: key,
Modified: attrs.Updated,
Size: attrs.Size,
IsTerminal: true,
}, nil
}