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

Refactor PutObject API calls to add context and additional metadata. #811

Merged
merged 1 commit into from
Sep 20, 2017

Conversation

poornas
Copy link
Contributor

@poornas poornas commented Sep 5, 2017

  • Add additional Api calls PutObjectWithContext, GetObjectWithContext, FPutObjectWithContext and FGetObjectWithContext to allow request cancellation.
  • Remove PutObjectWithSize,PutObjectStreaming,PutEncryptedObject,PutObjectWithMetadata calls and fold into a single PutObject call which accepts a pointer to PutObjectOptions struct. PutObjectOptions struct allows user to enter custom metadata, Content-Encoding, Cache-Control, Content-Type and Content-Disposition headers, configure number of threads for multipart PutObject calls, and set Encryption materials module to encrypt data.
  • Change FPutObject API signature to add additional parameter - a pointer to PutObjectOptions struct.
  • Number of workers used in multipart Put operation is now configurable by setting the number of threads as an optional field in the PutObjectOptions struct.

Fixes #700
Fixes #747
Fixes #759

"io"
)

func (c Client) PutObjectWithContext(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64,

Choose a reason for hiding this comment

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

exported method Client.PutObjectWithContext should have comment or be unexported

"github.com/minio/minio-go/pkg/s3utils"
)

type PutObjectOptions struct {

Choose a reason for hiding this comment

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

exported type PutObjectOptions should have comment or be unexported

"github.com/minio/minio-go/pkg/s3utils"
)

// FGetObject - download contents of an object to a local file.
func (c Client) FGetObject(bucketName, objectName, filePath string) error {
return c.fGetObjectWithContext(context.Background(), bucketName, objectName, filePath)
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure how it is useful to provide two APIs which has the same functionality, for example here:

  • FGetObject
    and
  • FGetObjectWithContext

I think we need to decide if they should have different behaviors or merge them into one function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was decided to have an extra variant of Put/GetObject when context is an additional argument, rather than force user to pass nil each time if they do not need request cancellation. We could however revisit this in the next meeting and reach a consensus.

Copy link
Member

Choose a reason for hiding this comment

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

rather than force user to pass nil each time if they do not need request cancellation.

emm, in that case, we need to pass nil here (if it works) and not context.Background()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as per https://golang.org/pkg/context/
Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use.

Since we are wrapping the functions, it makes sense to use context.Background(). Agree?

Copy link
Member

@vadmeste vadmeste Sep 8, 2017

Choose a reason for hiding this comment

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

Since we are wrapping the functions, it makes sense to use context.Background(). Agree?

sorry, yeah I see now how it can be useful to have two APIs

@poornas
Copy link
Contributor Author

poornas commented Sep 14, 2017

@vadmeste and @harshavardhana , this needs reviews.

@poornas poornas force-pushed the kfactor branch 2 times, most recently from e305847 to ed85921 Compare September 14, 2017 17:31
}

if opts.EncryptMaterials != nil {
if err := opts.EncryptMaterials.SetupEncryptMode(reader); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

This err assignment shadows with err return value. Just use err = instead of err :=

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

opts = &PutObjectOptions{}
}
// Set contentType based on filepath extension if not given or default
// value of "binary/octet-stream" if the extension has no associated type.
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we can name this as application/octet-stream instead. Since that is what we set eventually instead of binary/octet-stream

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was unintentional. fixed.


// getMetaData - constructs the headers from metadata entered by user in
// PutObjectOptions struct
func (opts *PutObjectOptions) getMetadata() (headers map[string][]string) {
Copy link
Member

Choose a reason for hiding this comment

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

We can make the return value to be more typed i.e getMetadataHeaders() (headers http.Header) instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Copy link
Member

@harshavardhana harshavardhana left a comment

Choose a reason for hiding this comment

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

This is a major change in API we need to move to 4.0 after this change is merged.

@harshavardhana
Copy link
Member

harshavardhana commented Sep 15, 2017

One suggestion is regarding commit message.

Add additional Api calls PutObjectWithContext, GetObjectWithContext,
FPutObjectWithContext and FGetObjectWithContext to allow request cancellation.

Remove PutObjectWithSize,PutObjectStreaming,PutEncryptedObject,PutObjectWithMetadata
calls and fold into a single PutObject call which accepts a pointer to PutObjectOptions
struct. PutObjectOptions struct allows user to enter custom metadata, Content-Encoding,
Cache-Control, Content-Type and Content-Disposition headers, configure number of threads
for multipart PutObject calls, and set Encryption materials module to encrypt data.

^^ This paragraph can be worded a bit better.

Pass pointer to PutObjectOptions struct as argument to FPutObject call.

This is a not a commit body worthy message can can be removed.

Core API methods PutObject, NewMultipartUpload, PutObjectPart and PutObjectPartWithMetadata
signatures have changed.

This can be moved above along with function signature changes.

Remove go 1.5,1.6 from Travis. Add 1.9

We can say Deprecated support for Go versions 1.5.x, 1.6.x, Add support for 1.9.x

@poornas
Copy link
Contributor Author

poornas commented Sep 15, 2017

@harshavardhana, reworded commit message.

@poornas
Copy link
Contributor Author

poornas commented Sep 15, 2017

@vadmeste , this needs to be reviewed.

@@ -79,7 +80,7 @@ func (c Client) getBucketPolicy(bucketName string) (policy.BucketAccessPolicy, e
urlValues.Set("policy", "")

// Execute GET on bucket to list objects.
resp, err := c.executeMethod("GET", requestMetadata{
resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{
Copy link
Member

Choose a reason for hiding this comment

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

@poornas, is there any reason why we are not accepting letting developers their own context here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@vadmeste, the scope of this PR is to limit adding context to PutObject calls because it is likely to be long running enough to warrant a request cancellation. There is not much of a use case for GetBucketPolicy to use context.

Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -77,7 +78,7 @@ func optimalPartInfo(objectSize int64) (totalPartsCount int, partSize int64, las

// getUploadID - fetch upload id if already present for an object name
// or initiate a new request to fetch a new upload id.
func (c Client) newUploadID(bucketName, objectName string, metaData map[string][]string) (uploadID string, err error) {
func (c Client) newUploadID(ctx context.Context, bucketName, objectName string, opts *PutObjectOptions) (uploadID string, err error) {
Copy link
Member

Choose a reason for hiding this comment

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

I like PutObjectOptions change, but couldn't we do it in a separate PR ? (let's keep it as it now since I am already late reviewing this PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@vadmeste, the PutObjectOptions change is the main reason for this PR. Context change is minor since we are just passing it through to the executeMethod everywhere so that request can get context. It made sense to combine the changes in one PR.

import "context"

// GetObjectWithContext - returns an seekable, readable object.
func (c Client) GetObjectWithContext(ctx context.Context, bucketName, objectName string) (*Object, error) {
Copy link
Member

Choose a reason for hiding this comment

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

I think we can avoid creating new file for this function, we can put it in api-get-file.go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

import "context"

// FGetObjectWithContext - download contents of an object to a local file.
func (c Client) FGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string) error {
Copy link
Member

Choose a reason for hiding this comment

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

We probably need to avoid creating a new file for this function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

break
}
if rErr != nil && rErr != io.ErrUnexpectedEOF {
if rErr != nil && rErr != io.ErrUnexpectedEOF && partNumber > 1 {
Copy link
Member

Choose a reason for hiding this comment

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

Here, io.ReadFull() can return an error other than io.ErrUnexpectedEOF and io.EOF but we ignore it when partNumber == 1. In other terms, we will create an object even if the reader returns a serious error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

return 0, err
}

// Set the necessary encryption headers, for future decryption.
Copy link
Member

Choose a reason for hiding this comment

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

Can you remove this comment ? it doesn't make sense anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Add additional Api calls PutObjectWithContext, GetObjectWithContext,
FPutObjectWithContext and FGetObjectWithContext to allow request cancellation.

Remove PutObjectWithSize,PutObjectStreaming,PutEncryptedObject,PutObjectWithMetadata
calls and fold into a single PutObject call which accepts a pointer to PutObjectOptions
struct. PutObjectOptions struct allows user to optionally set custom metadata, content response headers,
encryption materials module for data encryption, and specify number of threads for a multi-part
Put operation. Currently,Content-Encoding,Cache-Control, Content-Type and Content-Disposition headers
are supported.

Deprecated support for Go versions 1.5.x,1.6.x. Add support for 1.9.x
@poornas
Copy link
Contributor Author

poornas commented Sep 19, 2017

@vadmeste, updated the PR with your suggestions.Could you please review?

Copy link
Member

@vadmeste vadmeste left a comment

Choose a reason for hiding this comment

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

LGTM, tested some examples + encryption example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants