Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add minio support via http #147

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ enabled using the `-cache` flag. It supports the following values:
environmental variables be set. (Additional methods of loading credentials
are documented in the [aws-sdk-go session
package](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/)).
- http (Minio) URL (e.g. `http://endpoint/region/bucket/optional-path-prefix`) - will cache
images on Minio S3. This requires `AWS_ACCESS_KEY_ID` and `AWS_SECRET_KEY`
environmental variables be set. (Additional methods of loading credentials
are documented in the [aws-sdk-go session
package](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/)).
- gcs URL (e.g. `gcs://bucket-name/optional-path-prefix`) - will cache images
on Google Cloud Storage. Authentication is documented in Google's
[Application Default Credentials
Expand Down
3 changes: 3 additions & 0 deletions cmd/imageproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/peterbourgon/diskv"
"willnorris.com/go/imageproxy"
"willnorris.com/go/imageproxy/internal/gcscache"
"willnorris.com/go/imageproxy/internal/miniocache"
"willnorris.com/go/imageproxy/internal/s3cache"
)

Expand Down Expand Up @@ -153,6 +154,8 @@ func parseCache(c string) (imageproxy.Cache, error) {
return rediscache.NewWithClient(conn), nil
case "s3":
return s3cache.New(u.String())
case "http":
return miniocache.New(u.String())
case "file":
fallthrough
default:
Expand Down
120 changes: 120 additions & 0 deletions internal/miniocache/miniocache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Package miniocache provides an httpcache.Cache implementation that stores
// cached values on Minio S3.
package miniocache

import (
"bytes"
"crypto/md5"
"encoding/hex"
"io"
"io/ioutil"
"log"
"net/url"
"path"
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)

type cache struct {
*s3.S3
bucket, prefix string
}

func (c *cache) Get(key string) ([]byte, bool) {
key = path.Join(c.prefix, keyToFilename(key))
input := &s3.GetObjectInput{
Bucket: &c.bucket,
Key: &key,
}

resp, err := c.GetObject(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok && aerr.Code() != "NoSuchKey" {
log.Printf("error fetching from minio: %v", aerr)
}
return nil, false
}

value, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("error reading minio response body: %v", err)
return nil, false
}

return value, true
}
func (c *cache) Set(key string, value []byte) {
key = path.Join(c.prefix, keyToFilename(key))
input := &s3.PutObjectInput{
Body: aws.ReadSeekCloser(bytes.NewReader(value)),
Bucket: &c.bucket,
Key: &key,
}

_, err := c.PutObject(input)
if err != nil {
log.Printf("error writing to minio: %v", err)
}
}
func (c *cache) Delete(key string) {
key = path.Join(c.prefix, keyToFilename(key))
input := &s3.DeleteObjectInput{
Bucket: &c.bucket,
Key: &key,
}

_, err := c.DeleteObject(input)
if err != nil {
log.Printf("error deleting from minio: %v", err)
}
}

func keyToFilename(key string) string {
h := md5.New()
io.WriteString(h, key)
return hex.EncodeToString(h.Sum(nil))
}

// New constructs a cache configured using the provided URL string. URL should
// be of the form: "http://endpoint/region/bucket/optional-path-prefix". Credentials
// should be specified using one of the mechanisms supported by aws-sdk-go (see
// https://docs.aws.amazon.com/sdk-for-go/api/aws/session/).
func New(s string) (*cache, error) {
u, err := url.Parse(s)
if err != nil {
return nil, err
}

endpoint := u.Host
path := strings.SplitN(strings.TrimPrefix(u.Path, "/"), "/", 2)
region := path[0]
bucket := path[1]

var prefix string
if len(path) > 1 {
prefix = path[2]
}

// Configure to use Minio Server
s3Config := &aws.Config{
Endpoint: aws.String(endpoint),
Region: aws.String(region),
DisableSSL: aws.Bool(true),
S3ForcePathStyle: aws.Bool(true),
}

sess, err := session.NewSession(s3Config)
if err != nil {
return nil, err
}

return &cache{
S3: s3.New(sess),
bucket: bucket,
prefix: prefix,
}, nil
}