2021-03-10 15:24:13 -05:00
|
|
|
// Copyright 2013 The imageproxy authors.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2018-06-02 11:44:00 -05:00
|
|
|
// Package gcscache provides an httpcache.Cache implementation that stores
|
2018-02-02 04:56:25 -05:00
|
|
|
// cached values on Google Cloud Storage.
|
|
|
|
package gcscache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/md5"
|
|
|
|
"encoding/hex"
|
2022-01-22 13:29:37 -05:00
|
|
|
"errors"
|
2018-02-02 04:56:25 -05:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"path"
|
|
|
|
|
|
|
|
"cloud.google.com/go/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ctx = context.Background()
|
|
|
|
|
|
|
|
type cache struct {
|
|
|
|
bucket *storage.BucketHandle
|
|
|
|
prefix string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cache) Get(key string) ([]byte, bool) {
|
|
|
|
r, err := c.object(key).NewReader(ctx)
|
|
|
|
if err != nil {
|
2022-01-22 13:29:37 -05:00
|
|
|
if !errors.Is(err, storage.ErrObjectNotExist) {
|
2018-02-02 04:56:25 -05:00
|
|
|
log.Printf("error reading from gcs: %v", err)
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
defer r.Close()
|
|
|
|
|
2023-01-31 23:30:38 -05:00
|
|
|
value, err := io.ReadAll(r)
|
2018-02-02 04:56:25 -05:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("error reading from gcs: %v", err)
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
return value, true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cache) Set(key string, value []byte) {
|
|
|
|
w := c.object(key).NewWriter(ctx)
|
|
|
|
if _, err := w.Write(value); err != nil {
|
|
|
|
log.Printf("error writing to gcs: %v", err)
|
|
|
|
}
|
|
|
|
if err := w.Close(); err != nil {
|
|
|
|
log.Printf("error closing gcs object writer: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cache) Delete(key string) {
|
|
|
|
if err := c.object(key).Delete(ctx); err != nil {
|
|
|
|
log.Printf("error deleting gcs object: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cache) object(key string) *storage.ObjectHandle {
|
|
|
|
name := path.Join(c.prefix, keyToFilename(key))
|
|
|
|
return c.bucket.Object(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func keyToFilename(key string) string {
|
|
|
|
h := md5.New()
|
2020-06-19 20:08:35 -05:00
|
|
|
_, _ = io.WriteString(h, key)
|
2018-02-02 04:56:25 -05:00
|
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
// New constructs a Cache storing files in the specified GCS bucket. If prefix
|
|
|
|
// is not empty, objects will be prefixed with that path. Credentials should
|
|
|
|
// be specified using one of the mechanisms supported for Application Default
|
|
|
|
// Credentials (see https://cloud.google.com/docs/authentication/production)
|
|
|
|
func New(bucket, prefix string) (*cache, error) {
|
|
|
|
client, err := storage.NewClient(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &cache{
|
|
|
|
prefix: prefix,
|
|
|
|
bucket: client.Bucket(bucket),
|
|
|
|
}, nil
|
|
|
|
}
|