Skip to content

Commit 82378ae

Browse files
authored
Go: update S3 examples per audit to match current standards (awsdocs#7011)
1 parent 970756b commit 82378ae

22 files changed

+590
-146
lines changed

.doc_gen/metadata/s3_metadata.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,14 +2598,16 @@ s3_Scenario_UsingLargeFiles:
25982598
- sdk_version: 2
25992599
github: gov2/s3
26002600
excerpts:
2601-
- description: Upload a large object by using an upload manager to break the data into parts and upload them concurrently.
2601+
- description: Create functions that use upload and download managers to break the data into parts and
2602+
transfer them concurrently.
26022603
snippet_tags:
26032604
- gov2.s3.BucketBasics.struct
26042605
- gov2.s3.Upload
2605-
- description: Download a large object by using a download manager to get the data in parts and download them
2606-
concurrently.
2607-
snippet_tags:
26082606
- gov2.s3.Download
2607+
- description: Run an interactive scenario that shows you how to use the upload and download
2608+
managers in context.
2609+
snippet_tags:
2610+
- gov2.s3.Scenario_LargeObjects
26092611
Python:
26102612
versions:
26112613
- sdk_version: 3
@@ -2971,7 +2973,7 @@ s3_Scenario_GettingStarted:
29712973
- description: Define a struct that wraps bucket and object actions used by the scenario.
29722974
snippet_tags:
29732975
- gov2.s3.BucketBasics.complete
2974-
- description: Run an interactive scenario that shows you how work with S3 buckets and objects.
2976+
- description: Run an interactive scenario that shows you how to work with S3 buckets and objects.
29752977
snippet_tags:
29762978
- gov2.s3.Scenario_GetStarted
29772979
Rust:

gov2/s3/README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,20 @@ Code examples that show you how to perform the essential operations within a ser
4545

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

48-
- [CopyObject](actions/bucket_basics.go#L220)
49-
- [CreateBucket](actions/bucket_basics.go#L81)
50-
- [DeleteBucket](actions/bucket_basics.go#L278)
48+
- [CopyObject](actions/bucket_basics.go#L288)
49+
- [CreateBucket](actions/bucket_basics.go#L94)
50+
- [DeleteBucket](actions/bucket_basics.go#L387)
5151
- [DeleteObject](../workflows/s3_object_lock/actions/s3_actions.go#L365)
52-
- [DeleteObjects](../workflows/s3_object_lock/actions/s3_actions.go#L407)
53-
- [GetObject](actions/bucket_basics.go#L149)
52+
- [DeleteObjects](../workflows/s3_object_lock/actions/s3_actions.go#L413)
53+
- [GetObject](actions/bucket_basics.go#L200)
5454
- [GetObjectLegalHold](../workflows/s3_object_lock/actions/s3_actions.go#L72)
5555
- [GetObjectLockConfiguration](../workflows/s3_object_lock/actions/s3_actions.go#L109)
5656
- [GetObjectRetention](../workflows/s3_object_lock/actions/s3_actions.go#L138)
57-
- [HeadBucket](actions/bucket_basics.go#L51)
58-
- [ListBuckets](actions/bucket_basics.go#L35)
57+
- [HeadBucket](actions/bucket_basics.go#L64)
58+
- [ListBuckets](actions/bucket_basics.go#L36)
5959
- [ListObjectVersions](../workflows/s3_object_lock/actions/s3_actions.go#L338)
60-
- [ListObjectsV2](actions/bucket_basics.go#L238)
61-
- [PutObject](actions/bucket_basics.go#L100)
60+
- [ListObjectsV2](actions/bucket_basics.go#L316)
61+
- [PutObject](actions/bucket_basics.go#L126)
6262
- [PutObjectLegalHold](../workflows/s3_object_lock/actions/s3_actions.go#L173)
6363
- [PutObjectLockConfiguration](../workflows/s3_object_lock/actions/s3_actions.go#L234)
6464
- [PutObjectRetention](../workflows/s3_object_lock/actions/s3_actions.go#L276)

gov2/s3/actions/bucket_basics.go

Lines changed: 156 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
package actions
55

6+
// snippet-start:[gov2.s3.BucketBasics.complete]
7+
// snippet-start:[gov2.s3.BucketBasics.struct]
8+
69
import (
710
"bytes"
811
"context"
@@ -11,6 +14,7 @@ import (
1114
"io"
1215
"log"
1316
"os"
17+
"time"
1418

1519
"github.com/aws/aws-sdk-go-v2/aws"
1620
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
@@ -19,9 +23,6 @@ import (
1923
"github.com/aws/smithy-go"
2024
)
2125

22-
// snippet-start:[gov2.s3.BucketBasics.complete]
23-
// snippet-start:[gov2.s3.BucketBasics.struct]
24-
2526
// BucketBasics encapsulates the Amazon Simple Storage Service (Amazon S3) actions
2627
// used in the examples.
2728
// It contains S3Client, an Amazon S3 service client that is used to perform bucket
@@ -36,12 +37,24 @@ type BucketBasics struct {
3637

3738
// ListBuckets lists the buckets in the current account.
3839
func (basics BucketBasics) ListBuckets(ctx context.Context) ([]types.Bucket, error) {
39-
result, err := basics.S3Client.ListBuckets(ctx, &s3.ListBucketsInput{})
40+
var err error
41+
var output *s3.ListBucketsOutput
4042
var buckets []types.Bucket
41-
if err != nil {
42-
log.Printf("Couldn't list buckets for your account. Here's why: %v\n", err)
43-
} else {
44-
buckets = result.Buckets
43+
bucketPaginator := s3.NewListBucketsPaginator(basics.S3Client, &s3.ListBucketsInput{})
44+
for bucketPaginator.HasMorePages() {
45+
output, err = bucketPaginator.NextPage(ctx)
46+
if err != nil {
47+
var apiErr smithy.APIError
48+
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "AccessDenied" {
49+
fmt.Println("You don't have permission to list buckets for this account.")
50+
err = apiErr
51+
} else {
52+
log.Printf("Couldn't list buckets for your account. Here's why: %v\n", err)
53+
}
54+
break
55+
} else {
56+
buckets = append(buckets, output.Buckets...)
57+
}
4558
}
4659
return buckets, err
4760
}
@@ -89,8 +102,21 @@ func (basics BucketBasics) CreateBucket(ctx context.Context, name string, region
89102
},
90103
})
91104
if err != nil {
92-
log.Printf("Couldn't create bucket %v in Region %v. Here's why: %v\n",
93-
name, region, err)
105+
var owned *types.BucketAlreadyOwnedByYou
106+
var exists *types.BucketAlreadyExists
107+
if errors.As(err, &owned) {
108+
log.Printf("You already own bucket %s.\n", name)
109+
err = owned
110+
} else if errors.As(err, &exists) {
111+
log.Printf("Bucket %s already exists.\n", name)
112+
err = exists
113+
}
114+
} else {
115+
err = s3.NewBucketExistsWaiter(basics.S3Client).Wait(
116+
ctx, &s3.HeadBucketInput{Bucket: aws.String(name)}, time.Minute)
117+
if err != nil {
118+
log.Printf("Failed attempt to wait for bucket %s to exist.\n", name)
119+
}
94120
}
95121
return err
96122
}
@@ -112,8 +138,21 @@ func (basics BucketBasics) UploadFile(ctx context.Context, bucketName string, ob
112138
Body: file,
113139
})
114140
if err != nil {
115-
log.Printf("Couldn't upload file %v to %v:%v. Here's why: %v\n",
116-
fileName, bucketName, objectKey, err)
141+
var apiErr smithy.APIError
142+
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "EntityTooLarge" {
143+
log.Printf("Error while uploading object to %s. The object is too large.\n"+
144+
"To upload objects larger than 5GB, use the S3 console (160GB max)\n"+
145+
"or the multipart upload API (5TB max).", bucketName)
146+
} else {
147+
log.Printf("Couldn't upload file %v to %v:%v. Here's why: %v\n",
148+
fileName, bucketName, objectKey, err)
149+
}
150+
} else {
151+
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
152+
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectKey)}, time.Minute)
153+
if err != nil {
154+
log.Printf("Failed attempt to wait for object %s to exist.\n", objectKey)
155+
}
117156
}
118157
}
119158
return err
@@ -137,8 +176,20 @@ func (basics BucketBasics) UploadLargeObject(ctx context.Context, bucketName str
137176
Body: largeBuffer,
138177
})
139178
if err != nil {
140-
log.Printf("Couldn't upload large object to %v:%v. Here's why: %v\n",
141-
bucketName, objectKey, err)
179+
var apiErr smithy.APIError
180+
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "EntityTooLarge" {
181+
log.Printf("Error while uploading object to %s. The object is too large.\n"+
182+
"The maximum size for a multipart upload is 5TB.", bucketName)
183+
} else {
184+
log.Printf("Couldn't upload large object to %v:%v. Here's why: %v\n",
185+
bucketName, objectKey, err)
186+
}
187+
} else {
188+
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
189+
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectKey)}, time.Minute)
190+
if err != nil {
191+
log.Printf("Failed attempt to wait for object %s to exist.\n", objectKey)
192+
}
142193
}
143194

144195
return err
@@ -155,7 +206,13 @@ func (basics BucketBasics) DownloadFile(ctx context.Context, bucketName string,
155206
Key: aws.String(objectKey),
156207
})
157208
if err != nil {
158-
log.Printf("Couldn't get object %v:%v. Here's why: %v\n", bucketName, objectKey, err)
209+
var noKey *types.NoSuchKey
210+
if errors.As(err, &noKey) {
211+
log.Printf("Can't get object %s from bucket %s. No such key exists.\n", objectKey, bucketName)
212+
err = noKey
213+
} else {
214+
log.Printf("Couldn't get object %v:%v. Here's why: %v\n", bucketName, objectKey, err)
215+
}
159216
return err
160217
}
161218
defer result.Body.Close()
@@ -203,14 +260,25 @@ func (basics BucketBasics) DownloadLargeObject(ctx context.Context, bucketName s
203260

204261
// CopyToFolder copies an object in a bucket to a subfolder in the same bucket.
205262
func (basics BucketBasics) CopyToFolder(ctx context.Context, bucketName string, objectKey string, folderName string) error {
263+
objectDest := fmt.Sprintf("%v/%v", folderName, objectKey)
206264
_, err := basics.S3Client.CopyObject(ctx, &s3.CopyObjectInput{
207265
Bucket: aws.String(bucketName),
208266
CopySource: aws.String(fmt.Sprintf("%v/%v", bucketName, objectKey)),
209-
Key: aws.String(fmt.Sprintf("%v/%v", folderName, objectKey)),
267+
Key: aws.String(objectDest),
210268
})
211269
if err != nil {
212-
log.Printf("Couldn't copy object from %v:%v to %v:%v/%v. Here's why: %v\n",
213-
bucketName, objectKey, bucketName, folderName, objectKey, err)
270+
var notActive *types.ObjectNotInActiveTierError
271+
if errors.As(err, &notActive) {
272+
log.Printf("Couldn't copy object %s from %s because the object isn't in the active tier.\n",
273+
objectKey, bucketName)
274+
err = notActive
275+
}
276+
} else {
277+
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
278+
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: aws.String(objectDest)}, time.Minute)
279+
if err != nil {
280+
log.Printf("Failed attempt to wait for object %s to exist.\n", objectDest)
281+
}
214282
}
215283
return err
216284
}
@@ -227,8 +295,18 @@ func (basics BucketBasics) CopyToBucket(ctx context.Context, sourceBucket string
227295
Key: aws.String(objectKey),
228296
})
229297
if err != nil {
230-
log.Printf("Couldn't copy object from %v:%v to %v:%v. Here's why: %v\n",
231-
sourceBucket, objectKey, destinationBucket, objectKey, err)
298+
var notActive *types.ObjectNotInActiveTierError
299+
if errors.As(err, &notActive) {
300+
log.Printf("Couldn't copy object %s from %s because the object isn't in the active tier.\n",
301+
objectKey, sourceBucket)
302+
err = notActive
303+
}
304+
} else {
305+
err = s3.NewObjectExistsWaiter(basics.S3Client).Wait(
306+
ctx, &s3.HeadObjectInput{Bucket: aws.String(destinationBucket), Key: aws.String(objectKey)}, time.Minute)
307+
if err != nil {
308+
log.Printf("Failed attempt to wait for object %s to exist.\n", objectKey)
309+
}
232310
}
233311
return err
234312
}
@@ -239,16 +317,27 @@ func (basics BucketBasics) CopyToBucket(ctx context.Context, sourceBucket string
239317

240318
// ListObjects lists the objects in a bucket.
241319
func (basics BucketBasics) ListObjects(ctx context.Context, bucketName string) ([]types.Object, error) {
242-
result, err := basics.S3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
320+
var err error
321+
var output *s3.ListObjectsV2Output
322+
input := &s3.ListObjectsV2Input{
243323
Bucket: aws.String(bucketName),
244-
})
245-
var contents []types.Object
246-
if err != nil {
247-
log.Printf("Couldn't list objects in bucket %v. Here's why: %v\n", bucketName, err)
248-
} else {
249-
contents = result.Contents
250324
}
251-
return contents, err
325+
var objects []types.Object
326+
objectPaginator := s3.NewListObjectsV2Paginator(basics.S3Client, input)
327+
for objectPaginator.HasMorePages() {
328+
output, err = objectPaginator.NextPage(ctx)
329+
if err != nil {
330+
var noBucket *types.NoSuchBucket
331+
if errors.As(err, &noBucket) {
332+
log.Printf("Bucket %s does not exist.\n", bucketName)
333+
err = noBucket
334+
}
335+
break
336+
} else {
337+
objects = append(objects, output.Contents...)
338+
}
339+
}
340+
return objects, err
252341
}
253342

254343
// snippet-end:[gov2.s3.ListObjectsV2]
@@ -263,12 +352,32 @@ func (basics BucketBasics) DeleteObjects(ctx context.Context, bucketName string,
263352
}
264353
output, err := basics.S3Client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
265354
Bucket: aws.String(bucketName),
266-
Delete: &types.Delete{Objects: objectIds},
355+
Delete: &types.Delete{Objects: objectIds, Quiet: aws.Bool(true)},
267356
})
268-
if err != nil {
269-
log.Printf("Couldn't delete objects from bucket %v. Here's why: %v\n", bucketName, err)
357+
if err != nil || len(output.Errors) > 0 {
358+
log.Printf("Error deleting objects from bucket %s.\n", bucketName)
359+
if err != nil {
360+
var noBucket *types.NoSuchBucket
361+
if errors.As(err, &noBucket) {
362+
log.Printf("Bucket %s does not exist.\n", bucketName)
363+
err = noBucket
364+
}
365+
} else if len(output.Errors) > 0 {
366+
for _, outErr := range output.Errors {
367+
log.Printf("%s: %s\n", *outErr.Key, *outErr.Message)
368+
}
369+
err = fmt.Errorf("%s", *output.Errors[0].Message)
370+
}
270371
} else {
271-
log.Printf("Deleted %v objects.\n", len(output.Deleted))
372+
for _, delObjs := range output.Deleted {
373+
err = s3.NewObjectNotExistsWaiter(basics.S3Client).Wait(
374+
ctx, &s3.HeadObjectInput{Bucket: aws.String(bucketName), Key: delObjs.Key}, time.Minute)
375+
if err != nil {
376+
log.Printf("Failed attempt to wait for object %s to be deleted.\n", *delObjs.Key)
377+
} else {
378+
log.Printf("Deleted %s.\n", *delObjs.Key)
379+
}
380+
}
272381
}
273382
return err
274383
}
@@ -282,7 +391,21 @@ func (basics BucketBasics) DeleteBucket(ctx context.Context, bucketName string)
282391
_, err := basics.S3Client.DeleteBucket(ctx, &s3.DeleteBucketInput{
283392
Bucket: aws.String(bucketName)})
284393
if err != nil {
285-
log.Printf("Couldn't delete bucket %v. Here's why: %v\n", bucketName, err)
394+
var noBucket *types.NoSuchBucket
395+
if errors.As(err, &noBucket) {
396+
log.Printf("Bucket %s does not exist.\n", bucketName)
397+
err = noBucket
398+
} else {
399+
log.Printf("Couldn't delete bucket %v. Here's why: %v\n", bucketName, err)
400+
}
401+
} else {
402+
err = s3.NewBucketNotExistsWaiter(basics.S3Client).Wait(
403+
ctx, &s3.HeadBucketInput{Bucket: aws.String(bucketName)}, time.Minute)
404+
if err != nil {
405+
log.Printf("Failed attempt to wait for bucket %s to be deleted.\n", bucketName)
406+
} else {
407+
log.Printf("Deleted %s.\n", bucketName)
408+
}
286409
}
287410
return err
288411
}

0 commit comments

Comments
 (0)