Skip to content

Commit

Permalink
Merge commit '83d18fb4234bf48f206e3e984402ef2e9c71ec94' into release
Browse files Browse the repository at this point in the history
* commit '83d18fb4234bf48f206e3e984402ef2e9c71ec94':
  adds support for s3 outpost accesspoint arns and s3control outposts accesspoint, outposts bucket arns
  • Loading branch information
awssdkgo committed Sep 30, 2020
2 parents 235bdca + 83d18fb commit 6433362
Show file tree
Hide file tree
Showing 32 changed files with 10,833 additions and 2,407 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
### SDK Features
* `service/s3`: Adds support for outposts access point ARNs.
* `service/s3control`: Adds support for S3 on outposts access point and S3 on outposts bucket ARNs.

### SDK Enhancements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,28 @@ func (a AccessPointARN) GetARN() arn.ARN {

// ParseAccessPointResource attempts to parse the ARN's resource as an
// AccessPoint resource.
//
// Supported Access point resource format:
// - Access point format: arn:{partition}:s3:{region}:{accountId}:accesspoint/{accesspointName}
// - example: arn.aws.s3.us-west-2.012345678901:accesspoint/myaccesspoint
//
func ParseAccessPointResource(a arn.ARN, resParts []string) (AccessPointARN, error) {
if len(a.Region) == 0 {
return AccessPointARN{}, InvalidARNError{a, "region not set"}
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "region not set"}
}
if len(a.AccountID) == 0 {
return AccessPointARN{}, InvalidARNError{a, "account-id not set"}
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "account-id not set"}
}
if len(resParts) == 0 {
return AccessPointARN{}, InvalidARNError{a, "resource-id not set"}
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "resource-id not set"}
}
if len(resParts) > 1 {
return AccessPointARN{}, InvalidARNError{a, "sub resource not supported"}
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "sub resource not supported"}
}

resID := resParts[0]
if len(strings.TrimSpace(resID)) == 0 {
return AccessPointARN{}, InvalidARNError{a, "resource-id not set"}
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "resource-id not set"}
}

return AccessPointARN{
Expand Down
13 changes: 8 additions & 5 deletions service/s3/internal/arn/arn.go → internal/s3shared/arn/arn.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package arn

import (
"fmt"
"strings"

"github.com/aws/aws-sdk-go/aws/arn"
Expand All @@ -25,13 +26,14 @@ func ParseResource(s string, resParser ResourceParser) (resARN Resource, err err
}

if len(a.Partition) == 0 {
return nil, InvalidARNError{a, "partition not set"}
return nil, InvalidARNError{ARN: a, Reason: "partition not set"}
}
if a.Service != "s3" {
return nil, InvalidARNError{a, "service is not S3"}

if a.Service != "s3" && a.Service != "s3-outposts" {
return nil, InvalidARNError{ARN: a, Reason: "service is not supported"}
}
if len(a.Resource) == 0 {
return nil, InvalidARNError{a, "resource not set"}
return nil, InvalidARNError{ARN: a, Reason: "resource not set"}
}

return resParser(a)
Expand Down Expand Up @@ -66,6 +68,7 @@ type InvalidARNError struct {
Reason string
}

// Error returns a string denoting the occurred InvalidARNError
func (e InvalidARNError) Error() string {
return "invalid Amazon S3 ARN, " + e.Reason + ", " + e.ARN.String()
return fmt.Sprintf("invalid Amazon %s ARN, %s, %s", e.ARN.Service, e.Reason, e.ARN.String())
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestParseResource(t *testing.T) {
},
"Not S3 ARN": {
Input: "arn:aws:sqs:us-west-2:012345678901:accesspoint",
ExpectErr: "service is not S3",
ExpectErr: "service is not supported",
},
"No Resource": {
Input: "arn:aws:s3:us-west-2:012345678901:",
Expand Down Expand Up @@ -120,7 +120,7 @@ func mappedResourceParser(kinds map[string]func(arn.ARN, []string) (Resource, er

fn, ok := kinds[parts[0]]
if !ok {
return nil, InvalidARNError{a, "unknown resource type"}
return nil, InvalidARNError{ARN: a, Reason: "unknown resource type"}
}
return fn(a, parts[1:])
}
Expand Down
126 changes: 126 additions & 0 deletions internal/s3shared/arn/outpost_arn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package arn

import (
"strings"

"github.com/aws/aws-sdk-go/aws/arn"
)

// OutpostARN interface that should be satisfied by outpost ARNs
type OutpostARN interface {
Resource
GetOutpostID() string
}

// ParseOutpostARNResource will parse a provided ARNs resource using the appropriate ARN format
// and return a specific OutpostARN type
//
// Currently supported outpost ARN formats:
// * Outpost AccessPoint ARN format:
// - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/accesspoint/{accesspointName}
// - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/accesspoint/myaccesspoint
//
// * Outpost Bucket ARN format:
// - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/bucket/{bucketName}
// - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/bucket/mybucket
//
// Other outpost ARN formats may be supported and added in the future.
//
func ParseOutpostARNResource(a arn.ARN, resParts []string) (OutpostARN, error) {
if len(a.Region) == 0 {
return nil, InvalidARNError{ARN: a, Reason: "region not set"}
}

if len(a.AccountID) == 0 {
return nil, InvalidARNError{ARN: a, Reason: "account-id not set"}
}

// verify if outpost id is present and valid
if len(resParts) == 0 || len(strings.TrimSpace(resParts[0])) == 0 {
return nil, InvalidARNError{ARN: a, Reason: "outpost resource-id not set"}
}

// verify possible resource type exists
if len(resParts) < 3 {
return nil, InvalidARNError{
ARN: a, Reason: "incomplete outpost resource type. Expected bucket or access-point resource to be present",
}
}

// Since we know this is a OutpostARN fetch outpostID
outpostID := strings.TrimSpace(resParts[0])

switch resParts[1] {
case "accesspoint":
accesspointARN, err := ParseAccessPointResource(a, resParts[2:])
if err != nil {
return OutpostAccessPointARN{}, err
}
return OutpostAccessPointARN{
AccessPointARN: accesspointARN,
OutpostID: outpostID,
}, nil

case "bucket":
bucketName, err := parseBucketResource(a, resParts[2:])
if err != nil {
return nil, err
}
return OutpostBucketARN{
ARN: a,
BucketName: bucketName,
OutpostID: outpostID,
}, nil

default:
return nil, InvalidARNError{ARN: a, Reason: "unknown resource set for outpost ARN"}
}
}

// OutpostAccessPointARN represents outpost access point ARN.
type OutpostAccessPointARN struct {
AccessPointARN
OutpostID string
}

// GetOutpostID returns the outpost id of outpost access point arn
func (o OutpostAccessPointARN) GetOutpostID() string {
return o.OutpostID
}

// OutpostBucketARN represents the outpost bucket ARN.
type OutpostBucketARN struct {
arn.ARN
BucketName string
OutpostID string
}

// GetOutpostID returns the outpost id of outpost bucket arn
func (o OutpostBucketARN) GetOutpostID() string {
return o.OutpostID
}

// GetARN retrives the base ARN from outpost bucket ARN resource
func (o OutpostBucketARN) GetARN() arn.ARN {
return o.ARN
}

// parseBucketResource attempts to parse the ARN's bucket resource and retrieve the
// bucket resource id.
//
// parseBucketResource only parses the bucket resource id.
//
func parseBucketResource(a arn.ARN, resParts []string) (bucketName string, err error) {
if len(resParts) == 0 {
return bucketName, InvalidARNError{ARN: a, Reason: "bucket resource-id not set"}
}
if len(resParts) > 1 {
return bucketName, InvalidARNError{ARN: a, Reason: "sub resource not supported"}
}

bucketName = strings.TrimSpace(resParts[0])
if len(bucketName) == 0 {
return bucketName, InvalidARNError{ARN: a, Reason: "bucket resource-id not set"}
}
return bucketName, err
}
Loading

0 comments on commit 6433362

Please sign in to comment.