Skip to content

Commit 8dc43c2

Browse files
CLOUDP-285946: Add support for flex Clusters to "atlas cluster upgrade"
1 parent 105ad39 commit 8dc43c2

File tree

5 files changed

+290
-2
lines changed

5 files changed

+290
-2
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: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,28 @@ package clusters
1616

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

2122
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli"
2223
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/require"
2324
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/config"
2425
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/file"
2526
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/flag"
27+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/pointer"
2628
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store"
2729
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/usage"
2830
"github.com/spf13/afero"
2931
"github.com/spf13/cobra"
32+
atlasv2 "go.mongodb.org/atlas-sdk/v20241113002/admin"
3033
atlas "go.mongodb.org/atlas/mongodbatlas"
3134
)
3235

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

3542
type UpgradeOpts struct {
3643
cli.ProjectOpts
@@ -42,6 +49,7 @@ type UpgradeOpts struct {
4249
filename string
4350
enableTerminationProtection bool
4451
disableTerminationProtection bool
52+
isFlexCluster bool
4553
tag map[string]string
4654
fs afero.Fs
4755
store store.AtlasSharedClusterGetterUpgrader
@@ -56,6 +64,28 @@ func (opts *UpgradeOpts) initStore(ctx context.Context) func() error {
5664
}
5765

5866
func (opts *UpgradeOpts) Run() error {
67+
if opts.isFlexCluster {
68+
return opts.RunFlexCluster()
69+
}
70+
71+
return opts.RunSharedCluster()
72+
}
73+
74+
func (opts *UpgradeOpts) RunFlexCluster() error {
75+
cluster, err := opts.atlasTenantClusterUpgradeRequest20240805()
76+
if err != nil {
77+
return err
78+
}
79+
80+
r, err := opts.store.UpgradeFlexCluster(opts.ConfigProjectID(), cluster)
81+
if err != nil {
82+
return err
83+
}
84+
85+
return opts.Print(r)
86+
}
87+
88+
func (opts *UpgradeOpts) RunSharedCluster() error {
5989
cluster, err := opts.cluster()
6090
if err != nil {
6191
return err
@@ -72,18 +102,113 @@ func (opts *UpgradeOpts) Run() error {
72102
return opts.Print(r)
73103
}
74104

105+
func (opts *UpgradeOpts) atlasTenantClusterUpgradeRequest20240805() (*atlasv2.AtlasTenantClusterUpgradeRequest20240805, error) {
106+
var cluster *atlasv2.AtlasTenantClusterUpgradeRequest20240805
107+
if opts.filename != "" {
108+
err := file.Load(opts.fs, opts.filename, &cluster)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
if opts.name == "" {
114+
opts.name = cluster.Name
115+
}
116+
return cluster, nil
117+
}
118+
119+
flexClusterDescription, err := opts.store.FlexCluster(opts.ProjectID, opts.name)
120+
if err != nil {
121+
return nil, err
122+
}
123+
124+
return opts.newAtlasTenantClusterUpgradeRequestFromFlexClusterDescription(flexClusterDescription), nil
125+
}
126+
127+
func (opts *UpgradeOpts) newAtlasTenantClusterUpgradeRequestFromFlexClusterDescription(flexCluster *atlasv2.FlexClusterDescription20241113) *atlasv2.AtlasTenantClusterUpgradeRequest20240805 {
128+
mdbVersion := flexCluster.MongoDBVersion
129+
if opts.mdbVersion != "" {
130+
mdbVersion = &opts.mdbVersion
131+
}
132+
133+
terminationProtectionEnabled := flexCluster.TerminationProtectionEnabled
134+
if opts.disableTerminationProtection {
135+
terminationProtectionEnabled = pointer.Get(false)
136+
}
137+
138+
if opts.enableTerminationProtection {
139+
terminationProtectionEnabled = pointer.Get(true)
140+
}
141+
142+
var tags []atlasv2.ResourceTag
143+
if flexCluster.Tags != nil {
144+
tags = *flexCluster.Tags
145+
}
146+
147+
if len(opts.tag) > 0 {
148+
newTags := newResourceTags(opts.tag)
149+
tags = append(tags, *newTags...)
150+
}
151+
152+
backupEnabled := false
153+
if flexCluster.BackupSettings != nil {
154+
backupEnabled = *flexCluster.BackupSettings.Enabled
155+
}
156+
157+
return &atlasv2.AtlasTenantClusterUpgradeRequest20240805{
158+
BackupEnabled: &backupEnabled,
159+
ClusterType: flexCluster.ClusterType,
160+
GroupId: flexCluster.GroupId,
161+
MongoDBVersion: mdbVersion,
162+
Name: *flexCluster.Name,
163+
Tags: &tags,
164+
TerminationProtectionEnabled: terminationProtectionEnabled,
165+
VersionReleaseSystem: flexCluster.VersionReleaseSystem,
166+
ReplicationSpecs: opts.newReplicationSpecFromOpts(flexCluster),
167+
}
168+
}
169+
170+
func (opts *UpgradeOpts) newReplicationSpecFromOpts(flexCluster *atlasv2.FlexClusterDescription20241113) *[]atlasv2.ReplicationSpec20240805 {
171+
if opts.tier == "" {
172+
return nil
173+
}
174+
175+
diskSizeGb := flexCluster.ProviderSettings.DiskSizeGB
176+
if opts.diskSizeGB != 0 {
177+
diskSizeGb = &opts.diskSizeGB
178+
}
179+
180+
replicaSpec := atlasv2.ReplicationSpec20240805{
181+
RegionConfigs: &[]atlasv2.CloudRegionConfig20240805{
182+
{
183+
ElectableSpecs: &atlasv2.HardwareSpec20240805{
184+
InstanceSize: &opts.tier,
185+
DiskSizeGB: diskSizeGb,
186+
NodeCount: pointer.Get(replicaSetNodeCount),
187+
},
188+
ProviderName: flexCluster.ProviderSettings.BackingProviderName,
189+
RegionName: flexCluster.ProviderSettings.RegionName,
190+
Priority: pointer.Get(replicaSetPriority),
191+
},
192+
},
193+
}
194+
195+
return &[]atlasv2.ReplicationSpec20240805{replicaSpec}
196+
}
197+
75198
func (opts *UpgradeOpts) cluster() (*atlas.Cluster, error) {
76199
var cluster *atlas.Cluster
77200
if opts.filename != "" {
78201
err := file.Load(opts.fs, opts.filename, &cluster)
79202
if err != nil {
80203
return nil, err
81204
}
205+
82206
if opts.name == "" {
83207
opts.name = cluster.Name
84208
}
85209
return cluster, nil
86210
}
211+
87212
return opts.store.AtlasSharedCluster(opts.ProjectID, opts.name)
88213
}
89214

@@ -129,6 +254,26 @@ func isTenant(instanceSizeName string) bool {
129254
instanceSizeName == atlasM5
130255
}
131256

257+
// newIsFlexCluster sets the opts.isFlexCluster that indicates if the cluster to create is
258+
// a FlexCluster. The function calls the AtlasSharedAPI to get the cluster, and it sets the opts.isFlexCluster = true
259+
// in the event of a cannotUseFlexWithClusterApisErrorCode.
260+
func (opts *UpgradeOpts) newIsFlexCluster() error {
261+
_, err := opts.store.AtlasSharedCluster(opts.ProjectID, opts.name)
262+
var errorResponse *atlas.ErrorResponse
263+
ok := errors.As(err, &errorResponse)
264+
if !ok {
265+
opts.isFlexCluster = false
266+
return err
267+
}
268+
269+
if errorResponse.ErrorCode != cannotUseFlexWithClusterApisErrorCode {
270+
return err
271+
}
272+
273+
opts.isFlexCluster = true
274+
return nil
275+
}
276+
132277
// UpgradeBuilder builds a cobra.Command that can run as:
133278
// atlas cluster(s) upgrade [clusterName] --projectId projectId [--tier M#] [--diskSizeGB N] [--mdbVersion] [--tag key=value].
134279
func UpgradeBuilder() *cobra.Command {
@@ -152,6 +297,7 @@ func UpgradeBuilder() *cobra.Command {
152297
opts.ValidateProjectID,
153298
opts.initStore(cmd.Context()),
154299
opts.InitOutput(cmd.OutOrStdout(), upgradeTemplate),
300+
opts.newIsFlexCluster,
155301
)
156302
},
157303
RunE: func(_ *cobra.Command, _ []string) error {

internal/cli/clusters/upgrade_test.go

Lines changed: 110 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,110 @@ 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+
}
230+
231+
reqBody := &atlasv2.AtlasTenantClusterUpgradeRequest20240805{
232+
BackupEnabled: pointer.Get(false),
233+
Name: "Test",
234+
VersionReleaseSystem: pointer.Get("LTS"),
235+
ClusterType: pointer.Get("REPLICASET"),
236+
TerminationProtectionEnabled: pointer.Get(false),
237+
ReplicationSpecs: &[]atlasv2.ReplicationSpec20240805{
238+
{
239+
RegionConfigs: &[]atlasv2.CloudRegionConfig20240805{
240+
{
241+
Priority: pointer.Get(7),
242+
RegionName: pointer.Get("US_EAST_1"),
243+
ProviderName: pointer.Get("AWS"),
244+
ElectableSpecs: &atlasv2.HardwareSpec20240805{
245+
DiskSizeGB: pointer.Get(10.0),
246+
InstanceSize: pointer.Get("M10"),
247+
NodeCount: pointer.Get(3),
248+
},
249+
},
250+
},
251+
},
252+
},
253+
}
254+
255+
mockStore.
256+
EXPECT().
257+
UpgradeFlexCluster("", reqBody).
258+
Return(expected, nil).
259+
Times(1)
260+
261+
if err := upgradeOpts.Run(); err != nil {
262+
t.Fatalf("Run() unexpected error: %v", err)
263+
}
264+
})
265+
}

internal/mocks/mock_clusters.go

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

0 commit comments

Comments
 (0)