Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions internal/cli/atlas/quickstart/access_list_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/AlecAivazis/survey/v2"
"github.com/mongodb/mongodb-atlas-cli/internal/cli"
"github.com/mongodb/mongodb-atlas-cli/internal/flag"
"github.com/mongodb/mongodb-atlas-cli/internal/store"
"github.com/mongodb/mongodb-atlas-cli/internal/telemetry"
atlas "go.mongodb.org/atlas/mongodbatlas"
Expand All @@ -35,27 +36,30 @@ func (opts *Opts) createAccessList() error {
}

func (opts *Opts) askAccessListOptions() error {
if len(opts.IPAddresses) > 0 {
if !opts.shouldAskForValue(flag.AccessListIP) {
return nil
}
message := ""

if len(opts.IPAddresses) == 0 {
publicIP := store.IPAddress()
if publicIP != "" {
message = fmt.Sprintf(" [Press Enter to use your public IP address '%s']", publicIP)
}
opts.IPAddresses = append(opts.IPAddresses, publicIP)
}
fmt.Print(`
[Set up your database network access details]
`)
message := ""
publicIP := store.IPAddress()
if publicIP != "" {
message = fmt.Sprintf(" [Press Enter to use your public IP address '%s']", publicIP)
}
err := telemetry.TrackAskOne(
newAccessListQuestion(publicIP, message),
newAccessListQuestion(strings.Join(opts.IPAddresses, ", "), message),
&opts.IPAddressesResponse,
survey.WithValidator(survey.Required),
)

if err == nil && opts.IPAddressesResponse != "" {
ips := strings.Split(opts.IPAddressesResponse, ",")
opts.IPAddresses = append(opts.IPAddresses, ips...)
opts.IPAddresses = ips
}
return err
}
Expand Down
13 changes: 8 additions & 5 deletions internal/cli/atlas/quickstart/cluster_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/AlecAivazis/survey/v2"
"github.com/mongodb/mongodb-atlas-cli/internal/cli"
"github.com/mongodb/mongodb-atlas-cli/internal/flag"
"github.com/mongodb/mongodb-atlas-cli/internal/search"
"github.com/mongodb/mongodb-atlas-cli/internal/telemetry"
"github.com/mongodb/mongodb-atlas-cli/internal/usage"
Expand All @@ -40,16 +41,18 @@ func (opts *Opts) createCluster() error {
func (opts *Opts) askClusterOptions() error {
var qs []*survey.Question

if opts.ClusterName == "" {
opts.ClusterName = opts.defaultName
if opts.shouldAskForValue(flag.ClusterName) {
if opts.ClusterName == "" {
opts.ClusterName = opts.defaultName
}
qs = append(qs, newClusterNameQuestion(opts.ClusterName))
}

if opts.Provider == "" {
if opts.shouldAskForValue(flag.Provider) {
qs = append(qs, newClusterProviderQuestion())
}

if opts.Provider == "" || opts.ClusterName == "" || opts.Region == "" {
if opts.shouldAskForValue(flag.ClusterName) || opts.shouldAskForValue(flag.Provider) || opts.shouldAskForValue(flag.Region) {
fmt.Print(`
[Set up your Atlas cluster]
`)
Expand All @@ -60,7 +63,7 @@ func (opts *Opts) askClusterOptions() error {
}

// We need the provider to ask for the region
if opts.Region == "" {
if opts.shouldAskForValue(flag.Region) {
return opts.askClusterRegion()
}
return nil
Expand Down
4 changes: 3 additions & 1 deletion internal/cli/atlas/quickstart/confirm_cluster_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
const loadSampleDataMsg = `
Load sample data: Yes`

var ErrUserAborted = errors.New("user-aborted. Not creating cluster")

func (opts *Opts) askConfirmConfigQuestion() error {
if opts.Confirm {
return nil
Expand Down Expand Up @@ -68,7 +70,7 @@ Allow connections from (IP Address): %s
}

if !opts.Confirm {
return errors.New("user-aborted. Not creating cluster")
return ErrUserAborted
}
return nil
}
23 changes: 14 additions & 9 deletions internal/cli/atlas/quickstart/dbuser_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import (
"errors"
"fmt"

"github.com/mongodb/mongodb-atlas-cli/internal/config"
"github.com/mongodb/mongodb-atlas-cli/internal/telemetry"

"github.com/AlecAivazis/survey/v2"
"github.com/mongodb/mongodb-atlas-cli/internal/config"
"github.com/mongodb/mongodb-atlas-cli/internal/convert"
"github.com/mongodb/mongodb-atlas-cli/internal/flag"
"github.com/mongodb/mongodb-atlas-cli/internal/randgen"
"github.com/mongodb/mongodb-atlas-cli/internal/telemetry"
atlas "go.mongodb.org/atlas/mongodbatlas"
)

Expand All @@ -40,23 +40,28 @@ func (opts *Opts) askDBUserOptions() error {

if opts.DBUsername == "" {
opts.DBUsername = opts.defaultName
}

if opts.shouldAskForValue(flag.Username) {
qs = append(qs, newDBUsernameQuestion(opts.DBUsername, opts.validateUniqueUsername))
}

if opts.DBUserPassword == "" {
pwd, err := generatePassword()
if err != nil {
return err
if opts.shouldAskForValue(flag.Password) {
if opts.DBUserPassword == "" {
pwd, err := generatePassword()
if err != nil {
return err
}
opts.DBUserPassword = pwd
}
opts.DBUserPassword = pwd

minLength := 10
if config.Service() == config.CloudGovService {
minLength = 12
}
message := fmt.Sprintf(" [Must be >%d characters. Press Enter to use an auto-generated password]", minLength)

qs = append(qs, newDBUserPasswordQuestion(pwd, message))
qs = append(qs, newDBUserPasswordQuestion(opts.DBUserPassword, message))
}

if len(qs) == 0 {
Expand Down
114 changes: 94 additions & 20 deletions internal/cli/atlas/quickstart/quick_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/AlecAivazis/survey/v2"
"github.com/mongodb/mongodb-atlas-cli/internal/cli"
"github.com/mongodb/mongodb-atlas-cli/internal/cli/auth"
"github.com/mongodb/mongodb-atlas-cli/internal/config"
"github.com/mongodb/mongodb-atlas-cli/internal/flag"
"github.com/mongodb/mongodb-atlas-cli/internal/log"
Expand All @@ -37,6 +38,7 @@ import (
"github.com/mongodb/mongodb-atlas-cli/internal/usage"
"github.com/mongodb/mongodb-atlas-cli/internal/validate"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
atlas "go.mongodb.org/atlas/mongodbatlas"
)

Expand Down Expand Up @@ -86,6 +88,8 @@ const (
type Opts struct {
cli.GlobalOpts
cli.WatchOpts
login auth.LoginFlow
loginOpts *auth.LoginOpts
defaultName string
ClusterName string
Tier string
Expand All @@ -104,6 +108,9 @@ type Opts struct {
Confirm bool
CurrentIP bool
store store.AtlasClusterQuickStarter
shouldRunLogin bool
flags *pflag.FlagSet
flagSet map[string]struct{}
}

type quickstart struct {
Expand All @@ -123,6 +130,17 @@ type Flow interface {
Run() error
}

func NewQuickstartFlow(qsOpts *Opts) Flow {
return qsOpts
}

func NewQuickstartOpts(loginOpts *auth.LoginOpts) *Opts {
return &Opts{
loginOpts: loginOpts,
login: auth.NewLoginFlow(loginOpts),
}
}

func (opts *Opts) initStore(ctx context.Context) func() error {
return func() error {
var err error
Expand All @@ -131,13 +149,59 @@ func (opts *Opts) initStore(ctx context.Context) func() error {
}
}

func (opts *Opts) quickstartPreRun() error {
return opts.PreRunE(
opts.ValidateProjectID,
)
func (opts *Opts) quickstartPreRun(ctx context.Context, outWriter io.Writer) error {
opts.shouldRunLogin = false
opts.OutWriter = outWriter

// Get authentication status to define whether login should be run
status, _ := auth.GetStatus(ctx)
if status == auth.LoggedInWithValidToken || status == auth.LoggedInWithAPIKeys {
return opts.PreRunE(
opts.ValidateProjectID,
)
}

// If customer used --force and is not authenticated, check credentials and proceed. Likely to
// throw an error here.
if opts.Confirm {
if err := validate.Credentials(); err != nil {
return err
}
return opts.PreRunE(
opts.ValidateProjectID,
)
}

opts.loginOpts.OutWriter = opts.OutWriter
if err := opts.login.PreRun(); err != nil {
return err
}

opts.shouldRunLogin = true
_, _ = fmt.Fprintf(opts.OutWriter, `This action requires authentication.
`)
return opts.login.Run(ctx)
}

func (opts *Opts) shouldAskForValue(f string) bool {
_, isFlagSet := opts.flagSet[f]
return !isFlagSet
}

func (opts *Opts) trackFlags() {
if opts.flags == nil {
opts.flagSet = make(map[string]struct{})
return
}

opts.flagSet = make(map[string]struct{}, opts.flags.NFlag())
opts.flags.Visit(func(f *pflag.Flag) {
opts.flagSet[f.Name] = struct{}{}
})
}

func (opts *Opts) PreRun(ctx context.Context, outWriter io.Writer) error {
opts.shouldRunLogin = false
opts.setTier()

if opts.CurrentIP && len(opts.IPAddresses) > 0 {
Expand All @@ -154,6 +218,7 @@ func (opts *Opts) Run() error {
const base10 = 10
opts.defaultName = "Cluster" + strconv.FormatInt(time.Now().Unix(), base10)[5:]
opts.providerAndRegionToConstant()
opts.trackFlags()

if opts.CurrentIP {
if publicIP := store.IPAddress(); publicIP != "" {
Expand Down Expand Up @@ -414,23 +479,31 @@ func (opts *Opts) replaceWithDefaultSettings(values *quickstart) {
}

func (opts *Opts) interactiveSetup() error {
if err := opts.askClusterOptions(); err != nil {
return err
}
for {
if err := opts.askClusterOptions(); err != nil {
return err
}

if err := opts.askSampleDataQuestion(); err != nil {
return err
}
if err := opts.askSampleDataQuestion(); err != nil {
return err
}

if err := opts.askDBUserOptions(); err != nil {
return err
}
if err := opts.askDBUserOptions(); err != nil {
return err
}

if err := opts.askAccessListOptions(); err != nil {
return err
}
if err := opts.askAccessListOptions(); err != nil {
return err
}

return opts.askConfirmConfigQuestion()
if err := opts.askConfirmConfigQuestion(); err != nil && !errors.Is(err, ErrUserAborted) {
return err
}

if opts.Confirm {
return nil
}
}
}

// Builder
Expand All @@ -444,7 +517,7 @@ func (opts *Opts) interactiveSetup() error {
// [--skipMongosh skipMongosh]
// [--default]
func Builder() *cobra.Command {
opts := &Opts{}
opts := NewQuickstartOpts(auth.NewLoginOpts())
cmd := &cobra.Command{
Use: "quickstart",
Short: "Create and access an Atlas Cluster.",
Expand All @@ -453,12 +526,13 @@ func Builder() *cobra.Command {
$ %[1]s quickstart --force
$ %[1]s quickstart --clusterName Test --provider GCP --username dbuserTest`, cli.ExampleAtlasEntryPoint()),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := opts.quickstartPreRun(); err != nil {
if err := opts.PreRun(cmd.Context(), cmd.OutOrStdout()); err != nil {
return err
}
return opts.PreRun(cmd.Context(), cmd.OutOrStdout())
return opts.quickstartPreRun(cmd.Context(), cmd.OutOrStdout())
},
RunE: func(cmd *cobra.Command, args []string) error {
opts.flags = cmd.Flags()
return opts.Run()
},
}
Expand Down
Loading