Skip to content

Commit 4ae6db1

Browse files
CLOUDP-285946: Add support for flex Clusters to "atlas cluster upgrade" (#3469)
1 parent d6757e0 commit 4ae6db1

File tree

5 files changed

+305
-5
lines changed

5 files changed

+305
-5
lines changed

internal/cli/clusters/update_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
//go:build unit
16+
1517
package clusters
1618

1719
import (

internal/cli/clusters/upgrade.go

Lines changed: 161 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package clusters
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
2021

2122
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli"
@@ -27,10 +28,15 @@ import (
2728
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/usage"
2829
"github.com/spf13/afero"
2930
"github.com/spf13/cobra"
31+
atlasv2 "go.mongodb.org/atlas-sdk/v20241113002/admin"
3032
atlas "go.mongodb.org/atlas/mongodbatlas"
3133
)
3234

33-
const upgradeTemplate = "Upgrading cluster '{{.Name}}'.\n"
35+
const (
36+
upgradeTemplate = "Upgrading cluster '{{.Name}}'.\n"
37+
replicaSetNodeCount = 3
38+
replicaSetPriority = 7
39+
)
3440

3541
type UpgradeOpts struct {
3642
cli.ProjectOpts
@@ -42,6 +48,7 @@ type UpgradeOpts struct {
4248
filename string
4349
enableTerminationProtection bool
4450
disableTerminationProtection bool
51+
isFlexCluster bool
4552
tag map[string]string
4653
fs afero.Fs
4754
store store.AtlasSharedClusterGetterUpgrader
@@ -56,6 +63,28 @@ func (opts *UpgradeOpts) initStore(ctx context.Context) func() error {
5663
}
5764

5865
func (opts *UpgradeOpts) Run() error {
66+
if opts.isFlexCluster {
67+
return opts.RunFlexCluster()
68+
}
69+
70+
return opts.RunSharedCluster()
71+
}
72+
73+
func (opts *UpgradeOpts) RunFlexCluster() error {
74+
cluster, err := opts.atlasTenantClusterUpgradeRequest20240805()
75+
if err != nil {
76+
return err
77+
}
78+
79+
r, err := opts.store.UpgradeFlexCluster(opts.ConfigProjectID(), cluster)
80+
if err != nil {
81+
return err
82+
}
83+
84+
return opts.Print(r)
85+
}
86+
87+
func (opts *UpgradeOpts) RunSharedCluster() error {
5988
cluster, err := opts.cluster()
6089
if err != nil {
6190
return err
@@ -72,18 +101,125 @@ func (opts *UpgradeOpts) Run() error {
72101
return opts.Print(r)
73102
}
74103

104+
func (opts *UpgradeOpts) atlasTenantClusterUpgradeRequest20240805() (*atlasv2.AtlasTenantClusterUpgradeRequest20240805, error) {
105+
var cluster *atlasv2.AtlasTenantClusterUpgradeRequest20240805
106+
if opts.filename != "" {
107+
err := file.Load(opts.fs, opts.filename, &cluster)
108+
if err != nil {
109+
return nil, err
110+
}
111+
112+
cluster.Name = opts.name
113+
114+
return cluster, nil
115+
}
116+
117+
flexClusterDescription, err := opts.store.FlexCluster(opts.ConfigProjectID(), opts.name)
118+
if err != nil {
119+
return nil, err
120+
}
121+
122+
return opts.newAtlasTenantClusterUpgradeRequestFromFlexClusterDescription(flexClusterDescription), nil
123+
}
124+
125+
func (opts *UpgradeOpts) newAtlasTenantClusterUpgradeRequestFromFlexClusterDescription(flexCluster *atlasv2.FlexClusterDescription20241113) *atlasv2.AtlasTenantClusterUpgradeRequest20240805 {
126+
mdbVersion := flexCluster.MongoDBVersion
127+
if opts.mdbVersion != "" {
128+
mdbVersion = &opts.mdbVersion
129+
}
130+
131+
terminationProtectionEnabled := flexCluster.GetTerminationProtectionEnabled()
132+
if opts.disableTerminationProtection {
133+
terminationProtectionEnabled = false
134+
}
135+
136+
if opts.enableTerminationProtection {
137+
terminationProtectionEnabled = true
138+
}
139+
140+
var tags []atlasv2.ResourceTag
141+
if flexCluster.Tags != nil {
142+
tags = flexCluster.GetTags()
143+
}
144+
145+
if len(opts.tag) > 0 {
146+
newTags := newResourceTags(opts.tag)
147+
tags = append(tags, *newTags...)
148+
}
149+
150+
backupEnabled := false
151+
if settings, ok := flexCluster.GetBackupSettingsOk(); ok {
152+
backupEnabled = settings.GetEnabled()
153+
}
154+
155+
flexGroupID, _ := flexCluster.GetGroupIdOk()
156+
versionRelease, _ := flexCluster.GetVersionReleaseSystemOk()
157+
flexClusterType, _ := flexCluster.GetClusterTypeOk()
158+
159+
return &atlasv2.AtlasTenantClusterUpgradeRequest20240805{
160+
BackupEnabled: &backupEnabled,
161+
ClusterType: flexClusterType,
162+
GroupId: flexGroupID,
163+
MongoDBVersion: mdbVersion,
164+
Name: flexCluster.GetName(),
165+
Tags: &tags,
166+
TerminationProtectionEnabled: &terminationProtectionEnabled,
167+
VersionReleaseSystem: versionRelease,
168+
ReplicationSpecs: opts.newReplicationSpecFromOpts(flexCluster),
169+
}
170+
}
171+
172+
func (opts *UpgradeOpts) newReplicationSpecFromOpts(flexCluster *atlasv2.FlexClusterDescription20241113) *[]atlasv2.ReplicationSpec20240805 {
173+
if opts.tier == "" {
174+
return nil
175+
}
176+
177+
diskSizeGb := 0.0
178+
backingProviderName := ""
179+
regionN := ""
180+
if settings, ok := flexCluster.GetProviderSettingsOk(); ok {
181+
diskSizeGb = settings.GetDiskSizeGB()
182+
backingProviderName = settings.GetBackingProviderName()
183+
regionN = settings.GetRegionName()
184+
}
185+
186+
if opts.diskSizeGB != 0 {
187+
diskSizeGb = opts.diskSizeGB
188+
}
189+
190+
replicaSetCount := replicaSetNodeCount
191+
priority := replicaSetPriority
192+
193+
replicaSpec := atlasv2.ReplicationSpec20240805{
194+
RegionConfigs: &[]atlasv2.CloudRegionConfig20240805{
195+
{
196+
ElectableSpecs: &atlasv2.HardwareSpec20240805{
197+
InstanceSize: &opts.tier,
198+
DiskSizeGB: &diskSizeGb,
199+
NodeCount: &replicaSetCount,
200+
},
201+
ProviderName: &backingProviderName,
202+
RegionName: &regionN,
203+
Priority: &priority,
204+
},
205+
},
206+
}
207+
208+
return &[]atlasv2.ReplicationSpec20240805{replicaSpec}
209+
}
210+
75211
func (opts *UpgradeOpts) cluster() (*atlas.Cluster, error) {
76212
var cluster *atlas.Cluster
77213
if opts.filename != "" {
78214
err := file.Load(opts.fs, opts.filename, &cluster)
79215
if err != nil {
80216
return nil, err
81217
}
82-
if opts.name == "" {
83-
opts.name = cluster.Name
84-
}
218+
cluster.Name = opts.name
219+
85220
return cluster, nil
86221
}
222+
87223
return opts.store.AtlasSharedCluster(opts.ProjectID, opts.name)
88224
}
89225

@@ -129,6 +265,26 @@ func isTenant(instanceSizeName string) bool {
129265
instanceSizeName == atlasM5
130266
}
131267

268+
// newIsFlexCluster sets the opts.isFlexCluster that indicates if the cluster to create is
269+
// a FlexCluster. The function calls the AtlasSharedAPI to get the cluster, and it sets the opts.isFlexCluster = true
270+
// in the event of a cannotUseFlexWithClusterApisErrorCode.
271+
func (opts *UpgradeOpts) newIsFlexCluster() error {
272+
_, err := opts.store.AtlasSharedCluster(opts.ConfigProjectID(), opts.name)
273+
var errorResponse *atlas.ErrorResponse
274+
ok := errors.As(err, &errorResponse)
275+
if !ok {
276+
opts.isFlexCluster = false
277+
return err
278+
}
279+
280+
if errorResponse.ErrorCode != cannotUseFlexWithClusterApisErrorCode {
281+
return err
282+
}
283+
284+
opts.isFlexCluster = true
285+
return nil
286+
}
287+
132288
// UpgradeBuilder builds a cobra.Command that can run as:
133289
// atlas cluster(s) upgrade [clusterName] --projectId projectId [--tier M#] [--diskSizeGB N] [--mdbVersion] [--tag key=value].
134290
func UpgradeBuilder() *cobra.Command {
@@ -152,6 +308,7 @@ func UpgradeBuilder() *cobra.Command {
152308
opts.ValidateProjectID,
153309
opts.initStore(cmd.Context()),
154310
opts.InitOutput(cmd.OutOrStdout(), upgradeTemplate),
311+
opts.newIsFlexCluster,
155312
)
156313
},
157314
RunE: func(_ *cobra.Command, _ []string) error {

internal/cli/clusters/upgrade_test.go

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ import (
2121

2222
"github.com/golang/mock/gomock"
2323
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/mocks"
24+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/pointer"
2425
"github.com/spf13/afero"
26+
atlasv2 "go.mongodb.org/atlas-sdk/v20241113002/admin"
2527
"go.mongodb.org/atlas/mongodbatlas"
2628
)
2729

2830
const atlasM10 = "M10"
2931

30-
func TestUpgrade_Run(t *testing.T) {
32+
func TestUpgrade_RunSharedCluster(t *testing.T) {
3133
ctrl := gomock.NewController(t)
3234
mockStore := mocks.NewMockAtlasSharedClusterGetterUpgrader(ctrl)
3335

@@ -154,3 +156,111 @@ func TestUpgrade_Run(t *testing.T) {
154156
}
155157
})
156158
}
159+
160+
func TestUpgrade_RunFlexCluster(t *testing.T) {
161+
ctrl := gomock.NewController(t)
162+
mockStore := mocks.NewMockAtlasSharedClusterGetterUpgrader(ctrl)
163+
expected := &atlasv2.FlexClusterDescription20241113{
164+
Name: pointer.Get("Test"),
165+
}
166+
167+
t.Run("flags run", func(t *testing.T) {
168+
upgradeOpts := &UpgradeOpts{
169+
name: "Test",
170+
tier: atlasM10,
171+
diskSizeGB: 10,
172+
mdbVersion: "6.0",
173+
store: mockStore,
174+
isFlexCluster: true,
175+
}
176+
177+
mockStore.
178+
EXPECT().
179+
FlexCluster(upgradeOpts.ProjectID, upgradeOpts.name).
180+
Return(expected, nil).
181+
Times(1)
182+
183+
mockStore.
184+
EXPECT().
185+
UpgradeFlexCluster(upgradeOpts.ProjectID, upgradeOpts.newAtlasTenantClusterUpgradeRequestFromFlexClusterDescription(expected)).
186+
Return(expected, nil).
187+
Times(1)
188+
189+
if err := upgradeOpts.Run(); err != nil {
190+
t.Fatalf("Run() unexpected error: %v", err)
191+
}
192+
})
193+
194+
t.Run("file run", func(t *testing.T) {
195+
appFS := afero.NewMemMapFs()
196+
// create test file
197+
fileYML := `
198+
{
199+
"backupEnabled": false,
200+
"clusterType": "REPLICASET",
201+
"name": "Test",
202+
"replicationSpecs": [
203+
{
204+
"regionConfigs": [
205+
{
206+
"electableSpecs": {
207+
"diskSizeGB": 10,
208+
"instanceSize": "M10",
209+
"nodeCount": 3
210+
},
211+
"priority": 7,
212+
"providerName": "AWS",
213+
"regionName": "US_EAST_1"
214+
}
215+
]
216+
}
217+
],
218+
"terminationProtectionEnabled": false,
219+
"versionReleaseSystem": "LTS"
220+
}`
221+
fileName := "atlas_cluster_flex_upgrade_test.json"
222+
_ = afero.WriteFile(appFS, fileName, []byte(fileYML), 0600)
223+
224+
upgradeOpts := &UpgradeOpts{
225+
filename: fileName,
226+
fs: appFS,
227+
store: mockStore,
228+
isFlexCluster: true,
229+
name: "Test",
230+
}
231+
232+
reqBody := &atlasv2.AtlasTenantClusterUpgradeRequest20240805{
233+
BackupEnabled: pointer.Get(false),
234+
Name: "Test",
235+
VersionReleaseSystem: pointer.Get("LTS"),
236+
ClusterType: pointer.Get("REPLICASET"),
237+
TerminationProtectionEnabled: pointer.Get(false),
238+
ReplicationSpecs: &[]atlasv2.ReplicationSpec20240805{
239+
{
240+
RegionConfigs: &[]atlasv2.CloudRegionConfig20240805{
241+
{
242+
Priority: pointer.Get(7),
243+
RegionName: pointer.Get("US_EAST_1"),
244+
ProviderName: pointer.Get("AWS"),
245+
ElectableSpecs: &atlasv2.HardwareSpec20240805{
246+
DiskSizeGB: pointer.Get(10.0),
247+
InstanceSize: pointer.Get("M10"),
248+
NodeCount: pointer.Get(3),
249+
},
250+
},
251+
},
252+
},
253+
},
254+
}
255+
256+
mockStore.
257+
EXPECT().
258+
UpgradeFlexCluster(upgradeOpts.ConfigProjectID(), reqBody).
259+
Return(expected, nil).
260+
Times(1)
261+
262+
if err := upgradeOpts.Run(); err != nil {
263+
t.Fatalf("Run() unexpected error: %v", err)
264+
}
265+
})
266+
}

0 commit comments

Comments
 (0)