Skip to content

Commit

Permalink
Go: update S3 examples per audit to match current standards (awsdocs#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Laren-AWS authored Oct 29, 2024
1 parent 970756b commit 82378ae
Show file tree
Hide file tree
Showing 22 changed files with 590 additions and 146 deletions.
12 changes: 7 additions & 5 deletions .doc_gen/metadata/s3_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2598,14 +2598,16 @@ s3_Scenario_UsingLargeFiles:
- sdk_version: 2
github: gov2/s3
excerpts:
- description: Upload a large object by using an upload manager to break the data into parts and upload them concurrently.
- description: Create functions that use upload and download managers to break the data into parts and
transfer them concurrently.
snippet_tags:
- gov2.s3.BucketBasics.struct
- gov2.s3.Upload
- description: Download a large object by using a download manager to get the data in parts and download them
concurrently.
snippet_tags:
- gov2.s3.Download
- description: Run an interactive scenario that shows you how to use the upload and download
managers in context.
snippet_tags:
- gov2.s3.Scenario_LargeObjects
Python:
versions:
- sdk_version: 3
Expand Down Expand Up @@ -2971,7 +2973,7 @@ s3_Scenario_GettingStarted:
- description: Define a struct that wraps bucket and object actions used by the scenario.
snippet_tags:
- gov2.s3.BucketBasics.complete
- description: Run an interactive scenario that shows you how work with S3 buckets and objects.
- description: Run an interactive scenario that shows you how to work with S3 buckets and objects.
snippet_tags:
- gov2.s3.Scenario_GetStarted
Rust:
Expand Down
18 changes: 9 additions & 9 deletions gov2/s3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ Code examples that show you how to perform the essential operations within a ser

Code excerpts that show you how to call individual service functions.

- [CopyObject](actions/bucket_basics.go#L220)
- [CreateBucket](actions/bucket_basics.go#L81)
- [DeleteBucket](actions/bucket_basics.go#L278)
- [CopyObject](actions/bucket_basics.go#L288)
- [CreateBucket](actions/bucket_basics.go#L94)
- [DeleteBucket](actions/bucket_basics.go#L387)
- [DeleteObject](../workflows/s3_object_lock/actions/s3_actions.go#L365)
- [DeleteObjects](../workflows/s3_object_lock/actions/s3_actions.go#L407)
- [GetObject](actions/bucket_basics.go#L149)
- [DeleteObjects](../workflows/s3_object_lock/actions/s3_actions.go#L413)
- [GetObject](actions/bucket_basics.go#L200)
- [GetObjectLegalHold](../workflows/s3_object_lock/actions/s3_actions.go#L72)
- [GetObjectLockConfiguration](../workflows/s3_object_lock/actions/s3_actions.go#L109)
- [GetObjectRetention](../workflows/s3_object_lock/actions/s3_actions.go#L138)
- [HeadBucket](actions/bucket_basics.go#L51)
- [ListBuckets](actions/bucket_basics.go#L35)
- [HeadBucket](actions/bucket_basics.go#L64)
- [ListBuckets](actions/bucket_basics.go#L36)
- [ListObjectVersions](../workflows/s3_object_lock/actions/s3_actions.go#L338)
- [ListObjectsV2](actions/bucket_basics.go#L238)
- [PutObject](actions/bucket_basics.go#L100)
- [ListObjectsV2](actions/bucket_basics.go#L316)
- [PutObject](actions/bucket_basics.go#L126)
- [PutObjectLegalHold](../workflows/s3_object_lock/actions/s3_actions.go#L173)
- [PutObjectLockConfiguration](../workflows/s3_object_lock/actions/s3_actions.go#L234)
- [PutObjectRetention](../workflows/s3_object_lock/actions/s3_actions.go#L276)
Expand Down
189 changes: 156 additions & 33 deletions gov2/s3/actions/bucket_basics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

package actions

// snippet-start:[gov2.s3.BucketBasics.complete]
// snippet-start:[gov2.s3.BucketBasics.struct]

import (
"bytes"
"context"
Expand All @@ -11,6 +14,7 @@ import (
"io"
"log"
"os"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
Expand All @@ -19,9 +23,6 @@ import (
"github.com/aws/smithy-go"
)

// snippet-start:[gov2.s3.BucketBasics.complete]
// snippet-start:[gov2.s3.BucketBasics.struct]

// BucketBasics encapsulates the Amazon Simple Storage Service (Amazon S3) actions
// used in the examples.
// It contains S3Client, an Amazon S3 service client that is used to perform bucket
Expand All @@ -36,12 +37,24 @@ type BucketBasics struct {

// ListBuckets lists the buckets in the current account.
func (basics BucketBasics) ListBuckets(ctx context.Context) ([]types.Bucket, error) {
result, err := basics.S3Client.ListBuckets(ctx, &s3.ListBucketsInput{})
var err error
var output *s3.ListBucketsOutput
var buckets []types.Bucket
if err != nil {
log.Printf("Couldn't list buckets for your account. Here's why: %v\n", err)
} else {
buckets = result.Buckets
bucketPaginator := s3.NewListBucketsPaginator(basics.S3Client, &s3.ListBucketsInput{})
for bucketPaginator.HasMorePages() {
output, err = bucketPaginator.NextPage(ctx)
if err != nil {
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "AccessDenied" {
fmt.Println("You don't have permission to list buckets for this account.")
err = apiErr
} else {
log.Printf("Couldn't list buckets for your account. Here's why: %v\n", err)
}
break
} else {
buckets = append(buckets, output.Buckets...)
}
}
return buckets, err
}
Expand Down Expand Up @@ -89,8 +102,21 @@ func (basics BucketBasics) CreateBucket(ctx context.Context, name string, region
},
})
if err != nil {
log.Printf("Couldn't create bucket %v in Region %v. Here's why: %v\n",
name, region, err)
var owned *types.BucketAlreadyOwnedByYou
var exists *types.BucketAlreadyExists
if errors.As(err, &owned) {
log.Printf("You already own bucket %s.\n", name)
err = owned
} else if errors.As(err, &exists) {
log.Printf("Bucket %s already exists.\n", name)
err = exists
}
} else {
err = s3.NewBucketExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadBucketInput{Bucket: aws.String(name)}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for bucket %s to exist.\n", name)
}
}
return err
}
Expand All @@ -112,8 +138,21 @@ func (basics BucketBasics) UploadFile(ctx context.Context, bucketName string, ob
Body: file,
})
if err != nil {
log.Printf("Couldn't upload file %v to %v:%v. Here's why: %v\n",
fileName, bucketName, objectKey, err)
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "EntityTooLarge" {
log.Printf("Error while uploading object to %s. The object is too large.\n"+
"To upload objects larger than 5GB, use the S3 console (160GB max)\n"+
"or the multipart upload API (5TB max).", bucketName)
} else {
log.Printf("Couldn't upload file %v to %v:%v. Here's why: %v\n",
fileName, bucketName, objectKey, err)
}
} else {
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectKey)}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for object %s to exist.\n", objectKey)
}
}
}
return err
Expand All @@ -137,8 +176,20 @@ func (basics BucketBasics) UploadLargeObject(ctx context.Context, bucketName str
Body: largeBuffer,
})
if err != nil {
log.Printf("Couldn't upload large object to %v:%v. Here's why: %v\n",
bucketName, objectKey, err)
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "EntityTooLarge" {
log.Printf("Error while uploading object to %s. The object is too large.\n"+
"The maximum size for a multipart upload is 5TB.", bucketName)
} else {
log.Printf("Couldn't upload large object to %v:%v. Here's why: %v\n",
bucketName, objectKey, err)
}
} else {
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectKey)}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for object %s to exist.\n", objectKey)
}
}

return err
Expand All @@ -155,7 +206,13 @@ func (basics BucketBasics) DownloadFile(ctx context.Context, bucketName string,
Key: aws.String(objectKey),
})
if err != nil {
log.Printf("Couldn't get object %v:%v. Here's why: %v\n", bucketName, objectKey, err)
var noKey *types.NoSuchKey
if errors.As(err, &noKey) {
log.Printf("Can't get object %s from bucket %s. No such key exists.\n", objectKey, bucketName)
err = noKey
} else {
log.Printf("Couldn't get object %v:%v. Here's why: %v\n", bucketName, objectKey, err)
}
return err
}
defer result.Body.Close()
Expand Down Expand Up @@ -203,14 +260,25 @@ func (basics BucketBasics) DownloadLargeObject(ctx context.Context, bucketName s

// CopyToFolder copies an object in a bucket to a subfolder in the same bucket.
func (basics BucketBasics) CopyToFolder(ctx context.Context, bucketName string, objectKey string, folderName string) error {
objectDest := fmt.Sprintf("%v/%v", folderName, objectKey)
_, err := basics.S3Client.CopyObject(ctx, &s3.CopyObjectInput{
Bucket: aws.String(bucketName),
CopySource: aws.String(fmt.Sprintf("%v/%v", bucketName, objectKey)),
Key: aws.String(fmt.Sprintf("%v/%v", folderName, objectKey)),
Key: aws.String(objectDest),
})
if err != nil {
log.Printf("Couldn't copy object from %v:%v to %v:%v/%v. Here's why: %v\n",
bucketName, objectKey, bucketName, folderName, objectKey, err)
var notActive *types.ObjectNotInActiveTierError
if errors.As(err, &notActive) {
log.Printf("Couldn't copy object %s from %s because the object isn't in the active tier.\n",
objectKey, bucketName)
err = notActive
}
} else {
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectDest)}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for object %s to exist.\n", objectDest)
}
}
return err
}
Expand All @@ -227,8 +295,18 @@ func (basics BucketBasics) CopyToBucket(ctx context.Context, sourceBucket string
Key: aws.String(objectKey),
})
if err != nil {
log.Printf("Couldn't copy object from %v:%v to %v:%v. Here's why: %v\n",
sourceBucket, objectKey, destinationBucket, objectKey, err)
var notActive *types.ObjectNotInActiveTierError
if errors.As(err, &notActive) {
log.Printf("Couldn't copy object %s from %s because the object isn't in the active tier.\n",
objectKey, sourceBucket)
err = notActive
}
} else {
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadObjectInput{Bucket: aws.String(destinationBucket), Key: aws.String(objectKey)}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for object %s to exist.\n", objectKey)
}
}
return err
}
Expand All @@ -239,16 +317,27 @@ func (basics BucketBasics) CopyToBucket(ctx context.Context, sourceBucket string

// ListObjects lists the objects in a bucket.
func (basics BucketBasics) ListObjects(ctx context.Context, bucketName string) ([]types.Object, error) {
result, err := basics.S3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
var err error
var output *s3.ListObjectsV2Output
input := &s3.ListObjectsV2Input{
Bucket: aws.String(bucketName),
})
var contents []types.Object
if err != nil {
log.Printf("Couldn't list objects in bucket %v. Here's why: %v\n", bucketName, err)
} else {
contents = result.Contents
}
return contents, err
var objects []types.Object
objectPaginator := s3.NewListObjectsV2Paginator(basics.S3Client, input)
for objectPaginator.HasMorePages() {
output, err = objectPaginator.NextPage(ctx)
if err != nil {
var noBucket *types.NoSuchBucket
if errors.As(err, &noBucket) {
log.Printf("Bucket %s does not exist.\n", bucketName)
err = noBucket
}
break
} else {
objects = append(objects, output.Contents...)
}
}
return objects, err
}

// snippet-end:[gov2.s3.ListObjectsV2]
Expand All @@ -263,12 +352,32 @@ func (basics BucketBasics) DeleteObjects(ctx context.Context, bucketName string,
}
output, err := basics.S3Client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
Bucket: aws.String(bucketName),
Delete: &types.Delete{Objects: objectIds},
Delete: &types.Delete{Objects: objectIds, Quiet: aws.Bool(true)},
})
if err != nil {
log.Printf("Couldn't delete objects from bucket %v. Here's why: %v\n", bucketName, err)
if err != nil || len(output.Errors) > 0 {
log.Printf("Error deleting objects from bucket %s.\n", bucketName)
if err != nil {
var noBucket *types.NoSuchBucket
if errors.As(err, &noBucket) {
log.Printf("Bucket %s does not exist.\n", bucketName)
err = noBucket
}
} else if len(output.Errors) > 0 {
for _, outErr := range output.Errors {
log.Printf("%s: %s\n", *outErr.Key, *outErr.Message)
}
err = fmt.Errorf("%s", *output.Errors[0].Message)
}
} else {
log.Printf("Deleted %v objects.\n", len(output.Deleted))
for _, delObjs := range output.Deleted {
err = s3.NewObjectNotExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: delObjs.Key}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for object %s to be deleted.\n", *delObjs.Key)
} else {
log.Printf("Deleted %s.\n", *delObjs.Key)
}
}
}
return err
}
Expand All @@ -282,7 +391,21 @@ func (basics BucketBasics) DeleteBucket(ctx context.Context, bucketName string)
_, err := basics.S3Client.DeleteBucket(ctx, &s3.DeleteBucketInput{
Bucket: aws.String(bucketName)})
if err != nil {
log.Printf("Couldn't delete bucket %v. Here's why: %v\n", bucketName, err)
var noBucket *types.NoSuchBucket
if errors.As(err, &noBucket) {
log.Printf("Bucket %s does not exist.\n", bucketName)
err = noBucket
} else {
log.Printf("Couldn't delete bucket %v. Here's why: %v\n", bucketName, err)
}
} else {
err = s3.NewBucketNotExistsWaiter(basics.S3Client).Wait(
ctx, &s3.HeadBucketInput{Bucket: aws.String(bucketName)}, time.Minute)
if err != nil {
log.Printf("Failed attempt to wait for bucket %s to be deleted.\n", bucketName)
} else {
log.Printf("Deleted %s.\n", bucketName)
}
}
return err
}
Expand Down
Loading

0 comments on commit 82378ae

Please sign in to comment.