Skip to content

Commit b2964ed

Browse files
author
sivaram-mongodb
committed
feat: Update Search Deployment Resource
1 parent b00a7ab commit b2964ed

10 files changed

Lines changed: 185 additions & 123 deletions

File tree

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

Lines changed: 20 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,33 @@ func NewCFNSearchDeployment(prevModel *Model, apiResp *admin20231115014.ApiSearc
2525
NodeCount: &respSpecs[i].NodeCount,
2626
}
2727
}
28+
29+
// Preserve Id from prevModel if apiResp doesn't have it
30+
id := apiResp.Id
31+
if id == nil && prevModel != nil {
32+
id = prevModel.Id
33+
}
34+
2835
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,
36+
Profile: prevModel.Profile,
37+
ClusterName: prevModel.ClusterName,
38+
ProjectId: prevModel.ProjectId,
39+
Id: id,
40+
Specs: resultSpecs,
41+
StateName: apiResp.StateName,
42+
EncryptionAtRestProvider: apiResp.EncryptionAtRestProvider,
3543
}
3644
}
3745

38-
func NewSearchDeploymentReq(model *Model) admin20231115014.ApiSearchDeploymentRequest {
46+
func NewSearchDeploymentReq(model *Model) admin.ApiSearchDeploymentRequest {
3947
modelSpecs := model.Specs
40-
requestSpecs := make([]admin20231115014.ApiSearchDeploymentSpec, len(modelSpecs))
48+
requestSpecs := make([]admin.ApiSearchDeploymentSpec, len(modelSpecs))
4149
for i, spec := range modelSpecs {
4250
// Both spec fields are required in CFN model and will be defined
43-
requestSpecs[i] = admin20231115014.ApiSearchDeploymentSpec{
51+
requestSpecs[i] = admin.ApiSearchDeploymentSpec{
4452
InstanceSize: *spec.InstanceSize,
4553
NodeCount: *spec.NodeCount,
4654
}
4755
}
48-
return admin20231115014.ApiSearchDeploymentRequest{Specs: requestSpecs}
56+
return admin.ApiSearchDeploymentRequest{Specs: requestSpecs}
4957
}

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

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import (
1919

2020
"github.com/mongodb/mongodbatlas-cloudformation-resources/search-deployment/cmd/resource"
2121
"github.com/stretchr/testify/assert"
22-
admin20231115014 "go.mongodb.org/atlas-sdk/v20231115014/admin"
22+
"go.mongodb.org/atlas-sdk/v20250312012/admin"
2323
)
2424

2525
type sdkToCFNModelTestCase struct {
2626
prevModel resource.Model
2727
expectedModel resource.Model
28-
SDKResp admin20231115014.ApiSearchDeploymentResponse
28+
SDKResp admin.ApiSearchDeploymentResponse
2929
name string
3030
}
3131

@@ -44,54 +44,54 @@ func TestSDKToCFNModel(t *testing.T) {
4444
{
4545
name: "Complete SDK response",
4646
prevModel: resource.Model{
47-
Profile: admin20231115014.PtrString(profile),
48-
ClusterName: admin20231115014.PtrString(clusterName),
49-
ProjectId: admin20231115014.PtrString(dummyProjectID),
47+
Profile: admin.PtrString(profile),
48+
ClusterName: admin.PtrString(clusterName),
49+
ProjectId: admin.PtrString(dummyProjectID),
5050
},
51-
SDKResp: admin20231115014.ApiSearchDeploymentResponse{
52-
Id: admin20231115014.PtrString(dummyDeploymentID),
53-
GroupId: admin20231115014.PtrString(dummyProjectID),
54-
StateName: admin20231115014.PtrString(stateName),
55-
Specs: &[]admin20231115014.ApiSearchDeploymentSpec{
51+
SDKResp: admin.ApiSearchDeploymentResponse{
52+
Id: admin.PtrString(dummyDeploymentID),
53+
GroupId: admin.PtrString(dummyProjectID),
54+
StateName: admin.PtrString(stateName),
55+
Specs: &[]admin.ApiSearchDeploymentSpec{
5656
{
5757
InstanceSize: instanceSize,
5858
NodeCount: nodeCount,
5959
},
6060
},
6161
},
6262
expectedModel: resource.Model{
63-
Profile: admin20231115014.PtrString(profile),
64-
ClusterName: admin20231115014.PtrString(clusterName),
65-
ProjectId: admin20231115014.PtrString(dummyProjectID),
66-
Id: admin20231115014.PtrString(dummyDeploymentID),
67-
StateName: admin20231115014.PtrString(stateName),
63+
Profile: admin.PtrString(profile),
64+
ClusterName: admin.PtrString(clusterName),
65+
ProjectId: admin.PtrString(dummyProjectID),
66+
Id: admin.PtrString(dummyDeploymentID),
67+
StateName: admin.PtrString(stateName),
6868
Specs: []resource.ApiSearchDeploymentSpec{
6969
{
70-
InstanceSize: admin20231115014.PtrString(instanceSize),
71-
NodeCount: admin20231115014.PtrInt(nodeCount),
70+
InstanceSize: admin.PtrString(instanceSize),
71+
NodeCount: admin.PtrInt(nodeCount),
7272
},
7373
},
7474
},
7575
},
7676
{
7777
name: "Empty specs array",
7878
prevModel: resource.Model{
79-
Profile: admin20231115014.PtrString(profile),
80-
ClusterName: admin20231115014.PtrString(clusterName),
81-
ProjectId: admin20231115014.PtrString(dummyProjectID),
79+
Profile: admin.PtrString(profile),
80+
ClusterName: admin.PtrString(clusterName),
81+
ProjectId: admin.PtrString(dummyProjectID),
8282
},
83-
SDKResp: admin20231115014.ApiSearchDeploymentResponse{
84-
Id: admin20231115014.PtrString(dummyDeploymentID),
85-
GroupId: admin20231115014.PtrString(dummyProjectID),
86-
StateName: admin20231115014.PtrString(stateName),
87-
Specs: &[]admin20231115014.ApiSearchDeploymentSpec{},
83+
SDKResp: admin.ApiSearchDeploymentResponse{
84+
Id: admin.PtrString(dummyDeploymentID),
85+
GroupId: admin.PtrString(dummyProjectID),
86+
StateName: admin.PtrString(stateName),
87+
Specs: &[]admin.ApiSearchDeploymentSpec{},
8888
},
8989
expectedModel: resource.Model{
90-
Profile: admin20231115014.PtrString(profile),
91-
ClusterName: admin20231115014.PtrString(clusterName),
92-
ProjectId: admin20231115014.PtrString(dummyProjectID),
93-
Id: admin20231115014.PtrString(dummyDeploymentID),
94-
StateName: admin20231115014.PtrString(stateName),
90+
Profile: admin.PtrString(profile),
91+
ClusterName: admin.PtrString(clusterName),
92+
ProjectId: admin.PtrString(dummyProjectID),
93+
Id: admin.PtrString(dummyDeploymentID),
94+
StateName: admin.PtrString(stateName),
9595
Specs: []resource.ApiSearchDeploymentSpec{},
9696
},
9797
},
@@ -109,25 +109,25 @@ func TestCFNModelToSDK(t *testing.T) {
109109
testCases := []struct {
110110
model resource.Model
111111
name string
112-
expectedSDKReq admin20231115014.ApiSearchDeploymentRequest
112+
expectedSDKReq admin.ApiSearchDeploymentRequest
113113
}{
114114
{
115115
name: "Complete CFN model",
116116
model: resource.Model{
117-
Profile: admin20231115014.PtrString(profile),
118-
ClusterName: admin20231115014.PtrString(clusterName),
119-
ProjectId: admin20231115014.PtrString(dummyProjectID),
120-
Id: admin20231115014.PtrString(dummyDeploymentID),
121-
StateName: admin20231115014.PtrString(stateName),
117+
Profile: admin.PtrString(profile),
118+
ClusterName: admin.PtrString(clusterName),
119+
ProjectId: admin.PtrString(dummyProjectID),
120+
Id: admin.PtrString(dummyDeploymentID),
121+
StateName: admin.PtrString(stateName),
122122
Specs: []resource.ApiSearchDeploymentSpec{
123123
{
124-
InstanceSize: admin20231115014.PtrString(instanceSize),
125-
NodeCount: admin20231115014.PtrInt(nodeCount),
124+
InstanceSize: admin.PtrString(instanceSize),
125+
NodeCount: admin.PtrInt(nodeCount),
126126
},
127127
},
128128
},
129-
expectedSDKReq: admin20231115014.ApiSearchDeploymentRequest{
130-
Specs: []admin20231115014.ApiSearchDeploymentSpec{
129+
expectedSDKReq: admin.ApiSearchDeploymentRequest{
130+
Specs: []admin.ApiSearchDeploymentSpec{
131131
{
132132
InstanceSize: instanceSize,
133133
NodeCount: nodeCount,
@@ -138,15 +138,15 @@ func TestCFNModelToSDK(t *testing.T) {
138138
{
139139
name: "Empty specs array",
140140
model: resource.Model{
141-
Profile: admin20231115014.PtrString(profile),
142-
ClusterName: admin20231115014.PtrString(clusterName),
143-
ProjectId: admin20231115014.PtrString(dummyProjectID),
144-
Id: admin20231115014.PtrString(dummyDeploymentID),
145-
StateName: admin20231115014.PtrString(stateName),
141+
Profile: admin.PtrString(profile),
142+
ClusterName: admin.PtrString(clusterName),
143+
ProjectId: admin.PtrString(dummyProjectID),
144+
Id: admin.PtrString(dummyDeploymentID),
145+
StateName: admin.PtrString(stateName),
146146
Specs: []resource.ApiSearchDeploymentSpec{},
147147
},
148-
expectedSDKReq: admin20231115014.ApiSearchDeploymentRequest{
149-
Specs: []admin20231115014.ApiSearchDeploymentSpec{},
148+
expectedSDKReq: admin.ApiSearchDeploymentRequest{
149+
Specs: []admin.ApiSearchDeploymentSpec{},
150150
},
151151
},
152152
}

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

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

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

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"net/http"
2121
"strings"
2222

23-
admin20231115014 "go.mongodb.org/atlas-sdk/v20231115014/admin"
23+
"go.mongodb.org/atlas-sdk/v20250312012/admin"
2424

2525
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
2626
"github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
@@ -58,7 +58,7 @@ func Create(req handler.Request, prevModel *Model, currentModel *Model) (handler
5858
if progressErr != nil {
5959
return *progressErr, nil
6060
}
61-
connV2 := client.Atlas20231115014
61+
connV2 := client.AtlasSDK
6262

6363
// handling of subsequent retry calls
6464
if _, ok := req.CallbackContext[constants.ID]; ok {
@@ -68,7 +68,7 @@ func Create(req handler.Request, prevModel *Model, currentModel *Model) (handler
6868
projectID := util.SafeString(currentModel.ProjectId)
6969
clusterName := util.SafeString(currentModel.ClusterName)
7070
apiReq := NewSearchDeploymentReq(currentModel)
71-
apiResp, resp, err := connV2.AtlasSearchApi.CreateAtlasSearchDeployment(context.Background(), projectID, clusterName, &apiReq).Execute()
71+
apiResp, resp, err := connV2.AtlasSearchApi.CreateClusterSearchDeployment(context.Background(), projectID, clusterName, &apiReq).Execute()
7272
if err != nil {
7373
return handleError(resp, err)
7474
}
@@ -89,18 +89,29 @@ func Read(req handler.Request, prevModel *Model, currentModel *Model) (handler.P
8989
if progressErr != nil {
9090
return *progressErr, nil
9191
}
92-
connV2 := client.Atlas20231115014
92+
connV2 := client.AtlasSDK
9393

9494
projectID := util.SafeString(currentModel.ProjectId)
9595
clusterName := util.SafeString(currentModel.ClusterName)
96-
apiResp, resp, err := connV2.AtlasSearchApi.GetAtlasSearchDeployment(context.Background(), projectID, clusterName).Execute()
96+
apiResp, resp, err := connV2.AtlasSearchApi.GetClusterSearchDeployment(context.Background(), projectID, clusterName).Execute()
9797
if err != nil {
9898
return handleError(resp, err)
9999
}
100100

101+
newModel := NewCFNSearchDeployment(currentModel, apiResp)
102+
103+
// If Specs is empty, the search deployment has been deleted
104+
// Return NotFound instead of Success
105+
if len(newModel.Specs) == 0 {
106+
return handler.ProgressEvent{
107+
OperationStatus: handler.Failed,
108+
Message: "Resource not found",
109+
HandlerErrorCode: string(types.HandlerErrorCodeNotFound)}, nil
110+
}
111+
101112
return handler.ProgressEvent{
102113
OperationStatus: handler.Success,
103-
ResourceModel: NewCFNSearchDeployment(currentModel, apiResp),
114+
ResourceModel: newModel,
104115
}, nil
105116
}
106117

@@ -116,7 +127,7 @@ func Update(req handler.Request, prevModel *Model, currentModel *Model) (handler
116127
if progressErr != nil {
117128
return *progressErr, nil
118129
}
119-
connV2 := client.Atlas20231115014
130+
connV2 := client.AtlasSDK
120131

121132
// handling of subsequent retry calls
122133
if _, ok := req.CallbackContext[constants.ID]; ok {
@@ -126,12 +137,22 @@ func Update(req handler.Request, prevModel *Model, currentModel *Model) (handler
126137
projectID := util.SafeString(currentModel.ProjectId)
127138
clusterName := util.SafeString(currentModel.ClusterName)
128139
apiReq := NewSearchDeploymentReq(currentModel)
129-
apiResp, res, err := connV2.AtlasSearchApi.UpdateAtlasSearchDeployment(context.Background(), projectID, clusterName, &apiReq).Execute()
140+
apiResp, res, err := connV2.AtlasSearchApi.UpdateClusterSearchDeployment(context.Background(), projectID, clusterName, &apiReq).Execute()
130141
if err != nil {
142+
// Update should return NotFound if resource doesn't exist - this is already handled by handleError
131143
return handleError(res, err)
132144
}
133145

134146
newModel := NewCFNSearchDeployment(currentModel, apiResp)
147+
148+
// If Specs is empty, the search deployment has been deleted - return NotFound
149+
if len(newModel.Specs) == 0 {
150+
return handler.ProgressEvent{
151+
OperationStatus: handler.Failed,
152+
Message: "Resource not found",
153+
HandlerErrorCode: string(types.HandlerErrorCodeNotFound)}, nil
154+
}
155+
135156
return inProgressEvent("Updating Search Deployment", &newModel), nil
136157
}
137158

@@ -147,7 +168,7 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler
147168
if progressErr != nil {
148169
return *progressErr, nil
149170
}
150-
connV2 := client.Atlas20231115014
171+
connV2 := client.AtlasSDK
151172

152173
// handling of subsequent retry calls
153174
if _, ok := req.CallbackContext[constants.ID]; ok {
@@ -156,7 +177,7 @@ func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler
156177

157178
projectID := util.SafeString(currentModel.ProjectId)
158179
clusterName := util.SafeString(currentModel.ClusterName)
159-
if resp, err := connV2.AtlasSearchApi.DeleteAtlasSearchDeployment(context.Background(), projectID, clusterName).Execute(); err != nil {
180+
if resp, err := connV2.AtlasSearchApi.DeleteClusterSearchDeployment(context.Background(), projectID, clusterName).Execute(); err != nil {
160181
return handleError(resp, err)
161182
}
162183

@@ -169,13 +190,13 @@ func List(req handler.Request, prevModel *Model, currentModel *Model) (handler.P
169190

170191
// specific handling for search deployment API where 400 status code can include AlreadyExists or DoesNotExist that need specific mapping to CFN error codes
171192
func handleError(res *http.Response, err error) (handler.ProgressEvent, error) {
172-
if apiError, ok := admin20231115014.AsError(err); ok && *apiError.Error == http.StatusBadRequest && strings.Contains(*apiError.ErrorCode, SearchDeploymentAlreadyExistsError) {
193+
if apiError, ok := admin.AsError(err); ok && apiError.Error == http.StatusBadRequest && strings.Contains(apiError.ErrorCode, SearchDeploymentAlreadyExistsError) {
173194
return handler.ProgressEvent{
174195
OperationStatus: handler.Failed,
175196
Message: err.Error(),
176197
HandlerErrorCode: string(types.HandlerErrorCodeAlreadyExists)}, nil
177198
}
178-
if apiError, ok := admin20231115014.AsError(err); ok && *apiError.Error == http.StatusBadRequest && strings.Contains(*apiError.ErrorCode, SearchDeploymentDoesNotExistsError) {
199+
if apiError, ok := admin.AsError(err); ok && apiError.Error == http.StatusBadRequest && strings.Contains(apiError.ErrorCode, SearchDeploymentDoesNotExistsError) {
179200
return handler.ProgressEvent{
180201
OperationStatus: handler.Failed,
181202
Message: err.Error(),

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ import (
2323
"github.com/mongodb/mongodbatlas-cloudformation-resources/util"
2424
"github.com/mongodb/mongodbatlas-cloudformation-resources/util/constants"
2525
"github.com/mongodb/mongodbatlas-cloudformation-resources/util/progressevent"
26-
admin20231115014 "go.mongodb.org/atlas-sdk/v20231115014/admin"
26+
"go.mongodb.org/atlas-sdk/v20250312012/admin"
2727
)
2828

29-
func HandleStateTransition(connV2 admin20231115014.APIClient, currentModel *Model, targetState string) handler.ProgressEvent {
29+
func HandleStateTransition(connV2 admin.APIClient, currentModel *Model, targetState string) handler.ProgressEvent {
3030
projectID := util.SafeString(currentModel.ProjectId)
3131
clusterName := util.SafeString(currentModel.ClusterName)
32-
apiResp, resp, err := connV2.AtlasSearchApi.GetAtlasSearchDeployment(context.Background(), projectID, clusterName).Execute()
32+
apiResp, resp, err := connV2.AtlasSearchApi.GetClusterSearchDeployment(context.Background(), projectID, clusterName).Execute()
3333
if err != nil {
3434
if targetState == constants.DeletedState && resp.StatusCode == http.StatusBadRequest && strings.Contains(err.Error(), SearchDeploymentDoesNotExistsError) {
3535
return handler.ProgressEvent{
@@ -42,6 +42,17 @@ func HandleStateTransition(connV2 admin20231115014.APIClient, currentModel *Mode
4242
}
4343

4444
newModel := NewCFNSearchDeployment(currentModel, apiResp)
45+
46+
// For delete operations, check if Specs is empty - this indicates deletion is complete
47+
// The Atlas API returns 200 with only basic fields (no Specs) when deployment is deleted
48+
if targetState == constants.DeletedState && len(newModel.Specs) == 0 {
49+
return handler.ProgressEvent{
50+
OperationStatus: handler.Success,
51+
ResourceModel: nil,
52+
Message: constants.Complete,
53+
}
54+
}
55+
4556
if util.SafeString(newModel.StateName) == targetState {
4657
return handler.ProgressEvent{
4758
OperationStatus: handler.Success,

0 commit comments

Comments
 (0)