Skip to content

Commit 7df799f

Browse files
feat: Update Cluster resource to support flex clusters (#1430)
Co-authored-by: svc-apix-bot <svc-api-experience-integrations-escalation@mongodb.com>
1 parent e8bb19b commit 7df799f

20 files changed

Lines changed: 1403 additions & 454 deletions

.github/workflows/publish.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
run: |
6868
cd cfn-resources
6969
./cfn-publish.sh "${{ github.event.inputs.resourceName }}"
70-
echo "published_version=$(cat published_version.txt)" >> "$GITHUB_OUTPUT"
70+
echo "published_version=$(cat ${{ github.event.inputs.resourceName }}/published_version.txt)" >> "$GITHUB_OUTPUT"
7171
env:
7272
RESOURCE_VERSION_PUBLISHING: ${{ github.event.inputs.resourceVersionPublishing }}
7373
MCLI_OPS_MANAGER_URL: ${{ env.MONGODB_ATLAS_BASE_URL }}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package resource
15+
16+
import (
17+
"context"
18+
"strings"
19+
20+
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
21+
flex "github.com/mongodb/mongodbatlas-cloudformation-resources/flex-cluster/cmd/resource"
22+
"github.com/mongodb/mongodbatlas-cloudformation-resources/util"
23+
)
24+
25+
const (
26+
defaultPriority = 7
27+
defaultZoneName = "Zone 1"
28+
flexProvider = "FLEX"
29+
)
30+
31+
// clusterToFlexModelIdentifier transforms a cluster model to a flex cluster model representation.
32+
// It's used for Read and Delete where only the identifier is passed (project id and cluster name).
33+
// As regions are not passed, Atlas calls are made to learn if it's a flex cluster.
34+
// Returns nil if the cluster is not a flex cluster.
35+
func clusterToFlexModelIdentifier(req *handler.Request, client *util.MongoDBClient, c *Model) *flex.Model {
36+
f := &flex.Model{
37+
Profile: c.Profile,
38+
ProjectId: c.ProjectId,
39+
Name: c.Name,
40+
}
41+
if isCallback(req) {
42+
return nil // cluster operation in progress
43+
}
44+
if flex.IsCallback(req) {
45+
return f // flex operation in progress
46+
}
47+
_, _, errCluster := client.AtlasSDK.ClustersApi.GetCluster(context.Background(), *c.ProjectId, *c.Name).Execute()
48+
if errCluster != nil && strings.Contains(errCluster.Error(), "CANNOT_USE_FLEX_CLUSTER_IN_CLUSTER_API") {
49+
return f
50+
}
51+
return nil
52+
}
53+
54+
// clusterToFlexModelFull transforms a cluster model to a flex cluster model representation.
55+
// It's used for Create and Update where the full model is passed.
56+
// Regions are passed so not Atlas calls are needed to learn if it's a flex cluster.
57+
// Returns nil if the cluster is not a flex cluster.
58+
func clusterToFlexModelFull(c *Model) *flex.Model {
59+
if len(c.ReplicationSpecs) != 1 || len(c.ReplicationSpecs[0].AdvancedRegionConfigs) != 1 {
60+
return nil
61+
}
62+
firstRegion := c.ReplicationSpecs[0].AdvancedRegionConfigs[0]
63+
if util.SafeString(firstRegion.ProviderName) != flexProvider {
64+
return nil
65+
}
66+
f := &flex.Model{
67+
Profile: c.Profile,
68+
ProjectId: c.ProjectId,
69+
Name: c.Name,
70+
Id: c.Id,
71+
StateName: c.StateName,
72+
ClusterType: c.ClusterType,
73+
CreateDate: c.CreatedDate,
74+
MongoDBVersion: c.MongoDBVersion,
75+
TerminationProtectionEnabled: c.TerminationProtectionEnabled,
76+
VersionReleaseSystem: c.VersionReleaseSystem,
77+
ProviderSettings: &flex.ProviderSettings{
78+
BackingProviderName: firstRegion.BackingProviderName,
79+
RegionName: firstRegion.RegionName,
80+
ProviderName: firstRegion.ProviderName,
81+
},
82+
}
83+
if c.BackupEnabled != nil {
84+
f.BackupSettings = &flex.BackupSettings{
85+
Enabled: c.BackupEnabled,
86+
}
87+
}
88+
if c.ConnectionStrings != nil {
89+
f.ConnectionStrings = &flex.ConnectionStrings{
90+
Standard: c.ConnectionStrings.Standard,
91+
StandardSrv: c.ConnectionStrings.StandardSrv,
92+
}
93+
}
94+
f.Tags = make([]flex.Tag, len(c.Tags))
95+
for i, tag := range c.Tags {
96+
f.Tags[i] = flex.Tag{
97+
Key: tag.Key,
98+
Value: tag.Value,
99+
}
100+
}
101+
return f
102+
}
103+
104+
// fillModelForFlex updates the flex model into the cluster model.
105+
func fillModelForFlex(pe *handler.ProgressEvent, c *Model) {
106+
if pe.ResourceModel == nil {
107+
return
108+
}
109+
f := pe.ResourceModel.(*flex.Model) // will panic if not a flex model
110+
pe.ResourceModel = c
111+
112+
c.Profile = f.Profile
113+
c.ProjectId = f.ProjectId
114+
c.Name = f.Name
115+
c.Id = f.Id
116+
c.StateName = f.StateName
117+
c.ClusterType = f.ClusterType
118+
c.CreatedDate = f.CreateDate
119+
c.MongoDBVersion = f.MongoDBVersion
120+
c.TerminationProtectionEnabled = f.TerminationProtectionEnabled
121+
c.VersionReleaseSystem = f.VersionReleaseSystem
122+
if f.BackupSettings != nil {
123+
c.BackupEnabled = f.BackupSettings.Enabled
124+
} else {
125+
c.BackupEnabled = nil
126+
}
127+
if f.ConnectionStrings != nil {
128+
c.ConnectionStrings = &ConnectionStrings{
129+
Standard: f.ConnectionStrings.Standard,
130+
StandardSrv: f.ConnectionStrings.StandardSrv,
131+
}
132+
} else {
133+
c.ConnectionStrings = nil
134+
}
135+
c.Tags = make([]Tag, len(f.Tags))
136+
for i, tag := range f.Tags {
137+
c.Tags[i] = Tag{
138+
Key: tag.Key,
139+
Value: tag.Value,
140+
}
141+
}
142+
if f.ProviderSettings != nil {
143+
regionConfig := AdvancedRegionConfig{
144+
BackingProviderName: f.ProviderSettings.BackingProviderName,
145+
RegionName: f.ProviderSettings.RegionName,
146+
ProviderName: f.ProviderSettings.ProviderName,
147+
Priority: util.Pointer(defaultPriority),
148+
}
149+
replicationSpec := AdvancedReplicationSpec{
150+
AdvancedRegionConfigs: []AdvancedRegionConfig{regionConfig},
151+
ZoneName: util.Pointer(defaultZoneName),
152+
}
153+
c.ReplicationSpecs = []AdvancedReplicationSpec{replicationSpec}
154+
} else {
155+
c.ReplicationSpecs = nil
156+
}
157+
}
158+
159+
// fillModelForFlexList update the cluster list with flex clusters.
160+
func fillModelForFlexList(pe *handler.ProgressEvent, clusters []*Model) []*Model {
161+
if pe.ResourceModel == nil {
162+
return clusters
163+
}
164+
list := pe.ResourceModel.([]*flex.Model) // will panic if not a flex model array
165+
for _, f := range list {
166+
c := &Model{}
167+
fillModelForFlex(&handler.ProgressEvent{ResourceModel: f}, c)
168+
clusters = append(clusters, c)
169+
}
170+
return clusters
171+
}

cfn-resources/cluster/cmd/resource/mappings.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,8 @@ func expandAdvancedSettings(processArgs ProcessArgs) *admin20231115014.ClusterDe
439439
}
440440

441441
if processArgs.TransactionLifetimeLimitSeconds != nil {
442-
args.TransactionLifetimeLimitSeconds = cast64(processArgs.TransactionLifetimeLimitSeconds)
442+
limitSeconds := cast.ToInt64(*processArgs.TransactionLifetimeLimitSeconds)
443+
args.TransactionLifetimeLimitSeconds = &limitSeconds
443444
}
444445

445446
return &args

0 commit comments

Comments
 (0)