Skip to content
Merged
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
31 changes: 26 additions & 5 deletions cmd/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ package cmd
import (
"fmt"
"io"
"net/http"
"os"
"reflect"

v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/gabriel-vasile/mimetype"
"github.com/outscale/octl/pkg/builder"
Expand Down Expand Up @@ -51,8 +53,12 @@ func init() {
objectCmd, _ := lo.Find(storageCmd.Commands(), func(c *cobra.Command) bool { return c.Name() == "object" })
objectCmd.AddCommand(presignCmd)
presignCmd.Flags().String("bucket", "", "bucket")
presignCmd.Flags().Duration("expires", 0, "URL expiration (e.g. 30s, 1h)")
_ = presignCmd.MarkFlagRequired("bucket")
presignCmd.Flags().Duration("expires", 0, "URL expiration (e.g. 30s, 1h)")
presignCmd.Flags().String("method", http.MethodGet, "Method used to access the presigned URL (GET, PUT, DELETE)")
_ = presignCmd.RegisterFlagCompletionFunc("method", func(_ *cobra.Command, _ []string, _ string) ([]cobra.Completion, cobra.ShellCompDirective) {
return []cobra.Completion{http.MethodGet, http.MethodPut, http.MethodDelete}, cobra.ShellCompDirectiveDefault
})

runner.RegisterHook("auto-content-type", guessContentType)
}
Expand Down Expand Up @@ -92,10 +98,25 @@ func presign(cmd *cobra.Command, args []string) {
}
key := args[0]
bucket, _ := cmd.Flags().GetString("bucket")
req, err := cl.PresignGetObject(cmd.Context(), &s3.GetObjectInput{
Key: new(key),
Bucket: new(bucket),
}, opts...)
method, _ := cmd.Flags().GetString("method")
var req *v4.PresignedHTTPRequest
switch method {
case http.MethodGet:
req, err = cl.PresignGetObject(cmd.Context(), &s3.GetObjectInput{
Key: new(key),
Bucket: new(bucket),
}, opts...)
case http.MethodPut:
req, err = cl.PresignPutObject(cmd.Context(), &s3.PutObjectInput{
Key: new(key),
Bucket: new(bucket),
}, opts...)
case http.MethodDelete:
req, err = cl.PresignDeleteObject(cmd.Context(), &s3.DeleteObjectInput{
Key: new(key),
Bucket: new(bucket),
}, opts...)
}
if err != nil {
messages.ExitErr(err)
}
Expand Down
57 changes: 46 additions & 11 deletions cmd/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,21 +511,19 @@ func TestObjectTagging(t *testing.T) {
})
}

func TestPResign(t *testing.T) {
func TestPresign(t *testing.T) {
sum := sha1.Sum([]byte(t.TempDir()))
bucket := hex.EncodeToString(sum[:])

object := "object.txt"
path := file(t, object, hello)

t.Run("Presigning URL works", func(t *testing.T) {
_ = run(t, []string{"storage", "bucket", "create", "--bucket", bucket}, nil)
defer func() {
_ = run(t, []string{"storage", "bucket", "del", bucket, "-y"}, nil)
}()
_ = run(t, []string{"storage", "bucket", "create", "--bucket", bucket}, nil)
defer func() {
_ = run(t, []string{"storage", "bucket", "del", bucket, "-y"}, nil)
}()

var res s3.PutObjectOutput
runJSON(t, []string{"storage", "object", "put", object, "--bucket", bucket, "--body", path, "--output", "json"}, nil, &res)
t.Run("Presigning GetObject works", func(t *testing.T) {
object := "GetObject.txt"
path := file(t, object, hello)
_ = run(t, []string{"storage", "object", "put", object, "--bucket", bucket, "--body", path, "--output", "json"}, nil)
defer func() {
_ = run(t, []string{"storage", "object", "del", object, "--bucket", bucket, "-y"}, nil)
}()
Expand All @@ -545,4 +543,41 @@ func TestPResign(t *testing.T) {
assert.NotEqual(t, http.StatusOK, resp.StatusCode)
resp.Body.Close() //nolint
})

t.Run("Presigning PutObject works", func(t *testing.T) {
object := "PutObject.txt"
out := run(t, []string{"storage", "object", "presign", object, "--bucket", bucket, "--expires", "2s", "--method", "PUT"}, nil)
require.True(t, strings.HasPrefix(string(out), "http"))

req, err := http.NewRequestWithContext(t.Context(), http.MethodPut, string(out), strings.NewReader(hello))
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp.Body.Close() //nolint
defer func() {
_ = run(t, []string{"storage", "object", "del", object, "--bucket", bucket, "-y"}, nil)
}()

res := run(t, []string{"storage", "object", "download", object, "--bucket", bucket}, nil)
assert.Equal(t, hello, string(res))
})

t.Run("Presigning DeleteObject works", func(t *testing.T) {
object := "DeleteObject.txt"
path := file(t, object, hello)
_ = run(t, []string{"storage", "object", "put", object, "--bucket", bucket, "--body", path, "--output", "json"}, nil)

out := run(t, []string{"storage", "object", "presign", object, "--bucket", bucket, "--expires", "2s", "--method", "DELETE"}, nil)
require.True(t, strings.HasPrefix(string(out), "http"))

req, err := http.NewRequestWithContext(t.Context(), http.MethodDelete, string(out), nil)
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusNoContent, resp.StatusCode)
resp.Body.Close() //nolint

runWithError(t, []string{"storage", "object", "download", object, "--bucket", bucket}, nil)
})
}
1 change: 1 addition & 0 deletions docs/reference/octl_storage_object_presign.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ octl storage object presign key [flags]
--bucket string bucket
--expires duration URL expiration (e.g. 30s, 1h)
-h, --help help for presign
--method string Method used to access the presigned URL (GET, PUT, DELETE) (default "GET")
```

### Options inherited from parent commands
Expand Down
Loading