diff --git a/storage.go b/storage.go index e2bfe22..5a5bc87 100644 --- a/storage.go +++ b/storage.go @@ -33,6 +33,9 @@ func getStorageByURI(uri string) (storage, error) { // NoOp storage: Only logs requests return logStorage{}, nil + case "s3": + return s3StorageFromURI(uri), nil + default: return nil, fmt.Errorf("Storage scheme %q not defined", u.Scheme) } diff --git a/storage_s3.go b/storage_s3.go new file mode 100644 index 0000000..66e7255 --- /dev/null +++ b/storage_s3.go @@ -0,0 +1,69 @@ +package main + +import ( + "bytes" + "io" + "io/ioutil" + "net/url" + "path" + + "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 s3Storage struct { + bucket string + prefix string +} + +func s3StorageFromURI(uri string) s3Storage { + u, _ := url.Parse(uri) + + return s3Storage{ + bucket: u.Host, + prefix: u.Path, + } +} + +// WriteFile takes the content of a file and writes it into the underlying +// storage system. +func (s s3Storage) WriteFile(filepath string, content io.Reader, contentType string) error { + sess := session.Must(session.NewSession()) + svc := s3.New(sess) + + body, err := ioutil.ReadAll(content) + if err != nil { + return err + } + + _, err = svc.PutObject(&s3.PutObjectInput{ + Body: bytes.NewReader(body), + Bucket: aws.String(s.bucket), + ContentType: aws.String(contentType), + Key: aws.String(path.Join(s.prefix, filepath)), + }) + + return err +} + +// ReadFile retrieves a file from the underlying storage, needs to return +// errFileNotFound when file is not present. +func (s s3Storage) ReadFile(filepath string) (io.ReadCloser, error) { + sess := session.Must(session.NewSession()) + svc := s3.New(sess) + + res, err := svc.GetObject(&s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(path.Join(s.prefix, filepath)), + }) + if err != nil { + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == s3.ErrCodeNoSuchKey { + return nil, errFileNotFound + } + return nil, err + } + + return res.Body, nil +}