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

Presigned URL generation for Neptune and DocDB CopyDBClusterSnapshot & CreateDBCluster #3782

Merged
merged 2 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
### SDK Features

### SDK Enhancements
* `service/neptune`: Support for PreSignedUrl generation for CopyDBClusterSnapshot and CreateDBCluster operations. ([#3782](https://github.com/aws/aws-sdk-go/pull/3782))
* `service/docdb`: Support for PreSignedUrl generation for CopyDBClusterSnapshot and CreateDBCluster operations. ([#3782](https://github.com/aws/aws-sdk-go/pull/3782))

### SDK Bugs
33 changes: 30 additions & 3 deletions private/model/api/customization_passes.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func (a *API) customizationPasses() error {
"s3control": s3ControlCustomizations,
"cloudfront": cloudfrontCustomizations,
"rds": rdsCustomizations,
"neptune": neptuneCustomizations,
"docdb": docdbCustomizations,

// Disable endpoint resolving for services that require customer
// to provide endpoint them selves.
Expand Down Expand Up @@ -327,7 +329,34 @@ func rdsCustomizations(a *API) error {
"CreateDBClusterInput",
"StartDBInstanceAutomatedBackupsReplicationInput",
}
for _, input := range inputs {
generatePresignedURL(a, inputs)
return nil
}

// neptuneCustomizations are customization for the service/neptune. This adds
// non-modeled fields used for presigning.
func neptuneCustomizations(a *API) error {
inputs := []string{
"CopyDBClusterSnapshotInput",
"CreateDBClusterInput",
}
generatePresignedURL(a, inputs)
return nil
}

// neptuneCustomizations are customization for the service/neptune. This adds
// non-modeled fields used for presigning.
func docdbCustomizations(a *API) error {
inputs := []string{
"CopyDBClusterSnapshotInput",
"CreateDBClusterInput",
}
generatePresignedURL(a, inputs)
return nil
}

func generatePresignedURL(a *API, inputShapes []string) {
for _, input := range inputShapes {
if ref, ok := a.Shapes[input]; ok {
ref.MemberRefs["SourceRegion"] = &ShapeRef{
Documentation: docstring(`SourceRegion is the source region where the resource exists. This is not sent over the wire and is only used for presigning. This value should always have the same region as the source ARN.`),
Expand All @@ -342,8 +371,6 @@ func rdsCustomizations(a *API) error {
}
}
}

return nil
}

func disableEndpointResolving(a *API) error {
Expand Down
40 changes: 40 additions & 0 deletions service/docdb/api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 107 additions & 0 deletions service/docdb/customizations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package docdb

import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/request"
)

func init() {
ops := []string{
opCopyDBClusterSnapshot,
opCreateDBCluster,
}
initRequest = func(r *request.Request) {
for _, operation := range ops {
if r.Operation.Name == operation {
r.Handlers.Build.PushFront(fillPresignedURL)
}
}
}
}

func fillPresignedURL(r *request.Request) {
fns := map[string]func(r *request.Request){
opCopyDBClusterSnapshot: copyDBClusterSnapshotPresign,
opCreateDBCluster: createDBClusterPresign,
}
if !r.ParamsFilled() {
return
}
if f, ok := fns[r.Operation.Name]; ok {
f(r)
}
}

func copyDBClusterSnapshotPresign(r *request.Request) {
originParams := r.Params.(*CopyDBClusterSnapshotInput)

if originParams.SourceRegion == nil || originParams.PreSignedUrl != nil || originParams.DestinationRegion != nil {
return
}

originParams.DestinationRegion = r.Config.Region
// preSignedUrl is not required for instances in the same region.
if *originParams.SourceRegion == *originParams.DestinationRegion {
return
}

newParams := awsutil.CopyOf(r.Params).(*CopyDBClusterSnapshotInput)
originParams.PreSignedUrl = presignURL(r, originParams.SourceRegion, newParams)
}

func createDBClusterPresign(r *request.Request) {
originParams := r.Params.(*CreateDBClusterInput)

if originParams.SourceRegion == nil || originParams.PreSignedUrl != nil || originParams.DestinationRegion != nil {
return
}

originParams.DestinationRegion = r.Config.Region
// preSignedUrl is not required for instances in the same region.
if *originParams.SourceRegion == *originParams.DestinationRegion {
return
}

newParams := awsutil.CopyOf(r.Params).(*CreateDBClusterInput)
originParams.PreSignedUrl = presignURL(r, originParams.SourceRegion, newParams)
}

// presignURL will presign the request by using SoureRegion to sign with. SourceRegion is not
// sent to the service, and is only used to not have the SDKs parsing ARNs.
func presignURL(r *request.Request, sourceRegion *string, newParams interface{}) *string {
cfg := r.Config.Copy(aws.NewConfig().
WithEndpoint("").
WithRegion(aws.StringValue(sourceRegion)))

clientInfo := r.ClientInfo
resolved, err := r.Config.EndpointResolver.EndpointFor(
EndpointsID, aws.StringValue(cfg.Region),
func(opt *endpoints.Options) {
opt.DisableSSL = aws.BoolValue(cfg.DisableSSL)
opt.UseDualStack = aws.BoolValue(cfg.UseDualStack)
},
)
if err != nil {
r.Error = err
return nil
}

clientInfo.Endpoint = resolved.URL
clientInfo.SigningRegion = resolved.SigningRegion

// Presign a request with modified params
req := request.New(*cfg, clientInfo, r.Handlers, r.Retryer, r.Operation, newParams, r.Data)
req.Operation.HTTPMethod = "GET"
uri, err := req.Presign(5 * time.Minute) // 5 minutes should be enough.
if err != nil { // bubble error back up to original request
r.Error = err
return nil
}

// We have our URL, set it on params
return &uri
}
Loading