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 support for PreSigned URLs #1

Merged
merged 5 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions azure/container.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package azure

import (
"context"
"fmt"
"io"
"strings"
"time"
Expand Down Expand Up @@ -32,6 +34,11 @@ func (c *container) Name() string {
return c.id
}

func (c *container) PreSignRequest(_ context.Context, _ stow.ClientMethod, _ string,
_ stow.PresignRequestParams) (url string, err error) {
return "", fmt.Errorf("unsupported")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if azure won't work, does this mean we're going to have to have logic in flytectl to detect if we're trying to use azure storage, and then switch back to using the current logic?

Copy link
Author

@EngHabu EngHabu Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented. Tested too

}

func (c *container) Item(id string) (stow.Item, error) {
blob := c.client.GetContainerReference(c.id).GetBlobReference(id)
err := blob.GetProperties(nil)
Expand Down
7 changes: 7 additions & 0 deletions b2/container.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package b2

import (
"context"
"fmt"
"io"
"strings"
"time"
Expand All @@ -24,6 +26,11 @@ func (c *container) ID() string {
return c.bucket.Name
}

func (c *container) PreSignRequest(_ context.Context, _ stow.ClientMethod, _ string,
_ stow.PresignRequestParams) (url string, err error) {
return "", fmt.Errorf("unsupported")
}

// Name returns the name of the bucket
func (c *container) Name() string {
return c.bucket.Name
Expand Down
68 changes: 68 additions & 0 deletions clientmethod_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 20 additions & 1 deletion google/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package google
import (
"context"
"io"
"net/http"
"time"

"cloud.google.com/go/storage"
"github.com/pkg/errors"
Expand Down Expand Up @@ -33,10 +35,27 @@ func (c *Container) Name() string {
}

// Bucket returns the google bucket attributes
func (c *Container) Bucket() *storage.BucketHandle{
func (c *Container) Bucket() *storage.BucketHandle {
return c.client.Bucket(c.name)
}

func (c *Container) PreSignRequest(_ context.Context, clientMethod stow.ClientMethod, id string,
params stow.PresignRequestParams) (url string, err error) {
if len(params.HttpMethod) == 0 {
switch clientMethod {
case stow.ClientMethodGet:
params.HttpMethod = http.MethodGet
case stow.ClientMethodPut:
params.HttpMethod = http.MethodPut
}
}

return storage.SignedURL(c.name, id, &storage.SignedURLOptions{
Method: params.HttpMethod,
Expires: time.Now().Add(params.ExpiresIn),
})
}

// Item returns a stow.Item instance of a container based on the
// name of the container
func (c *Container) Item(id string) (stow.Item, error) {
Expand Down
7 changes: 7 additions & 0 deletions local/container.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package local

import (
"context"
"errors"
"fmt"
"io"
"net/url"
"os"
Expand Down Expand Up @@ -31,6 +33,11 @@ func (c *container) URL() *url.URL {
}
}

func (c *container) PreSignRequest(_ context.Context, _ stow.ClientMethod, _ string,
_ stow.PresignRequestParams) (url string, err error) {
return "", fmt.Errorf("unsupported")
}

func (c *container) CreateItem(name string) (stow.Item, io.WriteCloser, error) {
path := filepath.Join(c.path, filepath.FromSlash(name))
item := &item{
Expand Down
7 changes: 7 additions & 0 deletions oracle/container.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package oracle

import (
"context"
"fmt"
"io"
"strings"

Expand All @@ -16,6 +18,11 @@ type container struct {

var _ stow.Container = (*container)(nil)

func (c *container) PreSignRequest(_ context.Context, _ stow.ClientMethod, _ string,
_ stow.PresignRequestParams) (url string, err error) {
return "", fmt.Errorf("unsupported")
}

// ID returns a string value representing a unique container, in this case it's
// the Container's name.
func (c *container) ID() string {
Expand Down
28 changes: 28 additions & 0 deletions s3/container.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package s3

import (
"context"
"fmt"
"io"
"strings"

"github.com/aws/aws-sdk-go/aws/request"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/s3"
Expand All @@ -23,6 +27,30 @@ type container struct {
customEndpoint string
}

func (c *container) PreSignRequest(ctx context.Context, clientMethod stow.ClientMethod, id string,
params stow.PresignRequestParams) (url string, err error) {

var req *request.Request
switch clientMethod {
case stow.ClientMethodGet:
req, _ = c.client.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(c.name),
Key: aws.String(id),
})
case stow.ClientMethodPut:
req, _ = c.client.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(c.name),
Key: aws.String(id),
})
default:
return "", fmt.Errorf("unsupported client method [%v]", clientMethod.String())
}

req.SetContext(ctx)

return req.Presign(params.ExpiresIn)
}

// ID returns a string value which represents the name of the container.
func (c *container) ID() string {
return c.name
Expand Down
34 changes: 34 additions & 0 deletions s3/stow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package s3

import (
"context"
"encoding/json"
"fmt"
"net/http"
Expand All @@ -11,6 +12,9 @@ import (
"reflect"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
Expand All @@ -37,6 +41,36 @@ func TestStow(t *testing.T) {
test.All(t, "s3", config)
}

func TestPreSignedURL(t *testing.T) {
is := is.New(t)
accessKeyId := os.Getenv("S3ACCESSKEYID")
secretKey := os.Getenv("S3SECRETKEY")
region := os.Getenv("S3REGION")

if accessKeyId == "" || secretKey == "" || region == "" {
t.Skip("skipping test because missing one or more of S3ACCESSKEYID S3SECRETKEY S3REGION")
}

config := stow.ConfigMap{
"access_key_id": accessKeyId,
"secret_key": secretKey,
"region": region,
}

location, err := stow.Dial("s3", config)
is.NoErr(err)

container, err := location.Container("flyte-demo")
ctx := context.Background()
res, err := container.PreSignRequest(ctx, stow.ClientMethodPut, "blah/bloh/fileon", stow.PresignRequestParams{
ExpiresIn: time.Hour,
})

is.NoErr(err)
t.Log(res)
assert.NotEmpty(t, res)
}

func TestEtagCleanup(t *testing.T) {
etagValue := "9c51403a2255f766891a1382288dece4"
permutations := []string{
Expand Down
2 changes: 1 addition & 1 deletion s3/v2signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,4 @@ const logSignInfoMsg = `DEBUG: Request Signature:
func (v2 *signer) logSigningInfo() {
msg := fmt.Sprintf(logSignInfoMsg, v2.stringToSign, v2.signature)
v2.Logger.Log(msg)
}
}
7 changes: 7 additions & 0 deletions sftp/container.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package sftp

import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
Expand All @@ -25,6 +27,11 @@ func (c *container) Name() string {
return c.name
}

func (c *container) PreSignRequest(_ context.Context, _ stow.ClientMethod, _ string,
_ stow.PresignRequestParams) (url string, err error) {
return "", fmt.Errorf("unsupported")
}

// Item returns a stow.Item instance of a container based on the name of the
// container and the file.
func (c *container) Item(id string) (stow.Item, error) {
Expand Down
23 changes: 23 additions & 0 deletions stow.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stow

import (
"context"
"errors"
"io"
"net/url"
Expand Down Expand Up @@ -45,6 +46,19 @@ var (
NoPrefix = ""
)

// HttpMethod defines an alias type for string to represent http Methods. These are defined in RFC 7231 section 4.3.
type HttpMethod = string

//go:generate enumer --type=ClientMethod --trimprefix=ClientMethod -json

// ClientMethod defines common client methods across storage providers
type ClientMethod int

const (
ClientMethodGet ClientMethod = iota
ClientMethodPut
)

// IsCursorEnd checks whether the cursor indicates there are no
// more items or not.
func IsCursorEnd(cursor string) bool {
Expand Down Expand Up @@ -77,6 +91,12 @@ type Location interface {
ItemByURL(url *url.URL) (Item, error)
}

type PresignRequestParams struct {
ExpiresIn time.Duration
ExtraParams map[string]interface{}
HttpMethod HttpMethod
}

// Container represents a container.
type Container interface {
// ID gets a unique string describing this Container.
Expand All @@ -100,6 +120,9 @@ type Container interface {
// Put creates a new Item with the specified name, and contents
// read from the reader.
Put(name string, r io.Reader, size int64, metadata map[string]interface{}) (Item, error)

// PreSignRequest generates a pre-signed url for the given id (key after bucket/container) and a given clientMethod.
PreSignRequest(ctx context.Context, clientMethod ClientMethod, id string, params PresignRequestParams) (url string, err error)
}

// Item represents an item inside a Container.
Expand Down
7 changes: 7 additions & 0 deletions swift/container.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package swift

import (
"context"
"fmt"
"io"
"strings"

Expand All @@ -25,6 +27,11 @@ func (c *container) Name() string {
return c.id
}

func (c *container) PreSignRequest(_ context.Context, _ stow.ClientMethod, _ string,
_ stow.PresignRequestParams) (url string, err error) {
return "", fmt.Errorf("unsupported")
}

func (c *container) Item(id string) (stow.Item, error) {
return c.getItem(id)
}
Expand Down