Skip to content
Draft
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
44 changes: 44 additions & 0 deletions .github/workflows/contract-testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
api-key: ${{ steps.filter.outputs.api-key }}
auditing: ${{ steps.filter.outputs.auditing }}
backup-compliance-policy: ${{ steps.filter.outputs.backup-compliance-policy }}
cloud-backup-snapshot-export-bucket: ${{ steps.filter.outputs.cloud-backup-snapshot-export-bucket }}
cloud-backup-restore-jobs: ${{ steps.filter.outputs.cloud-backup-restore-jobs }}
cluster-outage-simulation: ${{ steps.filter.outputs.cluster-outage-simulation }}
database-user: ${{ steps.filter.outputs.database-user }}
Expand Down Expand Up @@ -66,6 +67,8 @@ jobs:
- 'cfn-resources/auditing/**'
backup-compliance-policy:
- 'cfn-resources/backup-compliance-policy/**'
cloud-backup-snapshot-export-bucket:
- 'cfn-resources/cloud-backup-snapshot-export-bucket/**'
cloud-backup-restore-jobs:
- 'cfn-resources/cloud-backup-restore-jobs/**'
cluster-outage-simulation:
Expand Down Expand Up @@ -335,6 +338,47 @@ jobs:

make run-contract-testing
make delete-test-resources
cloud-backup-snapshot-export-bucket:
needs: change-detection
if: ${{ needs.change-detection.outputs.cloud-backup-snapshot-export-bucket == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5
with:
go-version-file: 'cfn-resources/go.mod'
- name: setup Atlas CLI
uses: mongodb/atlas-github-action@e3c9e0204659bafbb3b65e1eb1ee745cca0e9f3b
- uses: aws-actions/setup-sam@d78e1a4a9656d3b223e59b80676a797f20093133
with:
use-installer: true
- uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_TEST_ENV }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_TEST_ENV }}
aws-region: eu-west-1
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: '3.9'
cache: 'pip' # caching pip dependencies
- run: pip install cloudformation-cli cloudformation-cli-go-plugin
- name: Run the Contract test
shell: bash
env:
MONGODB_ATLAS_PUBLIC_API_KEY: ${{ secrets.CLOUD_DEV_PUBLIC_KEY }}
MONGODB_ATLAS_PRIVATE_API_KEY: ${{ secrets.CLOUD_DEV_PRIVATE_KEY }}
MONGODB_ATLAS_ORG_ID: ${{ secrets.CLOUD_DEV_ORG_ID }}
MONGODB_ATLAS_OPS_MANAGER_URL: ${{ vars.MONGODB_ATLAS_BASE_URL }}
MONGODB_ATLAS_PROFILE: cfn-cloud-dev-github-action
run: |
pushd cfn-resources/cloud-backup-snapshot-export-bucket
make create-test-resources

cat inputs/inputs_1_create.json

make run-contract-testing
make delete-test-resources

cloud-backup-restore-jobs:
needs: change-detection
if: ${{ needs.change-detection.outputs.cloud-backup-restore-jobs == 'true' }}
Expand Down
22 changes: 20 additions & 2 deletions cfn-resources/cloud-backup-snapshot-export-bucket/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build debug clean
.PHONY: build test clean
tags=logging callback metrics scheduler
cgo=0
goos=linux
Expand All @@ -13,7 +13,25 @@ build:

debug:
cfn generate
env GOOS=$(goos) CGO_ENABLED=$(cgo) GOARCH=$(goarch) go build -ldflags="$(ldXflagsD)" -tags="$(tags)" -o bin/bootstrap cmd/main.go
env GOOS=$(goos) CGO_ENABLED=$(cgo) GOARCH=$(goarch) go build -ldflags="$(ldXflagsD)" -tags="$(tags)" -o bin/debug cmd/main.go
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The debug target builds to bin/debug, but the CloudFormation RPDK tooling (and repo helper scripts like cfn-submit-helper.sh / cfn-testing-helper.sh) expect the executable at bin/bootstrap even for debug builds. Consider keeping the output path as bin/bootstrap and only changing the ldflags/log level for debug.

Suggested change
env GOOS=$(goos) CGO_ENABLED=$(cgo) GOARCH=$(goarch) go build -ldflags="$(ldXflagsD)" -tags="$(tags)" -o bin/debug cmd/main.go
env GOOS=$(goos) CGO_ENABLED=$(cgo) GOARCH=$(goarch) go build -ldflags="$(ldXflagsD)" -tags="$(tags)" -o bin/bootstrap cmd/main.go

Copilot uses AI. Check for mistakes.

clean:
rm -rf bin

submit: clean build # submit to private registry must use release build not debug build
@echo "==> Submitting to private registry for testing"
cfn submit --set-default --region us-east-1

create-test-resources:
@echo "==> Creating test files and resources for contract testing"
./test/contract-testing/cfn-test-create.sh

delete-test-resources:
@echo "==> Delete test resources used for contract testing"
./test/contract-testing/cfn-test-delete.sh

run-contract-testing:
@echo "==> Run contract testing"
make build
sam local start-lambda &
cfn test --function-name TestEntrypoint --verbose

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

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

Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ import (
"errors"

"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/mongodb/mongodbatlas-cloudformation-resources/util"
"github.com/mongodb/mongodbatlas-cloudformation-resources/util/constants"
"github.com/mongodb/mongodbatlas-cloudformation-resources/util/progressevent"
"github.com/mongodb/mongodbatlas-cloudformation-resources/util/validator"
admin20231115002 "go.mongodb.org/atlas-sdk/v20231115002/admin"
"go.mongodb.org/atlas-sdk/v20250312013/admin"
)

const (
Expand Down Expand Up @@ -53,17 +52,18 @@ func Create(req handler.Request, prevModel *Model, currentModel *Model) (handler
return *pe, nil
}

params := &admin20231115002.DiskBackupSnapshotAWSExportBucket{
BucketName: currentModel.BucketName,
CloudProvider: aws.String(constants.AWS),
IamRoleId: currentModel.IamRoleID,
params := &admin.DiskBackupSnapshotExportBucketRequest{
BucketName: currentModel.BucketName,
CloudProvider: constants.AWS,
IamRoleId: currentModel.IamRoleID,
RequirePrivateNetworking: currentModel.RequirePrivateNetworking,
}
output, resp, err := client.Atlas20231115002.CloudBackupsApi.CreateExportBucket(context.Background(), *currentModel.ProjectId, params).Execute()
output, resp, err := client.AtlasSDK.CloudBackupsApi.CreateExportBucket(context.Background(), *currentModel.ProjectId, params).Execute()
if err != nil {
return progressevent.GetFailedEventByResponse(err.Error(), resp), nil
}

currentModel.Id = output.Id
currentModel.Id = &output.Id

return handler.ProgressEvent{
OperationStatus: handler.Success,
Expand All @@ -84,7 +84,7 @@ func Read(req handler.Request, prevModel *Model, currentModel *Model) (handler.P
return *pe, nil
}

output, resp, err := client.Atlas20231115002.CloudBackupsApi.GetExportBucket(context.Background(), *currentModel.ProjectId, *currentModel.Id).Execute()
output, resp, err := client.AtlasSDK.CloudBackupsApi.GetExportBucket(context.Background(), *currentModel.ProjectId, *currentModel.Id).Execute()
if err != nil {
return progressevent.GetFailedEventByResponse(err.Error(), resp), nil
}
Expand Down Expand Up @@ -114,7 +114,7 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler
return *pe, nil
}

_, resp, err := client.Atlas20231115002.CloudBackupsApi.DeleteExportBucket(context.Background(), *currentModel.ProjectId, *currentModel.Id).Execute()
resp, err := client.AtlasSDK.CloudBackupsApi.DeleteExportBucket(context.Background(), *currentModel.ProjectId, *currentModel.Id).Execute()
if err != nil {
return progressevent.GetFailedEventByResponse(err.Error(), resp), nil
}
Expand All @@ -137,20 +137,22 @@ func List(req handler.Request, prevModel *Model, currentModel *Model) (handler.P
return *pe, nil
}

output, resp, err := client.Atlas20231115002.CloudBackupsApi.ListExportBuckets(context.Background(), *currentModel.ProjectId).Execute()
output, resp, err := client.AtlasSDK.CloudBackupsApi.ListExportBuckets(context.Background(), *currentModel.ProjectId).Execute()
if err != nil {
return progressevent.GetFailedEventByResponse(err.Error(), resp), nil
}

resultList := make([]interface{}, 0)
resultList := make([]any, 0)

for i := range output.Results {
model := Model{
ProjectId: currentModel.ProjectId,
Profile: currentModel.Profile,
if output.Results != nil {
for i := range *output.Results {
model := Model{
ProjectId: currentModel.ProjectId,
Profile: currentModel.Profile,
}
model.updateModel(&(*output.Results)[i])
resultList = append(resultList, model)
}
model.updateModel(&output.Results[i])
resultList = append(resultList, model)
}

return handler.ProgressEvent{
Expand All @@ -160,8 +162,9 @@ func List(req handler.Request, prevModel *Model, currentModel *Model) (handler.P
}, nil
}

func (m *Model) updateModel(bucket *admin20231115002.DiskBackupSnapshotAWSExportBucket) {
m.BucketName = bucket.BucketName
func (m *Model) updateModel(bucket *admin.DiskBackupSnapshotExportBucketResponse) {
m.Id = &bucket.Id
m.BucketName = &bucket.BucketName
m.IamRoleID = bucket.IamRoleId
m.Id = bucket.Id
m.RequirePrivateNetworking = bucket.RequirePrivateNetworking
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ To declare this entity in your AWS CloudFormation template, use the following sy
"<a href="#bucketname" title="BucketName">BucketName</a>" : <i>String</i>,
"<a href="#projectid" title="ProjectId">ProjectId</a>" : <i>String</i>,
"<a href="#iamroleid" title="IamRoleID">IamRoleID</a>" : <i>String</i>,
"<a href="#requireprivatenetworking" title="RequirePrivateNetworking">RequirePrivateNetworking</a>" : <i>Boolean</i>,
}
}
</pre>
Expand All @@ -29,6 +30,7 @@ Properties:
<a href="#bucketname" title="BucketName">BucketName</a>: <i>String</i>
<a href="#projectid" title="ProjectId">ProjectId</a>: <i>String</i>
<a href="#iamroleid" title="IamRoleID">IamRoleID</a>: <i>String</i>
<a href="#requireprivatenetworking" title="RequirePrivateNetworking">RequirePrivateNetworking</a>: <i>Boolean</i>
</pre>

## Properties
Expand Down Expand Up @@ -61,9 +63,9 @@ _Required_: Yes

_Type_: String

_Minimum_: <code>24</code>
_Minimum Length_: <code>24</code>

_Maximum_: <code>24</code>
_Maximum Length_: <code>24</code>

_Pattern_: <code>^([a-f0-9]{24})$</code>

Expand All @@ -77,14 +79,24 @@ _Required_: Yes

_Type_: String

_Minimum_: <code>24</code>
_Minimum Length_: <code>24</code>

_Maximum_: <code>24</code>
_Maximum Length_: <code>24</code>

_Pattern_: <code>^([a-f0-9]{24})$</code>

_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)

#### RequirePrivateNetworking

Indicates whether to do exports over PrivateLink as opposed to public IPs. Defaults to false.

_Required_: No

_Type_: Boolean

_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)

## Return Values

### Fn::GetAtt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"minLength": 24,
"pattern": "^([a-f0-9]{24})$"
},
"RequirePrivateNetworking": {
"type": "boolean",
"description": "Indicates whether to do exports over PrivateLink as opposed to public IPs. Defaults to false."
},
"Id": {
"type": "string",
"description": "Unique 24-hexadecimal character string that identifies the Amazon Web Services (AWS) Simple Storage Service (S3) export bucket.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,28 @@ Resources:
Properties:
MaxSessionDuration: 8400
AssumeRolePolicyDocument:
Version: '2012-10-17'
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: resources.cloudformation.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount:
Ref: AWS::AccountId
StringLike:
aws:SourceArn:
Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/resource/MongoDB-Atlas-CloudBackupSnapshotExportBucket/*
Path: "/"
Policies:
- PolicyName: ResourceTypePolicy
PolicyDocument:
Version: '2012-10-17'
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "secretsmanager:CreateSecret"
- "secretsmanager:DescribeSecret"
- "secretsmanager:GetSecretValue"
- "secretsmanager:PutSecretValue"
- "secretsmanager:UpdateSecretVersionStage"
- "ec2:CreateVpcEndpoint"
- "ec2:DeleteVpcEndpoints"
- "cloudformation:CreateResource"
- "cloudformation:DeleteResource"
- "cloudformation:GetResource"
- "cloudformation:GetResourceRequestStatus"
- "cloudformation:ListResources"
- "cloudformation:UpdateResource"
- "iam:AttachRolePolicy"
- "iam:CreateRole"
- "iam:DeleteRole"
- "iam:GetRole"
- "iam:GetRolePolicy"
- "iam:ListAttachedRolePolicies"
- "iam:ListRolePolicies"
- "iam:PutRolePolicy"
- "secretsmanager:GetSecretValue"
Resource: "*"
Outputs:
ExecutionRoleArn:
Expand Down
Loading
Loading