Skip to content

Commit af1c7c5

Browse files
jeroenvervaekeandreaangiolilloGustavo Bazan
authored
CLOUDP-137380: [CLI] profile names with dots(.) break the cli (#2566)
Co-authored-by: Andrea Angiolillo <[email protected]> Co-authored-by: Gustavo Bazan <[email protected]>
1 parent 72b6af0 commit af1c7c5

File tree

13 files changed

+217
-48
lines changed

13 files changed

+217
-48
lines changed

cmd/mongocli/mongocli.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import (
2424
"strings"
2525

2626
survey "github.com/AlecAivazis/survey/v2/core"
27+
"github.com/mongodb/mongodb-atlas-cli/internal/cli"
2728
"github.com/mongodb/mongodb-atlas-cli/internal/cli/root/mongocli"
2829
"github.com/mongodb/mongodb-atlas-cli/internal/config"
29-
"github.com/mongodb/mongodb-atlas-cli/internal/flag"
3030
"github.com/spf13/cobra"
3131
)
3232

@@ -106,12 +106,8 @@ func initConfig() {
106106
log.Fatalf("Error loading config: %v", err)
107107
}
108108

109-
if profile != "" {
110-
config.SetName(profile)
111-
} else if profile = config.GetString(flag.Profile); profile != "" {
112-
config.SetName(profile)
113-
} else if availableProfiles := config.List(); len(availableProfiles) == 1 {
114-
config.SetName(availableProfiles[0])
109+
if err := cli.InitProfile(profile); err != nil {
110+
log.Fatalf("Error loading profile: %v", err)
115111
}
116112
}
117113

internal/cli/atlas/clusters/region_tier_autocomplete.go

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

1717
import (
1818
"context"
19+
"fmt"
1920
"sort"
2021
"strings"
2122

@@ -38,7 +39,11 @@ type autoCompleteOpts struct {
3839

3940
func (opts *autoCompleteOpts) autocompleteTier() cli.AutoFunc {
4041
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
41-
opts.parseFlags(cmd)
42+
if err := opts.parseFlags(cmd); err != nil {
43+
cobra.CompErrorln(fmt.Sprintf("failed to parse flags: %v", err))
44+
return nil, cobra.ShellCompDirectiveError
45+
}
46+
4247
if err := validate.Credentials(); err != nil {
4348
cobra.CompErrorln("no credentials")
4449
return nil, cobra.ShellCompDirectiveError
@@ -86,7 +91,11 @@ func (opts *autoCompleteOpts) tierSuggestions(toComplete string) ([]string, erro
8691

8792
func (opts *autoCompleteOpts) autocompleteRegion() cli.AutoFunc {
8893
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
89-
opts.parseFlags(cmd)
94+
if err := opts.parseFlags(cmd); err != nil {
95+
cobra.CompErrorln(fmt.Sprintf("failed to parse flags: %v", err))
96+
return nil, cobra.ShellCompDirectiveError
97+
}
98+
9099
if err := validate.Credentials(); err != nil {
91100
cobra.CompErrorln("no credentials")
92101
return nil, cobra.ShellCompDirectiveError
@@ -139,15 +148,13 @@ func (opts *autoCompleteOpts) initStore(ctx context.Context) error {
139148
return err
140149
}
141150

142-
func (opts *autoCompleteOpts) parseFlags(cmd *cobra.Command) {
151+
func (opts *autoCompleteOpts) parseFlags(cmd *cobra.Command) error {
143152
profile := cmd.Flag(flag.Profile).Value.String()
144-
if profile != "" {
145-
config.SetName(profile)
146-
} else if profile = config.GetString(flag.Profile); profile != "" {
147-
config.SetName(profile)
148-
} else if availableProfiles := config.List(); len(availableProfiles) == 1 {
149-
config.SetName(availableProfiles[0])
153+
154+
if err := cli.InitProfile(profile); err != nil {
155+
return err
150156
}
157+
151158
if project := cmd.Flag(flag.ProjectID).Value.String(); project != "" {
152159
opts.ProjectID = project
153160
}
@@ -159,4 +166,6 @@ func (opts *autoCompleteOpts) parseFlags(cmd *cobra.Command) {
159166
if tier := cmd.Flag(flag.Tier).Value.String(); tier != "" {
160167
opts.tier = tier
161168
}
169+
170+
return nil
162171
}

internal/cli/atlas/processes/process_autocomplete.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package processes
1616

1717
import (
1818
"context"
19+
"fmt"
1920
"sort"
2021
"strings"
2122

@@ -35,7 +36,11 @@ type AutoCompleteOpts struct {
3536

3637
func (opts *AutoCompleteOpts) AutocompleteProcesses() cli.AutoFunc {
3738
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
38-
opts.parseFlags(cmd)
39+
if err := opts.parseFlags(cmd); err != nil {
40+
cobra.CompErrorln(fmt.Sprintf("failed to parse flags: %v", err))
41+
return nil, cobra.ShellCompDirectiveError
42+
}
43+
3944
if err := validate.Credentials(); err != nil {
4045
cobra.CompErrorln("no credentials")
4146
return nil, cobra.ShellCompDirectiveError
@@ -82,16 +87,16 @@ func (opts *AutoCompleteOpts) initStore(ctx context.Context) error {
8287
return err
8388
}
8489

85-
func (opts *AutoCompleteOpts) parseFlags(cmd *cobra.Command) {
90+
func (opts *AutoCompleteOpts) parseFlags(cmd *cobra.Command) error {
8691
profile := cmd.Flag(flag.Profile).Value.String()
87-
if profile != "" {
88-
config.SetName(profile)
89-
} else if profile = config.GetString(flag.Profile); profile != "" {
90-
config.SetName(profile)
91-
} else if availableProfiles := config.List(); len(availableProfiles) == 1 {
92-
config.SetName(availableProfiles[0])
92+
93+
if err := cli.InitProfile(profile); err != nil {
94+
return err
9395
}
96+
9497
if project := cmd.Flag(flag.ProjectID).Value.String(); project != "" {
9598
opts.ProjectID = project
9699
}
100+
101+
return nil
97102
}

internal/cli/config/delete.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ func (opts *DeleteOpts) Run() error {
3434
return nil
3535
}
3636

37-
config.SetName(opts.Entry)
37+
if err := config.SetName(opts.Entry); err != nil {
38+
return err
39+
}
40+
3841
if err := config.Delete(); err != nil {
3942
return err
4043
}

internal/cli/config/describe.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ func (opts *describeOpts) Run() error {
3838
if !config.Exists(opts.name) {
3939
return fmt.Errorf("you don't have a profile named '%s'", opts.name)
4040
}
41-
config.SetName(opts.name)
41+
42+
if err := config.SetName(opts.name); err != nil {
43+
return err
44+
}
45+
4246
return opts.Print(config.Map())
4347
}
4448

internal/cli/config/rename.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ func (opts *RenameOpts) Run() error {
4747
}
4848
}
4949

50-
config.SetName(opts.oldName)
50+
if err := config.SetName(opts.oldName); err != nil {
51+
return err
52+
}
53+
5154
if err := config.Rename(opts.newName); err != nil {
5255
return err
5356
}

internal/cli/profile.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2024 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 cli
16+
17+
import (
18+
"github.com/mongodb/mongodb-atlas-cli/internal/config"
19+
"github.com/mongodb/mongodb-atlas-cli/internal/flag"
20+
)
21+
22+
func InitProfile(profile string) error {
23+
if profile != "" {
24+
return config.SetName(profile)
25+
} else if profile = config.GetString(flag.Profile); profile != "" {
26+
return config.SetName(profile)
27+
} else if availableProfiles := config.List(); len(availableProfiles) == 1 {
28+
return config.SetName(availableProfiles[0])
29+
}
30+
31+
return nil
32+
}

internal/cli/root/atlas/builder.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,6 @@ func handleSignal() {
107107
}, os.Interrupt, syscall.SIGTERM)
108108
}
109109

110-
func initProfile(profile string) {
111-
if profile != "" {
112-
config.SetName(profile)
113-
} else if profile = config.GetString(flag.Profile); profile != "" {
114-
config.SetName(profile)
115-
} else if availableProfiles := config.List(); len(availableProfiles) == 1 {
116-
config.SetName(availableProfiles[0])
117-
}
118-
}
119-
120110
// Builder conditionally adds children commands as needed.
121111
func Builder() *cobra.Command {
122112
var (
@@ -144,7 +134,9 @@ Use the --help flag with any command for more info on that command.`,
144134
log.SetLevel(log.DebugLevel)
145135
}
146136

147-
initProfile(profile)
137+
if err := cli.InitProfile(profile); err != nil {
138+
return err
139+
}
148140

149141
telemetry.StartTrackingCommand(cmd, args)
150142

internal/config/profile.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,25 @@ func (p *Profile) Name() string {
241241
return p.name
242242
}
243243

244-
func SetName(name string) { Default().SetName(name) }
245-
func (p *Profile) SetName(name string) {
244+
var ErrProfileNameHasDots = errors.New("profile should not contain '.'")
245+
246+
func validateName(name string) error {
247+
if strings.Contains(name, ".") {
248+
return fmt.Errorf("%w: %q", ErrProfileNameHasDots, name)
249+
}
250+
251+
return nil
252+
}
253+
254+
func SetName(name string) error { return Default().SetName(name) }
255+
func (p *Profile) SetName(name string) error {
256+
if err := validateName(name); err != nil {
257+
return err
258+
}
259+
246260
p.name = strings.ToLower(name)
261+
262+
return nil
247263
}
248264

249265
func Set(name string, value interface{}) { Default().Set(name, value) }
@@ -623,6 +639,10 @@ func Filename() string {
623639
// Rename replaces the Profile to a new Profile name, overwriting any Profile that existed before.
624640
func Rename(newProfileName string) error { return Default().Rename(newProfileName) }
625641
func (p *Profile) Rename(newProfileName string) error {
642+
if err := validateName(newProfileName); err != nil {
643+
return err
644+
}
645+
626646
// Configuration needs to be deleted from toml, as viper doesn't support this yet.
627647
// FIXME :: change when https://github.com/spf13/viper/pull/519 is merged.
628648
configurationAfterDelete := viper.AllSettings()

internal/config/profile_integration_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func TestProfile_Get_Default(t *testing.T) {
117117

118118
func TestProfile_Get_NonDefault(t *testing.T) {
119119
profile := profileWithOneNonDefaultUser()
120-
profile.SetName(atlas)
120+
require.NoError(t, profile.SetName(atlas))
121121

122122
require.NoError(t, profile.LoadMongoCLIConfig(false))
123123

@@ -134,7 +134,7 @@ func TestProfile_Delete_NonDefault(t *testing.T) {
134134
profile := profileWithOneDefaultUserOneNonDefault()
135135
require.NoError(t, profile.LoadMongoCLIConfig(false))
136136

137-
profile.SetName(atlas)
137+
require.NoError(t, profile.SetName(atlas))
138138

139139
require.NoError(t, profile.Delete())
140140
require.NoError(t, profile.LoadMongoCLIConfig(false))
@@ -144,31 +144,31 @@ func TestProfile_Delete_NonDefault(t *testing.T) {
144144

145145
func TestProfile_Rename(t *testing.T) {
146146
profile := profileWithOneDefaultUserOneNonDefault()
147-
profile.SetName(DefaultProfile)
147+
require.NoError(t, profile.SetName(DefaultProfile))
148148

149149
require.NoError(t, profile.LoadMongoCLIConfig(false))
150150
defaultDescription := profile.Map()
151151
require.NoError(t, profile.Rename(newProfileName))
152152
require.NoError(t, profile.LoadMongoCLIConfig(false))
153-
profile.SetName(DefaultProfile)
153+
require.NoError(t, profile.SetName(DefaultProfile))
154154
a := assert.New(t)
155155
a.Empty(profile.Map())
156-
profile.SetName(newProfileName)
156+
require.NoError(t, profile.SetName(newProfileName))
157157
descriptionAfterRename := profile.Map()
158158
// after renaming, one Profile should exist
159159
a.Equal(defaultDescription, descriptionAfterRename, "descriptions should be equal after renaming")
160160
}
161161

162162
func TestProfile_Rename_OverwriteExisting(t *testing.T) {
163163
profile := profileWithOneDefaultUserOneNonDefault()
164-
profile.SetName(DefaultProfile)
164+
require.NoError(t, profile.SetName(DefaultProfile))
165165

166166
require.NoError(t, profile.LoadMongoCLIConfig(false))
167167
defaultDescription := profile.Map()
168168

169169
require.NoError(t, profile.Rename(atlas))
170170
require.NoError(t, profile.LoadMongoCLIConfig(false))
171-
profile.SetName(atlas)
171+
require.NoError(t, profile.SetName(atlas))
172172
descriptionAfterRename := profile.Map()
173173
// after renaming, one Profile should exist
174174
assert.Equal(t, defaultDescription, descriptionAfterRename, "descriptions should be equal after renaming")
@@ -178,7 +178,7 @@ func TestProfile_Set(t *testing.T) {
178178
profile := profileWithOneDefaultUserOneNonDefault()
179179
require.NoError(t, profile.LoadMongoCLIConfig(false))
180180

181-
profile.SetName(DefaultProfile)
181+
require.NoError(t, profile.SetName(DefaultProfile))
182182

183183
profile.Set(projectID, "newProjectId")
184184

0 commit comments

Comments
 (0)