mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
117 lines
3.9 KiB
Go
117 lines
3.9 KiB
Go
|
/*
|
||
|
Copyright (c) Edgeless Systems GmbH
|
||
|
|
||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
Package s3 implements a very thin wrapper around the AWS S3 client.
|
||
|
It only exists to enable stubbing of the AWS S3 client in tests.
|
||
|
*/
|
||
|
package s3
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"crypto/md5"
|
||
|
"encoding/base64"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||
|
)
|
||
|
|
||
|
// Client is a wrapper around the AWS S3 client.
|
||
|
type Client struct {
|
||
|
s3client *s3.Client
|
||
|
}
|
||
|
|
||
|
// NewClient creates a new AWS S3 client.
|
||
|
func NewClient(region string) (*Client, error) {
|
||
|
// Use context.Background here because this context will not influence the later operations of the client.
|
||
|
// The context given here is used for http requests that are made during client construction.
|
||
|
// Client construction happens once during proxy setup.
|
||
|
clientCfg, err := config.LoadDefaultConfig(
|
||
|
context.Background(),
|
||
|
config.WithRegion(region),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("loading AWS S3 client config: %w", err)
|
||
|
}
|
||
|
|
||
|
client := s3.NewFromConfig(clientCfg)
|
||
|
|
||
|
return &Client{client}, nil
|
||
|
}
|
||
|
|
||
|
// GetObject returns the object with the given key from the given bucket.
|
||
|
// If a versionID is given, the specific version of the object is returned.
|
||
|
func (c Client) GetObject(ctx context.Context, bucket, key, versionID, sseCustomerAlgorithm, sseCustomerKey, sseCustomerKeyMD5 string) (*s3.GetObjectOutput, error) {
|
||
|
getObjectInput := &s3.GetObjectInput{
|
||
|
Bucket: &bucket,
|
||
|
Key: &key,
|
||
|
}
|
||
|
if versionID != "" {
|
||
|
getObjectInput.VersionId = &versionID
|
||
|
}
|
||
|
if sseCustomerAlgorithm != "" {
|
||
|
getObjectInput.SSECustomerAlgorithm = &sseCustomerAlgorithm
|
||
|
}
|
||
|
if sseCustomerKey != "" {
|
||
|
getObjectInput.SSECustomerKey = &sseCustomerKey
|
||
|
}
|
||
|
if sseCustomerKeyMD5 != "" {
|
||
|
getObjectInput.SSECustomerKeyMD5 = &sseCustomerKeyMD5
|
||
|
}
|
||
|
|
||
|
return c.s3client.GetObject(ctx, getObjectInput)
|
||
|
}
|
||
|
|
||
|
// PutObject creates a new object in the given bucket with the given key and body.
|
||
|
// Various optional parameters can be set.
|
||
|
func (c Client) PutObject(ctx context.Context, bucket, key, tags, contentType, objectLockLegalHoldStatus, objectLockMode, sseCustomerAlgorithm, sseCustomerKey, sseCustomerKeyMD5 string, objectLockRetainUntilDate time.Time, metadata map[string]string, body []byte) (*s3.PutObjectOutput, error) {
|
||
|
// The AWS Go SDK has two versions. V1 does not set the Content-Type header.
|
||
|
// V2 always sets the Content-Type header. We use V2.
|
||
|
// The s3 API sets an object's content-type to binary/octet-stream if
|
||
|
// it receives a request without a Content-Type header set.
|
||
|
// Since a client using V1 may depend on the Content-Type binary/octet-stream
|
||
|
// we have to explicitly emulate the S3 API behavior, if we receive a request
|
||
|
// without a Content-Type.
|
||
|
if contentType == "" {
|
||
|
contentType = "binary/octet-stream"
|
||
|
}
|
||
|
|
||
|
contentMD5 := md5.Sum(body)
|
||
|
encodedContentMD5 := base64.StdEncoding.EncodeToString(contentMD5[:])
|
||
|
|
||
|
putObjectInput := &s3.PutObjectInput{
|
||
|
Bucket: &bucket,
|
||
|
Key: &key,
|
||
|
Body: bytes.NewReader(body),
|
||
|
Tagging: &tags,
|
||
|
Metadata: metadata,
|
||
|
ContentMD5: &encodedContentMD5,
|
||
|
ContentType: &contentType,
|
||
|
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatus(objectLockLegalHoldStatus),
|
||
|
}
|
||
|
if sseCustomerAlgorithm != "" {
|
||
|
putObjectInput.SSECustomerAlgorithm = &sseCustomerAlgorithm
|
||
|
}
|
||
|
if sseCustomerKey != "" {
|
||
|
putObjectInput.SSECustomerKey = &sseCustomerKey
|
||
|
}
|
||
|
if sseCustomerKeyMD5 != "" {
|
||
|
putObjectInput.SSECustomerKeyMD5 = &sseCustomerKeyMD5
|
||
|
}
|
||
|
|
||
|
// It is not allowed to only set one of these two properties.
|
||
|
if objectLockMode != "" && !objectLockRetainUntilDate.IsZero() {
|
||
|
putObjectInput.ObjectLockMode = types.ObjectLockMode(objectLockMode)
|
||
|
putObjectInput.ObjectLockRetainUntilDate = &objectLockRetainUntilDate
|
||
|
}
|
||
|
|
||
|
return c.s3client.PutObject(ctx, putObjectInput)
|
||
|
}
|