Skip to content

Commit dc7a447

Browse files
authored
CLOUDP-61290: Validate base url (#124)
1 parent eb4f003 commit dc7a447

File tree

13 files changed

+536
-103
lines changed

13 files changed

+536
-103
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ addlicense:
5050
.PHONY: gen-mocks
5151
gen-mocks: ## Generate mocks
5252
@echo "==> Generating mocks"
53+
mockgen -source=internal/config/profile.go -destination=internal/mocks/mock_profile.go -package=mocks
5354
mockgen -source=internal/store/alert_configuration.go -destination=internal/mocks/mock_alert_configuration.go -package=mocks
5455
mockgen -source=internal/store/automation.go -destination=internal/mocks/mock_automation.go -package=mocks
5556
mockgen -source=internal/store/clusters.go -destination=internal/mocks/mock_clusters.go -package=mocks

cmd/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ import (
3030
var (
3131
rootCmd = &cobra.Command{
3232
Version: version.Version,
33-
Use: config.Name,
33+
Use: config.ToolName,
3434
Short: "CLI tool to manage your MongoDB Cloud",
35-
Long: fmt.Sprintf("Use %s command help for information on a specific command", config.Name),
35+
Long: fmt.Sprintf("Use %s command help for information on a specific command", config.ToolName),
3636
}
3737
)
3838

internal/cli/cli.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package cli
1616

1717
import (
18-
"encoding/hex"
1918
"errors"
2019
"fmt"
2120
"strconv"
@@ -25,6 +24,7 @@ import (
2524
atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas"
2625
"github.com/mongodb/mongocli/internal/config"
2726
"github.com/mongodb/mongocli/internal/prompts"
27+
"github.com/mongodb/mongocli/internal/validate"
2828
)
2929

3030
const (
@@ -37,14 +37,6 @@ type globalOpts struct {
3737
projectID string
3838
}
3939

40-
func validateObjectID(s string) error {
41-
b, err := hex.DecodeString(s)
42-
if err != nil || len(b) != 12 {
43-
return fmt.Errorf("the provided value '%s' is not a valid ObjectID", s)
44-
}
45-
return nil
46-
}
47-
4840
func deploymentStatus(baseURL, projectID string) string {
4941
return fmt.Sprintf("Changes are being applied, please check %s/v2/%s#deployment/topology for status\n", baseURL, projectID)
5042
}
@@ -67,7 +59,7 @@ func (opts *globalOpts) PreRunE(cbs ...cmdOpt) error {
6759
if opts.ProjectID() == "" {
6860
return errMissingProjectID
6961
}
70-
if err := validateObjectID(opts.ProjectID()); err != nil {
62+
if err := validate.ObjectID(opts.ProjectID()); err != nil {
7163
return err
7264
}
7365
for _, f := range cbs {

internal/cli/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"github.com/mongodb/mongocli/internal/description"
2121
"github.com/mongodb/mongocli/internal/flags"
2222
"github.com/mongodb/mongocli/internal/usage"
23-
"github.com/mongodb/mongocli/internal/validators"
23+
"github.com/mongodb/mongocli/internal/validate"
2424
"github.com/spf13/cobra"
2525
)
2626

@@ -66,7 +66,7 @@ func (opts *configOpts) Run() error {
6666
helpLink = "https://docs.opsmanager.mongodb.com/current/tutorial/configure-public-api-access/"
6767
}
6868

69-
var defaultQuestions = []*survey.Question{
69+
defaultQuestions := []*survey.Question{
7070
{
7171
Name: "publicAPIKey",
7272
Prompt: &survey.Input{
@@ -89,11 +89,11 @@ func (opts *configOpts) Run() error {
8989
{
9090
Name: "opsManagerURL",
9191
Prompt: &survey.Input{
92-
Message: "Ops Manager Base URL:",
92+
Message: "URL to Access Ops Manage:",
9393
Default: config.OpsManagerURL(),
94-
Help: "Ops Manager host URL",
94+
Help: "https://docs.opsmanager.mongodb.com/current/reference/config/ui-settings/#URL-to-Access-Ops-Manager",
9595
},
96-
Validate: validators.ValidURL,
96+
Validate: validate.URL,
9797
},
9898
}
9999
defaultQuestions = append(opsManagerQuestions, defaultQuestions...)
@@ -112,6 +112,7 @@ func ConfigBuilder() *cobra.Command {
112112
cmd := &cobra.Command{
113113
Use: "config",
114114
Short: description.ConfigDescription,
115+
Long: description.ConfigLongDescription,
115116
RunE: func(cmd *cobra.Command, args []string) error {
116117
return opts.Run()
117118
},

internal/cli/config_set.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,54 @@ package cli
1616

1717
import (
1818
"fmt"
19+
"strings"
1920

2021
"github.com/mongodb/mongocli/internal/config"
2122
"github.com/mongodb/mongocli/internal/description"
2223
"github.com/mongodb/mongocli/internal/search"
24+
"github.com/mongodb/mongocli/internal/validate"
2325
"github.com/spf13/cobra"
2426
)
2527

2628
type configSetOpts struct {
2729
globalOpts
28-
prop string
29-
val string
30+
prop string
31+
val string
32+
store config.ProfileSetSaver
3033
}
3134

3235
func (opts *configSetOpts) Run() error {
33-
config.Set(opts.prop, opts.val)
34-
if err := config.Save(); err != nil {
36+
if strings.HasSuffix(opts.prop, "_url") {
37+
if err := validate.URL(opts.val); err != nil {
38+
return err
39+
}
40+
} else if strings.HasSuffix(opts.prop, "_id") {
41+
if err := validate.ObjectID(opts.val); err != nil {
42+
return err
43+
}
44+
}
45+
opts.store.Set(opts.prop, opts.val)
46+
if err := opts.store.Save(); err != nil {
3547
return err
3648
}
37-
fmt.Printf("Updated prop '%s'\n", opts.prop)
49+
fmt.Printf("Updated property '%s'\n", opts.prop)
3850
return nil
3951
}
4052

4153
func ConfigSetBuilder() *cobra.Command {
42-
opts := &configSetOpts{}
54+
opts := &configSetOpts{
55+
store: config.Config(),
56+
}
4357
cmd := &cobra.Command{
44-
Use: "set [prop] [val]",
45-
Short: description.SetConfig,
58+
Use: "set [property] [value]",
59+
Short: description.ConfigSetDescription,
60+
Long: fmt.Sprintf(description.ConfigSetLongDescription, config.Properties()),
4661
Args: func(cmd *cobra.Command, args []string) error {
4762
if len(args) != 2 {
4863
return fmt.Errorf("accepts %d arg(s), received %d", 2, len(args))
4964
}
5065
if !search.StringInSlice(cmd.ValidArgs, args[0]) {
51-
return fmt.Errorf("invalid prop %q", args[0])
66+
return fmt.Errorf("invalid property: %q", args[0])
5267
}
5368
return nil
5469
},

internal/cli/config_set_test.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright 2020 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+
"testing"
19+
20+
"github.com/golang/mock/gomock"
21+
"github.com/mongodb/mongocli/internal/mocks"
22+
)
23+
24+
func TestConfigSet_Run(t *testing.T) {
25+
ctrl := gomock.NewController(t)
26+
mockStore := mocks.NewMockProfileSetSaver(ctrl)
27+
defer ctrl.Finish()
28+
29+
t.Run("valid prop", func(t *testing.T) {
30+
setOpts := &configSetOpts{
31+
store: mockStore,
32+
prop: "test",
33+
val: "something",
34+
}
35+
mockStore.
36+
EXPECT().
37+
Set(setOpts.prop, setOpts.val).
38+
Times(1)
39+
40+
mockStore.
41+
EXPECT().
42+
Save().Return(nil).
43+
Times(1)
44+
45+
err := setOpts.Run()
46+
47+
if err != nil {
48+
t.Fatalf("Run() unexpected error: %v\n", err)
49+
}
50+
})
51+
52+
t.Run("valid base_url", func(t *testing.T) {
53+
setOpts := &configSetOpts{
54+
store: mockStore,
55+
prop: "base_url",
56+
val: "http://test:9080/",
57+
}
58+
mockStore.
59+
EXPECT().
60+
Set(setOpts.prop, setOpts.val).
61+
Times(1)
62+
63+
mockStore.
64+
EXPECT().
65+
Save().Return(nil).
66+
Times(1)
67+
68+
err := setOpts.Run()
69+
70+
if err != nil {
71+
t.Fatalf("Run() unexpected error: %v\n", err)
72+
}
73+
})
74+
75+
t.Run("invalid base_url", func(t *testing.T) {
76+
setOpts := &configSetOpts{
77+
store: mockStore,
78+
prop: "base_url",
79+
}
80+
mockStore.
81+
EXPECT().
82+
Set(setOpts.prop, setOpts.val).
83+
Times(0)
84+
85+
mockStore.
86+
EXPECT().
87+
Save().Return(nil).
88+
Times(0)
89+
90+
err := setOpts.Run()
91+
92+
if err == nil {
93+
t.Fatal("Run() expected an error but got none\n")
94+
}
95+
})
96+
97+
t.Run("valid org_id", func(t *testing.T) {
98+
setOpts := &configSetOpts{
99+
store: mockStore,
100+
prop: "org_id",
101+
val: "5e9f088b4797476aa0a5d56a",
102+
}
103+
mockStore.
104+
EXPECT().
105+
Set(setOpts.prop, setOpts.val).
106+
Times(1)
107+
108+
mockStore.
109+
EXPECT().
110+
Save().Return(nil).
111+
Times(1)
112+
113+
err := setOpts.Run()
114+
115+
if err != nil {
116+
t.Fatalf("Run() unexpected error: %v\n", err)
117+
}
118+
})
119+
120+
t.Run("invalid org_id", func(t *testing.T) {
121+
setOpts := &configSetOpts{
122+
store: mockStore,
123+
prop: "org_id",
124+
val: "1",
125+
}
126+
mockStore.
127+
EXPECT().
128+
Set(setOpts.prop, setOpts.val).
129+
Times(0)
130+
131+
mockStore.
132+
EXPECT().
133+
Save().Return(nil).
134+
Times(0)
135+
136+
err := setOpts.Run()
137+
138+
if err == nil {
139+
t.Fatal("Run() expected an error but got none\n")
140+
}
141+
})
142+
143+
t.Run("valid project_id", func(t *testing.T) {
144+
setOpts := &configSetOpts{
145+
store: mockStore,
146+
prop: "project_id",
147+
val: "5e9f088b4797476aa0a5d56a",
148+
}
149+
mockStore.
150+
EXPECT().
151+
Set(setOpts.prop, setOpts.val).
152+
Times(1)
153+
154+
mockStore.
155+
EXPECT().
156+
Save().Return(nil).
157+
Times(1)
158+
159+
err := setOpts.Run()
160+
161+
if err != nil {
162+
t.Fatalf("Run() unexpected error: %v\n", err)
163+
}
164+
})
165+
166+
t.Run("invalid project_id", func(t *testing.T) {
167+
setOpts := &configSetOpts{
168+
store: mockStore,
169+
prop: "project_id",
170+
val: "1",
171+
}
172+
mockStore.
173+
EXPECT().
174+
Set(setOpts.prop, setOpts.val).
175+
Times(0)
176+
177+
mockStore.
178+
EXPECT().
179+
Save().Return(nil).
180+
Times(0)
181+
182+
err := setOpts.Run()
183+
184+
if err == nil {
185+
t.Fatal("Run() expected an error but got none\n")
186+
}
187+
})
188+
}

internal/config/const.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
package config
1616

1717
const (
18-
Name = "mongocli" // Name of the CLI
18+
ToolName = "mongocli" // ToolName of the CLI
1919
EnvPrefix = "mcli" // Prefix for ENV variables
2020
DefaultProfile = "default" // DefaultProfile default
2121
CloudService = "cloud" // CloudService setting when using Atlas API

0 commit comments

Comments
 (0)