• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb / mongodb-atlas-cli / 16677365954

01 Aug 2025 01:40PM UTC coverage: 57.846% (-0.1%) from 57.953%
16677365954

Pull #4075

github

cveticm
Addes unit tests
Pull Request #4075: CLOUDP-330236: Adds NewServiceAccountTransport

0 of 12 new or added lines in 1 file covered. (0.0%)

518 existing lines in 22 files now uncovered.

23774 of 41099 relevant lines covered (57.85%)

2.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.37
/internal/cli/setup/setup_cmd.go
1
// Copyright 2022 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

15
package setup
16

17
import (
18
        "context"
19
        "errors"
20
        "fmt"
21
        "io"
22
        "os"
23
        "strconv"
24
        "strings"
25
        "syscall"
26
        "time"
27

28
        "github.com/AlecAivazis/survey/v2"
29
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli"
30
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/auth"
31
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/commonerrors"
32
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/require"
33
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/compass"
34
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/config"
35
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/flag"
36
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/log"
37
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/mongosh"
38
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/prerun"
39
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/sighandle"
40
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store"
41
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/telemetry"
42
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/usage"
43
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/validate"
44
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/vscode"
45
        "github.com/spf13/cobra"
46
        "github.com/spf13/pflag"
47
        atlasClustersPinned "go.mongodb.org/atlas-sdk/v20240530005/admin"
48
        atlasv2 "go.mongodb.org/atlas-sdk/v20250312005/admin"
49
)
50

51
const (
52
        DefaultAtlasTier           = "M0"
53
        defaultAtlasGovTier        = "M30"
54
        atlasAdmin                 = "atlasAdmin"
55
        replicaSet                 = "REPLICASET"
56
        defaultProvider            = "AWS"
57
        defaultRegion              = "US_EAST_1"
58
        defaultRegionGCP           = "US_EAST_4"
59
        defaultRegionAzure         = "US_EAST_2"
60
        defaultRegionGov           = "US_GOV_EAST_1"
61
        defaultSettings            = "default"
62
        customSettings             = "custom"
63
        cancelSettings             = "cancel"
64
        skipConnect                = "skip"
65
        compassConnect             = "compass"
66
        mongoshConnect             = "mongosh"
67
        vsCodeConnect              = "vscode"
68
        clusterWideScaling         = "clusterWideScaling"
69
        independentShardScaling    = "independentShardScaling"
70
        deprecateMessageSharedTier = "The '%s' tier is deprecated. For the migration guide and timeline, visit: https://dochub.mongodb.org/core/flex-migration.\n"
71
)
72

73
var (
74
        settingOptions      = []string{defaultSettings, customSettings, cancelSettings}
75
        settingsDescription = map[string]string{
76
                defaultSettings: "With default settings",
77
                customSettings:  "With custom settings",
78
                cancelSettings:  "Cancel setup",
79
        }
80
        connectWithOptions     = []string{mongoshConnect, compassConnect, vsCodeConnect, skipConnect}
81
        connectWithDescription = map[string]string{
82
                mongoshConnect: "MongoDB Shell",
83
                compassConnect: "MongoDB Compass",
84
                vsCodeConnect:  "MongoDB for VsCode",
85
                skipConnect:    "Skip Connection",
86
        }
87
)
88

89
var errNeedsProject = errors.New("ensure you select or add a project to the profile")
90

91
const setupTemplateCloseHandler = `
92
Enter 'atlas cluster watch %s' to learn when your cluster is available.
93
`
94

95
const setupTemplateStoreWarning = `
96
Store your database authentication access details in a secure location:
97
Database User Username: %s
98
Database User Password: %s
99
`
100

101
const setupTemplateIntro = `Press [Enter] to use the default values.
102

103
Enter [?] on any option to get help.
104
`
105

106
const setupTemplateCluster = `
107
Creating your cluster... [It's safe to 'Ctrl + C']
108
`
109
const setupTemplateIPNotFound = `
110
We could not find your public IP address. To add your IP address run:
111
  atlas accesslist create
112

113
`
114

115
//go:generate go tool go.uber.org/mock/mockgen -typed -destination=setup_mock_test.go -package=setup . AtlasClusterQuickStarter
116

117
type profileReader interface {
118
        ProjectID() string
119
        OrgID() string
120
}
121

122
type AtlasClusterQuickStarter interface {
123
        AddSampleData(string, string) (*atlasv2.SampleDatasetStatus, error)
124
        SampleDataStatus(string, string) (*atlasv2.SampleDatasetStatus, error)
125
        CloudProviderRegions(string, string, []string) (*atlasv2.PaginatedApiAtlasProviderRegions, error)
126
        CreateCluster(v15 *atlasClustersPinned.AdvancedClusterDescription) (*atlasClustersPinned.AdvancedClusterDescription, error)
127
        LatestAtlasCluster(string, string) (*atlasv2.ClusterDescription20240805, error)
128
        CreateClusterLatest(v15 *atlasv2.ClusterDescription20240805) (*atlasv2.ClusterDescription20240805, error)
129
        MDBVersions(projectID string, opt *store.MDBVersionListOptions) (*atlasv2.PaginatedAvailableVersion, error)
130
        CreateDatabaseUser(*atlasv2.CloudDatabaseUser) (*atlasv2.CloudDatabaseUser, error)
131
        DatabaseUser(string, string, string) (*atlasv2.CloudDatabaseUser, error)
132
        CreateProjectIPAccessList([]*atlasv2.NetworkPermissionEntry) (*atlasv2.PaginatedNetworkAccess, error)
133
}
134

135
type Opts struct {
136
        cli.ProjectOpts
137
        cli.WatchOpts
138
        login                       auth.LoginOpts
139
        config                      profileReader
140
        store                       AtlasClusterQuickStarter
141
        defaultName                 string
142
        ClusterName                 string
143
        Provider                    string
144
        Region                      string
145
        IPAddresses                 []string
146
        IPAddressesResponse         string
147
        DBUsername                  string
148
        DBUserPassword              string
149
        SampleDataJobID             string
150
        MDBVersion                  string
151
        Tier                        string
152
        Tag                         map[string]string
153
        SkipSampleData              bool
154
        SkipMongosh                 bool
155
        connectWith                 string
156
        DefaultValue                bool
157
        Confirm                     bool
158
        CurrentIP                   bool
159
        EnableTerminationProtection bool
160
        AutoScalingMode             string
161
        flags                       *pflag.FlagSet
162
        flagSet                     map[string]struct{}
163
        settings                    string
164
        connectionString            string
165

166
        // control
167
        skipLogin bool
168
}
169

170
type clusterSettings struct {
171
        ClusterName                 string
172
        Provider                    string
173
        Region                      string
174
        Tier                        string
175
        DBUsername                  string
176
        DBUserPassword              string
177
        IPAddresses                 []string
178
        EnableTerminationProtection bool
179
        SkipSampleData              bool
180
        Tag                         map[string]string
181
        MdbVersion                  string
182
}
183

184
func (opts *Opts) providerAndRegionToConstant() {
1✔
185
        opts.Provider = strings.ToUpper(opts.Provider)
1✔
186
        opts.Region = strings.ReplaceAll(strings.ToUpper(opts.Region), "-", "_")
1✔
187
}
1✔
188

189
func (opts *Opts) trackFlags() {
1✔
190
        if opts.flags == nil {
1✔
191
                opts.flagSet = make(map[string]struct{})
×
192
                return
×
193
        }
×
194

195
        opts.flagSet = make(map[string]struct{}, opts.flags.NFlag())
1✔
196
        opts.flags.Visit(func(f *pflag.Flag) {
2✔
197
                opts.flagSet[f.Name] = struct{}{}
1✔
198
        })
1✔
199
}
200

201
func (opts *Opts) newDefaultValues() (*clusterSettings, error) {
1✔
202
        values := &clusterSettings{}
1✔
203
        values.SkipSampleData = opts.SkipSampleData
1✔
204

1✔
205
        values.ClusterName = opts.ClusterName
1✔
206
        if opts.ClusterName == "" {
2✔
207
                values.ClusterName = opts.defaultName
1✔
208
        }
1✔
209

210
        values.Provider = opts.Provider
1✔
211
        if opts.Provider == "" {
2✔
212
                values.Provider = defaultProvider
1✔
213
        }
1✔
214

215
        values.Region = opts.Region
1✔
216
        if opts.Region == "" {
2✔
217
                if config.CloudGovService == config.Service() {
1✔
218
                        values.Region = defaultRegionGov
×
219
                } else {
1✔
220
                        switch strings.ToUpper(opts.Provider) {
1✔
221
                        case "AZURE":
×
222
                                values.Region = defaultRegionAzure
×
223
                        case "GCP":
×
224
                                values.Region = defaultRegionGCP
×
225
                        default:
1✔
226
                                values.Region = defaultRegion
1✔
227
                        }
228
                }
229
        }
230

231
        values.MdbVersion = opts.MDBVersion
1✔
232
        if opts.MDBVersion == "" {
2✔
233
                opts.MDBVersion, _ = cli.DefaultMongoDBMajorVersion()
1✔
234
        }
1✔
235

236
        values.DBUsername = opts.DBUsername
1✔
237
        if opts.DBUsername == "" {
2✔
238
                values.DBUsername = opts.defaultName
1✔
239
        }
1✔
240

241
        values.DBUserPassword = opts.DBUserPassword
1✔
242
        if opts.DBUserPassword == "" {
2✔
243
                pwd, err := generatePassword()
1✔
244
                if err != nil {
1✔
245
                        return nil, err
×
246
                }
×
247
                values.DBUserPassword = pwd
1✔
248
        }
249

250
        values.IPAddresses = opts.IPAddresses
1✔
251
        if len(opts.IPAddresses) == 0 {
2✔
252
                if publicIP := store.IPAddress(); publicIP != "" {
2✔
253
                        values.IPAddresses = []string{publicIP}
1✔
254
                } else {
2✔
255
                        _, _ = log.Warning(setupTemplateIPNotFound)
1✔
256
                }
1✔
257
        }
258

259
        values.Tier = opts.Tier
1✔
260
        values.EnableTerminationProtection = opts.EnableTerminationProtection
1✔
261
        values.Tag = opts.Tag
1✔
262

1✔
263
        return values, nil
1✔
264
}
265

266
func (opts *Opts) clusterCreationWatcher() (any, bool, error) {
1✔
267
        result, err := opts.store.LatestAtlasCluster(opts.ConfigProjectID(), opts.ClusterName)
1✔
268
        if err != nil {
1✔
269
                return nil, false, err
×
270
        }
×
271
        return nil, result.GetStateName() == "IDLE", nil
1✔
272
}
273

274
func (opts *Opts) sampleDataWatcher() (any, bool, error) {
×
275
        result, err := opts.store.SampleDataStatus(opts.ConfigProjectID(), opts.SampleDataJobID)
×
276
        if err != nil {
×
277
                return nil, false, err
×
278
        }
×
279
        if result.GetState() == "FAILED" {
×
280
                return nil, false, fmt.Errorf("failed to load data: %s", result.GetErrorMessage())
×
281
        }
×
282
        return nil, result.GetState() == "COMPLETED", nil
×
283
}
284

285
func (opts *Opts) loadSampleData() error {
1✔
286
        if opts.SkipSampleData {
2✔
287
                return nil
1✔
288
        }
1✔
289

290
        fmt.Print(`
×
291
Loading sample data into your cluster... [It's safe to 'Ctrl + C']
×
292
`)
×
293
        sampleDataJob, err := opts.store.AddSampleData(opts.ConfigProjectID(), opts.ClusterName)
×
294

×
295
        if err != nil {
×
296
                return nil
×
297
        }
×
298

299
        opts.SampleDataJobID = sampleDataJob.GetId()
×
300

×
301
        _, err = opts.Watch(opts.sampleDataWatcher)
×
302
        return err
×
303
}
304

305
func (opts *Opts) createResources() error {
1✔
306
        if err := opts.createDatabaseUser(); err != nil {
2✔
307
                return err
1✔
308
        }
1✔
309

310
        if err := opts.createAccessList(); err != nil {
1✔
311
                return err
×
312
        }
×
313

314
        if err := opts.createCluster(); err != nil {
1✔
315
                target, _ := atlasClustersPinned.AsError(err)
×
316
                if target.GetErrorCode() == "CANNOT_CREATE_FREE_CLUSTER_VIA_PUBLIC_API" && strings.Contains(strings.ToLower(target.GetDetail()), cli.ErrFreeClusterAlreadyExists.Error()) {
×
317
                        return cli.ErrFreeClusterAlreadyExists
×
318
                } else if target.GetErrorCode() == "INVALID_ATTRIBUTE" && strings.Contains(target.GetDetail(), "regionName") {
×
319
                        return cli.ErrNoRegionExistsTryCommand
×
320
                }
×
321
                return err
×
322
        }
323
        return nil
1✔
324
}
325

326
func (opts *Opts) askSampleDataQuestion() error {
×
327
        if opts.SkipSampleData {
×
328
                return nil
×
329
        }
×
330

331
        q := newSampleDataQuestion()
×
332
        var addSampleData bool
×
333
        if err := telemetry.TrackAskOne(q, &addSampleData); err != nil {
×
334
                return err
×
335
        }
×
336
        opts.SkipSampleData = !addSampleData
×
337

×
338
        return nil
×
339
}
340

341
func (opts *Opts) interactiveSetup() error {
×
342
        if err := opts.askClusterOptions(); err != nil {
×
343
                return err
×
344
        }
×
345

346
        if err := opts.askSampleDataQuestion(); err != nil {
×
347
                return err
×
348
        }
×
349

350
        if err := opts.askDBUserOptions(); err != nil {
×
351
                return err
×
352
        }
×
353

354
        return opts.askAccessListOptions()
×
355
}
356

357
func (opts *Opts) shouldAskForValue(f string) bool {
×
358
        _, isFlagSet := opts.flagSet[f]
×
359
        return !isFlagSet
×
360
}
×
361

362
func (opts *Opts) replaceWithDefaultSettings(values *clusterSettings) {
1✔
363
        if values.ClusterName != "" {
2✔
364
                opts.ClusterName = values.ClusterName
1✔
365
        }
1✔
366

367
        if values.Provider != "" {
2✔
368
                opts.Provider = values.Provider
1✔
369
        }
1✔
370

371
        if values.Region != "" {
2✔
372
                opts.Region = values.Region
1✔
373
        }
1✔
374

375
        if values.MdbVersion != "" {
1✔
376
                opts.MDBVersion = values.MdbVersion
×
377
        }
×
378

379
        if values.DBUsername != "" {
2✔
380
                opts.DBUsername = values.DBUsername
1✔
381
        }
1✔
382

383
        if values.DBUserPassword != "" {
2✔
384
                opts.DBUserPassword = values.DBUserPassword
1✔
385
        }
1✔
386

387
        if values.IPAddresses != nil {
2✔
388
                opts.IPAddresses = values.IPAddresses
1✔
389
        }
1✔
390

391
        opts.EnableTerminationProtection = values.EnableTerminationProtection
1✔
392
        opts.SkipSampleData = values.SkipSampleData
1✔
393
        opts.Tag = values.Tag
1✔
394
}
395

396
// setupCloseHandler creates a 'listener' on a new goroutine which will notify the
397
// program if it receives an interrupt from the OS. We then handle this by printing
398
// the dbUsername and dbPassword.
399
func (opts *Opts) setupCloseHandler() {
1✔
400
        sighandle.Notify(func(sig os.Signal) {
1✔
401
                fmt.Printf(setupTemplateCloseHandler, opts.ClusterName)
×
402
                telemetry.FinishTrackingCommand(telemetry.TrackOptions{
×
403
                        Signal: sig.String(),
×
404
                })
×
405
                os.Exit(0)
×
406
        }, os.Interrupt, syscall.SIGTERM)
×
407
}
408

409
func (opts *Opts) Run(ctx context.Context) error {
1✔
410
        if !opts.skipLogin {
1✔
411
                _, _ = fmt.Fprintf(opts.OutWriter, `Next steps:
×
412
1. Log in and verify your MongoDB Atlas account in your browser.
×
413
2. Return to the terminal to create your first free MongoDB database in Atlas.
×
414

×
415
If you wish to register a new account, run: 'atlas auth register'.
×
416
`)
×
417

×
418
                if err := opts.login.LoginRun(ctx); err != nil {
×
UNCOV
419
                        return err
×
420
                }
×
421
        }
422
        if err := opts.clusterPreRun(ctx, opts.OutWriter); err != nil {
1✔
423
                return err
×
424
        }
×
425

426
        if opts.config.ProjectID() == "" {
1✔
427
                return fmt.Errorf("%w: %s", errNeedsProject, config.Default().Name())
×
UNCOV
428
        }
×
429

430
        return opts.setupCluster()
1✔
431
}
432

433
func (opts *Opts) clusterPreRun(ctx context.Context, outWriter io.Writer) error {
1✔
434
        opts.setTier()
1✔
435
        defaultProfile := config.Default()
1✔
436

1✔
437
        return opts.PreRunE(
1✔
438
                opts.initStore(ctx),
1✔
439
                opts.login.SyncWithOAuthAccessProfile(defaultProfile),
1✔
440
                opts.login.InitFlow(defaultProfile),
1✔
441
                opts.InitOutput(outWriter, ""),
1✔
442
        )
1✔
443
}
1✔
444

445
func (opts *Opts) setupCluster() error {
1✔
446
        const base10 = 10
1✔
447
        opts.defaultName = "Cluster" + strconv.FormatInt(time.Now().Unix(), base10)[5:]
1✔
448
        opts.providerAndRegionToConstant()
1✔
449
        opts.trackFlags()
1✔
450

1✔
451
        if opts.CurrentIP {
1✔
UNCOV
452
                if publicIP := store.IPAddress(); publicIP != "" {
×
UNCOV
453
                        opts.IPAddresses = []string{publicIP}
×
UNCOV
454
                } else {
×
UNCOV
455
                        _, _ = log.Warning(setupTemplateIPNotFound)
×
UNCOV
456
                }
×
457
        }
458

459
        values, dErr := opts.newDefaultValues()
1✔
460
        if dErr != nil {
1✔
461
                return dErr
×
462
        }
×
463

464
        if opts.Confirm {
2✔
465
                opts.settings = defaultSettings
1✔
466
        } else {
1✔
UNCOV
467
                if err := opts.askConfirmDefaultQuestion(values); err != nil {
×
UNCOV
468
                        return err
×
469
                }
×
470
        }
471

472
        switch opts.settings {
1✔
UNCOV
473
        case customSettings:
×
UNCOV
474
                fmt.Print(setupTemplateIntro)
×
475

×
476
                if err := opts.interactiveSetup(); err != nil {
×
477
                        return err
×
UNCOV
478
                }
×
479
        case defaultSettings:
1✔
480
                opts.replaceWithDefaultSettings(values)
1✔
481
        case cancelSettings:
×
482
                _, _ = fmt.Println("user-aborted. Not creating cluster")
×
483
                return nil
×
484
        }
485

486
        // Create db user, access list and cluster
487
        if err := opts.createResources(); err != nil {
2✔
488
                return err
1✔
489
        }
1✔
490

491
        fmt.Printf(`We are deploying %s...
1✔
492
`, opts.ClusterName)
1✔
493

1✔
494
        fmt.Printf(setupTemplateStoreWarning, opts.DBUsername, opts.DBUserPassword)
1✔
495
        opts.setupCloseHandler()
1✔
496

1✔
497
        fmt.Print(setupTemplateCluster)
1✔
498

1✔
499
        // Watch cluster creation
1✔
500
        if _, er := opts.Watch(opts.clusterCreationWatcher); er != nil {
1✔
UNCOV
501
                return er
×
UNCOV
502
        }
×
503

504
        fmt.Println("Cluster created.")
1✔
505

1✔
506
        // Get cluster's connection string
1✔
507
        cluster, err := opts.store.LatestAtlasCluster(opts.ConfigProjectID(), opts.ClusterName)
1✔
508
        if err != nil {
1✔
509
                return err
×
510
        }
×
511

512
        opts.connectionString = cluster.ConnectionStrings.GetStandardSrv()
1✔
513

1✔
514
        fmt.Printf("Your connection string: %v\n", opts.connectionString)
1✔
515

1✔
516
        if err := opts.loadSampleData(); err != nil {
1✔
517
                return err
×
518
        }
×
519

520
        return opts.runConnectWith()
1✔
521
}
522

523
func (opts *Opts) runConnectWith() error {
1✔
524
        if opts.connectWith == "" {
2✔
525
                if opts.SkipMongosh { // deprecated flag --skipMongosh
2✔
526
                        return nil
1✔
527
                }
1✔
528

UNCOV
529
                if opts.Confirm { // --force
×
UNCOV
530
                        opts.connectWith = skipConnect
×
UNCOV
531
                } else {
×
UNCOV
532
                        if err := opts.promptConnect(); err != nil {
×
UNCOV
533
                                return err
×
UNCOV
534
                        }
×
535
                }
536
        }
537

538
        switch opts.connectWith {
×
539
        case skipConnect:
×
540
                _, _ = fmt.Fprintln(os.Stderr, "connection skipped")
×
541
        case compassConnect:
×
542
                if !compass.Detect() {
×
UNCOV
543
                        return compass.ErrCompassNotInstalled
×
UNCOV
544
                }
×
UNCOV
545
                if _, err := log.Warningln("Launching MongoDB Compass..."); err != nil {
×
546
                        return err
×
547
                }
×
548
                return compass.Run(opts.DBUsername, opts.DBUserPassword, opts.connectionString)
×
549
        case mongoshConnect:
×
550
                if !mongosh.Detect() {
×
551
                        return mongosh.ErrMongoshNotInstalled
×
552
                }
×
553
                return mongosh.Run(opts.DBUsername, opts.DBUserPassword, opts.connectionString)
×
554
        case vsCodeConnect:
×
555
                if !vscode.Detect() {
×
556
                        return vscode.ErrVsCodeCliNotInstalled
×
557
                }
×
558
                if _, err := log.Warningln("Launching VsCode..."); err != nil {
×
559
                        return err
×
560
                }
×
561
                return vscode.SaveConnection(opts.connectionString, opts.ClusterName, "atlas")
×
562
        }
563

564
        return nil
×
565
}
566

567
func (opts *Opts) promptConnect() error {
×
568
        p := &survey.Select{
×
569
                Message: "How would you like to connect to your cluster?",
×
UNCOV
570
                Options: connectWithOptions,
×
UNCOV
571
                Description: func(value string, _ int) string {
×
572
                        return connectWithDescription[value]
×
UNCOV
573
                },
×
574
        }
575

576
        return telemetry.TrackAskOne(p, &opts.connectWith, nil)
×
577
}
578

579
func (opts *Opts) PreRun(ctx context.Context) error {
1✔
580
        opts.skipLogin = true
1✔
581

1✔
582
        if err := validate.NoAPIKeys(); err != nil {
2✔
583
                // Why are we ignoring the error?
1✔
584
                // Because if the user has API keys, we just want to proceed with the flow
1✔
585
                // Then why not remove the error?
1✔
586
                // The error is useful in other components that call `validate.NoAPIKeys()`
1✔
587
                return nil
1✔
588
        }
1✔
589

590
        // if profile has access token and refresh token is valid, we can skip login
UNCOV
591
        if _, err := auth.AccountWithAccessToken(); err == nil {
×
UNCOV
592
                if err := opts.login.RefreshAccessToken(ctx); !commonerrors.IsInvalidRefreshToken(err) {
×
UNCOV
593
                        return nil
×
UNCOV
594
                }
×
595
        }
UNCOV
596
        opts.skipLogin = false
×
UNCOV
597
        return nil
×
598
}
599

600
func (opts *Opts) initStore(ctx context.Context) func() error {
1✔
601
        return func() error {
2✔
602
                var err error
1✔
603
                opts.store, err = store.New(store.AuthenticatedPreset(config.Default()), store.WithContext(ctx))
1✔
604
                return err
1✔
605
        }
1✔
606
}
607

608
func (opts *Opts) setTier() {
1✔
609
        if config.CloudGovService == config.Service() && opts.Tier == DefaultAtlasTier {
1✔
UNCOV
610
                opts.Tier = defaultAtlasGovTier
×
UNCOV
611
        }
×
612
}
613

614
func (opts *Opts) SetupAtlasFlags(cmd *cobra.Command) {
1✔
615
        cmd.Flags().StringVar(&opts.Tier, flag.Tier, DefaultAtlasTier, usage.Tier)
1✔
616
        cmd.Flags().StringVar(&opts.Provider, flag.Provider, "", usage.Provider)
1✔
617
        cmd.Flags().StringVarP(&opts.Region, flag.Region, flag.RegionShort, "", usage.Region)
1✔
618
        cmd.Flags().StringSliceVar(&opts.IPAddresses, flag.AccessListIP, []string{}, usage.NetworkAccessListIPEntry)
1✔
619
        cmd.Flags().StringVar(&opts.DBUsername, flag.Username, "", usage.DBUsername)
1✔
620
        cmd.Flags().StringVar(&opts.DBUserPassword, flag.Password, "", usage.Password)
1✔
621
        cmd.Flags().BoolVar(&opts.EnableTerminationProtection, flag.EnableTerminationProtection, false, usage.EnableTerminationProtection)
1✔
622
        cmd.Flags().BoolVar(&opts.CurrentIP, flag.CurrentIP, false, usage.CurrentIPSimplified)
1✔
623
        cmd.Flags().StringToStringVar(&opts.Tag, flag.Tag, nil, usage.Tag)
1✔
624
        cmd.Flags().StringVar(&opts.AutoScalingMode, flag.AutoScalingMode, clusterWideScaling, usage.AutoScalingMode)
1✔
625

1✔
626
        opts.AddProjectOptsFlags(cmd)
1✔
627

1✔
628
        cmd.MarkFlagsMutuallyExclusive(flag.CurrentIP, flag.AccessListIP)
1✔
629
}
1✔
630

631
func (opts *Opts) SetupFlowFlags(cmd *cobra.Command) {
1✔
632
        cmd.Flags().BoolVar(&opts.SkipSampleData, flag.SkipSampleData, false, usage.SkipSampleData)
1✔
633
        cmd.Flags().BoolVar(&opts.SkipMongosh, flag.SkipMongosh, false, usage.SkipMongosh)
1✔
634
        cmd.Flags().BoolVar(&opts.Confirm, flag.Force, false, usage.ForceQuickstart)
1✔
635
        _ = cmd.Flags().MarkDeprecated(flag.SkipMongosh, "Use --connectWith instead")
1✔
636
        cmd.MarkFlagsMutuallyExclusive(flag.SkipMongosh, flag.ConnectWith)
1✔
637
}
1✔
638

639
func (opts *Opts) validateTier() error {
1✔
640
        opts.Tier = strings.ToUpper(opts.Tier)
1✔
641
        if opts.Tier == atlasM2 || opts.Tier == atlasM5 {
1✔
UNCOV
642
                _, _ = fmt.Fprintf(os.Stderr, deprecateMessageSharedTier, opts.Tier)
×
UNCOV
643
        }
×
644
        return nil
1✔
645
}
646

647
// Builder
648
// atlas setup
649
//
650
//        [--clusterName clusterName]
651
//        [--provider provider]
652
//        [--region regionName]
653
//        [--username username]
654
//        [--password password]
655
//        [--skipMongosh skipMongosh]
656
func Builder() *cobra.Command {
1✔
657
        opts := &Opts{}
1✔
658

1✔
659
        cmd := &cobra.Command{
1✔
660
                Use:     "setup",
1✔
661
                Aliases: []string{"quickstart"},
1✔
662
                Short:   "Login, authenticate, create, and access an Atlas cluster.",
1✔
663
                Long:    `This command takes you through login, default profile creation, creating your first free tier cluster and connecting to it using MongoDB Shell.`,
1✔
664
                Example: `  # Override default cluster settings like name, provider, or database username by using the command options
1✔
665
  atlas setup --clusterName Test --provider GCP --username dbuserTest`,
1✔
666
                Hidden: false,
1✔
667
                Args:   require.NoArgs,
1✔
668
                PreRunE: func(cmd *cobra.Command, _ []string) error {
2✔
669
                        defaultProfile := config.Default()
1✔
670
                        opts.config = defaultProfile
1✔
671
                        opts.OutWriter = cmd.OutOrStdout()
1✔
672
                        opts.login.OutWriter = opts.OutWriter
1✔
673

1✔
674
                        if err := opts.login.SyncWithOAuthAccessProfile(defaultProfile)(); err != nil {
1✔
UNCOV
675
                                return err
×
UNCOV
676
                        }
×
677
                        if err := opts.login.InitFlow(defaultProfile)(); err != nil {
1✔
UNCOV
678
                                return err
×
UNCOV
679
                        }
×
680
                        if err := opts.PreRun(cmd.Context()); err != nil {
1✔
UNCOV
681
                                return nil
×
UNCOV
682
                        }
×
683
                        var preRun []prerun.CmdOpt
1✔
684
                        // login pre run if applicable
1✔
685
                        if !opts.skipLogin {
1✔
UNCOV
686
                                opts.login.Asker = &telemetry.Ask{}
×
687
                                preRun = append(preRun, opts.login.LoginPreRun(cmd.Context()))
×
688
                        }
×
689
                        preRun = append(preRun, opts.validateTier)
1✔
690
                        preRun = append(preRun, validate.AutoScalingMode(opts.AutoScalingMode))
1✔
691

1✔
692
                        return opts.PreRunE(preRun...)
1✔
693
                },
694
                RunE: func(cmd *cobra.Command, _ []string) error {
1✔
695
                        opts.flags = cmd.Flags()
1✔
696
                        return opts.Run(cmd.Context())
1✔
697
                },
1✔
698
        }
699

700
        // Login related
701
        cmd.Flags().BoolVar(&opts.login.IsGov, "gov", false, "Login with Atlas for Government.")
1✔
702
        cmd.Flags().BoolVar(&opts.login.NoBrowser, "noBrowser", false, "Don't try to open a browser session.")
1✔
703
        // Setup related
1✔
704
        cmd.Flags().StringVar(&opts.MDBVersion, flag.MDBVersion, "", usage.DeploymentMDBVersion)
1✔
705
        cmd.Flags().StringVar(&opts.connectWith, flag.ConnectWith, "", usage.ConnectWithAtlasSetup)
1✔
706
        opts.SetupAtlasFlags(cmd)
1✔
707
        opts.SetupFlowFlags(cmd)
1✔
708

1✔
709
        cmd.Flags().StringVar(&opts.ClusterName, flag.ClusterName, "", usage.ClusterName)
1✔
710
        cmd.Flags().BoolVarP(&opts.DefaultValue, flag.Default, "Y", false, usage.QuickstartDefault)
1✔
711
        _ = cmd.Flags().MarkDeprecated(flag.Default, "please use --force instead")
1✔
712

1✔
713
        return cmd
1✔
714
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc