Skip to content

Commit 209048c

Browse files
Merge master into CLOUDP-369800-backup-compliance-policy: resolved constants conflict
2 parents 2207423 + 2941cea commit 209048c

95 files changed

Lines changed: 5344 additions & 529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/contract-testing.yaml

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
search-deployment: ${{ steps.filter.outputs.search-deployment }}
3232
stream-connection: ${{ steps.filter.outputs.stream-connection }}
3333
stream-instance: ${{ steps.filter.outputs.stream-instance }}
34+
stream-processor: ${{ steps.filter.outputs.stream-processor }}
3435
stream-workspace: ${{ steps.filter.outputs.stream-workspace }}
3536
steps:
3637
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
@@ -79,6 +80,8 @@ jobs:
7980
- 'cfn-resources/stream-connection/**'
8081
stream-instance:
8182
- 'cfn-resources/stream-instance/**'
83+
stream-processor:
84+
- 'cfn-resources/stream-processor/**'
8285
stream-workspace:
8386
- 'cfn-resources/stream-workspace/**'
8487
access-list-api-key:
@@ -899,6 +902,46 @@ jobs:
899902
pushd cfn-resources/stream-instance
900903
make create-test-resources
901904
cat inputs/inputs_1_create.json
905+
make run-contract-testing
906+
make delete-test-resources
907+
stream-processor:
908+
needs: change-detection
909+
if: ${{ needs.change-detection.outputs.stream-processor == 'true' }}
910+
runs-on: ubuntu-latest
911+
steps:
912+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
913+
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5
914+
with:
915+
go-version-file: 'cfn-resources/go.mod'
916+
- name: setup Atlas CLI
917+
uses: mongodb/atlas-github-action@e3c9e0204659bafbb3b65e1eb1ee745cca0e9f3b
918+
- uses: aws-actions/setup-sam@c2a20b1822cc4a6bc594ff7f1dbb658758e383c3
919+
with:
920+
use-installer: true
921+
- uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708
922+
with:
923+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_TEST_ENV }}
924+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_TEST_ENV }}
925+
aws-region: eu-west-1
926+
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548
927+
with:
928+
python-version: '3.9'
929+
cache: 'pip' # caching pip dependencies
930+
- run: pip install cloudformation-cli cloudformation-cli-go-plugin
931+
- name: Run the Contract test
932+
shell: bash
933+
env:
934+
MONGODB_ATLAS_PUBLIC_API_KEY: ${{ secrets.CLOUD_DEV_PUBLIC_KEY }}
935+
MONGODB_ATLAS_PRIVATE_API_KEY: ${{ secrets.CLOUD_DEV_PRIVATE_KEY }}
936+
MONGODB_ATLAS_ORG_ID: ${{ secrets.CLOUD_DEV_ORG_ID }}
937+
MONGODB_ATLAS_OPS_MANAGER_URL: ${{ vars.MONGODB_ATLAS_BASE_URL }}
938+
MONGODB_ATLAS_PROFILE: cfn-cloud-dev-github-action
939+
run: |
940+
cd cfn-resources/stream-processor
941+
make create-test-resources
942+
943+
cat inputs/*
944+
902945
make run-contract-testing
903946
make delete-test-resources
904947
stream-workspace:
@@ -941,4 +984,4 @@ jobs:
941984
cat inputs/inputs_1_update.json
942985
943986
make run-contract-testing
944-
make delete-test-resources
987+
make delete-test-resources

cfn-resources/organization/cmd/resource/config.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cfn-resources/organization/cmd/resource/model.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cfn-resources/organization/cmd/resource/resource.go

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,37 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler
217217

218218
// If exists
219219
_, response, err = currentModel.getOrgDetails(ctx, conn, currentModel)
220-
if err != nil && response.StatusCode == http.StatusUnauthorized {
220+
if err != nil && util.StatusUnauthorized(response) {
221221
return handleError(response, constants.DELETE, err)
222222
}
223223

224+
currentModel.IsDeleted = util.Pointer(false)
225+
226+
responseMsg, progressEvent := runDelete(ctx, conn, currentModel)
227+
if responseMsg.Error != nil {
228+
// Retry once on transient server error, waiting 20 seconds before retrying request
229+
// This covers case of contract tests which create and delete an org within seconds, encountering an error while deleting the org
230+
if responseMsg.Response != nil && responseMsg.Response.StatusCode == http.StatusInternalServerError {
231+
_, _ = logger.Warnf("Transient server error while deleting organization, retrying in 20 seconds")
232+
time.Sleep(20 * time.Second)
233+
responseMsg, progressEvent = runDelete(ctx, conn, currentModel)
234+
}
235+
}
236+
if progressEvent != nil {
237+
return *progressEvent, nil
238+
}
239+
if responseMsg.Error != nil {
240+
return handleError(responseMsg.Response, constants.DELETE, responseMsg.Error)
241+
}
242+
243+
return handler.ProgressEvent{
244+
OperationStatus: handler.Success,
245+
Message: DeleteCompleted,
246+
ResourceModel: nil}, nil
247+
}
248+
249+
// Encapsulate the delete+wait logic so the same flow can be used on retry.
250+
func runDelete(ctx context.Context, conn *admin.APIClient, currentModel *Model) (*DeleteResponse, *handler.ProgressEvent) {
224251
deleteRequest := conn.OrganizationsApi.DeleteOrg(ctx, *currentModel.OrgId)
225252

226253
// Since the Delete API is synchronous and takes more than 1 minute most of the time,
@@ -234,38 +261,29 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler
234261
responseChan <- DeleteResponse{Error: err, Response: response}
235262
}()
236263

237-
currentModel.IsDeleted = util.Pointer(false)
238264
select {
239265
case responseMsg := <-responseChan:
240-
if responseMsg.Error != nil {
241-
return handleError(responseMsg.Response, constants.DELETE, responseMsg.Error)
242-
}
243-
266+
return &responseMsg, nil
244267
case <-time.After(30 * time.Second):
245268
// If the Delete is not completed in the above time,
246269
// we return a progress event with inProgress status and callback context
247-
return handler.ProgressEvent{
270+
return nil, &handler.ProgressEvent{
248271
OperationStatus: handler.InProgress,
249272
Message: DeleteInProgress,
250273
ResourceModel: currentModel,
251274
CallbackDelaySeconds: CallBackSeconds,
252275
CallbackContext: map[string]interface{}{
253276
constants.StateName: DeletingState,
254277
},
255-
}, nil
278+
}
256279
}
257-
258-
return handler.ProgressEvent{
259-
OperationStatus: handler.Success,
260-
Message: DeleteCompleted,
261-
ResourceModel: nil}, nil
262280
}
263281

264282
func deleteCallback(ctx context.Context, conn *admin.APIClient, currentModel *Model) (handler.ProgressEvent, error) {
265283
// Read before delete
266284
org, response, err := currentModel.getOrgDetails(ctx, conn, currentModel)
267285
if err != nil {
268-
if response.StatusCode == http.StatusUnauthorized {
286+
if util.StatusUnauthorized(response) {
269287
return handler.ProgressEvent{
270288
OperationStatus: handler.Success,
271289
Message: DeleteCompleted,
@@ -315,28 +333,29 @@ func (model *Model) getOrgDetails(ctx context.Context, conn *admin.APIClient, cu
315333
model.MultiFactorAuthRequired = settings.MultiFactorAuthRequired
316334
model.RestrictEmployeeAccess = settings.RestrictEmployeeAccess
317335
model.GenAIFeaturesEnabled = settings.GenAIFeaturesEnabled
336+
model.SecurityContact = settings.SecurityContact
318337

319338
return model, response, nil
320339
}
321340

322341
func handleError(response *http.Response, method constants.CfnFunctions, err error) (handler.ProgressEvent, error) {
323342
errMsg := fmt.Sprintf("%s error:%s", method, err.Error())
324343
_, _ = logger.Warn(errMsg)
325-
if response.StatusCode == http.StatusConflict {
344+
if util.StatusConflict(response) {
326345
return handler.ProgressEvent{
327346
OperationStatus: handler.Failed,
328347
Message: errMsg,
329348
HandlerErrorCode: string(types.HandlerErrorCodeAlreadyExists)}, nil
330349
}
331350

332-
if response.StatusCode == http.StatusUnauthorized {
351+
if util.StatusUnauthorized(response) {
333352
return handler.ProgressEvent{
334353
OperationStatus: handler.Failed,
335354
Message: "Not found",
336355
HandlerErrorCode: string(types.HandlerErrorCodeNotFound)}, nil
337356
}
338357

339-
if response.StatusCode == http.StatusBadRequest {
358+
if util.StatusBadRequest(response) {
340359
return handler.ProgressEvent{
341360
OperationStatus: handler.Failed,
342361
Message: errMsg,
@@ -359,6 +378,7 @@ func newOrganizationSettings(model *Model) *admin.OrganizationSettings {
359378
MultiFactorAuthRequired: model.MultiFactorAuthRequired,
360379
RestrictEmployeeAccess: model.RestrictEmployeeAccess,
361380
GenAIFeaturesEnabled: model.GenAIFeaturesEnabled,
381+
SecurityContact: model.SecurityContact,
362382
}
363383
}
364384

cfn-resources/organization/docs/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ To declare this entity in your AWS CloudFormation template, use the following sy
2323
"<a href="#isdeleted" title="IsDeleted">IsDeleted</a>" : <i>Boolean</i>,
2424
"<a href="#apiaccesslistrequired" title="ApiAccessListRequired">ApiAccessListRequired</a>" : <i>Boolean</i>,
2525
"<a href="#multifactorauthrequired" title="MultiFactorAuthRequired">MultiFactorAuthRequired</a>" : <i>Boolean</i>,
26-
"<a href="#restrictemployeeaccess" title="RestrictEmployeeAccess">RestrictEmployeeAccess</a>" : <i>Boolean</i>
26+
"<a href="#restrictemployeeaccess" title="RestrictEmployeeAccess">RestrictEmployeeAccess</a>" : <i>Boolean</i>,
27+
"<a href="#securitycontact" title="SecurityContact">SecurityContact</a>" : <i>String</i>
2728
}
2829
}
2930
</pre>
@@ -45,6 +46,7 @@ Properties:
4546
<a href="#apiaccesslistrequired" title="ApiAccessListRequired">ApiAccessListRequired</a>: <i>Boolean</i>
4647
<a href="#multifactorauthrequired" title="MultiFactorAuthRequired">MultiFactorAuthRequired</a>: <i>Boolean</i>
4748
<a href="#restrictemployeeaccess" title="RestrictEmployeeAccess">RestrictEmployeeAccess</a>: <i>Boolean</i>
49+
<a href="#securitycontact" title="SecurityContact">SecurityContact</a>: <i>String</i>
4850
</pre>
4951

5052
## Properties
@@ -173,6 +175,16 @@ _Type_: Boolean
173175

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

178+
#### SecurityContact
179+
180+
Email address of the security contact for the organization.
181+
182+
_Required_: No
183+
184+
_Type_: String
185+
186+
_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
187+
176188
## Return Values
177189

178190
### Fn::GetAtt

cfn-resources/organization/mongodb-atlas-organization.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
"RestrictEmployeeAccess": {
8484
"type": "boolean",
8585
"description": "Flag that indicates whether to block MongoDB Support from accessing Atlas infrastructure for any deployment in the specified organization without explicit permission. Once this setting is turned on, you can grant MongoDB Support a 24-hour bypass access to the Atlas deployment to resolve support issues. To learn more, see: https://www.mongodb.com/docs/atlas/security-restrict-support-access/."
86+
},
87+
"SecurityContact": {
88+
"type": "string",
89+
"description": "Email address of the security contact for the organization."
8690
}
8791
},
8892
"additionalProperties": false,

cfn-resources/organization/test/inputs_1_create.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
"RestrictEmployeeAccess": "false",
1515
"ApiAccessListRequired": "false",
1616
"SkipDefaultAlertsSettings": "true",
17-
"GenAIFeaturesEnabled": "true"
17+
"GenAIFeaturesEnabled": "true",
18+
"SecurityContact": "security-test@example.com"
1819
}

cfn-resources/organization/test/inputs_1_update.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
"RestrictEmployeeAccess": "true",
1515
"ApiAccessListRequired": "false",
1616
"SkipDefaultAlertsSettings": "false",
17-
"GenAIFeaturesEnabled": "false"
17+
"GenAIFeaturesEnabled": "false",
18+
"SecurityContact": "security-updated@example.com"
1819
}

cfn-resources/search-deployment/cmd/resource/mappings.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
package resource
1616

17-
import admin20231115014 "go.mongodb.org/atlas-sdk/v20231115014/admin"
17+
import "go.mongodb.org/atlas-sdk/v20250312012/admin"
1818

19-
func NewCFNSearchDeployment(prevModel *Model, apiResp *admin20231115014.ApiSearchDeploymentResponse) Model {
19+
func NewCFNSearchDeployment(prevModel *Model, apiResp *admin.ApiSearchDeploymentResponse) Model {
2020
respSpecs := apiResp.GetSpecs()
2121
resultSpecs := make([]ApiSearchDeploymentSpec, len(respSpecs))
2222
for i := range respSpecs {
@@ -25,25 +25,27 @@ func NewCFNSearchDeployment(prevModel *Model, apiResp *admin20231115014.ApiSearc
2525
NodeCount: &respSpecs[i].NodeCount,
2626
}
2727
}
28+
2829
return Model{
29-
Profile: prevModel.Profile,
30-
ClusterName: prevModel.ClusterName,
31-
ProjectId: prevModel.ProjectId,
32-
Id: apiResp.Id,
33-
Specs: resultSpecs,
34-
StateName: apiResp.StateName,
30+
Profile: prevModel.Profile,
31+
ClusterName: prevModel.ClusterName,
32+
ProjectId: prevModel.ProjectId,
33+
Id: apiResp.Id,
34+
Specs: resultSpecs,
35+
StateName: apiResp.StateName,
36+
EncryptionAtRestProvider: apiResp.EncryptionAtRestProvider,
3537
}
3638
}
3739

38-
func NewSearchDeploymentReq(model *Model) admin20231115014.ApiSearchDeploymentRequest {
40+
func NewSearchDeploymentReq(model *Model) admin.ApiSearchDeploymentRequest {
3941
modelSpecs := model.Specs
40-
requestSpecs := make([]admin20231115014.ApiSearchDeploymentSpec, len(modelSpecs))
42+
requestSpecs := make([]admin.ApiSearchDeploymentSpec, len(modelSpecs))
4143
for i, spec := range modelSpecs {
4244
// Both spec fields are required in CFN model and will be defined
43-
requestSpecs[i] = admin20231115014.ApiSearchDeploymentSpec{
45+
requestSpecs[i] = admin.ApiSearchDeploymentSpec{
4446
InstanceSize: *spec.InstanceSize,
4547
NodeCount: *spec.NodeCount,
4648
}
4749
}
48-
return admin20231115014.ApiSearchDeploymentRequest{Specs: requestSpecs}
50+
return admin.ApiSearchDeploymentRequest{Specs: requestSpecs}
4951
}

0 commit comments

Comments
 (0)