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

mongodb / mongodb-atlas-cli / 22219697489

20 Feb 2026 10:06AM UTC coverage: 64.114% (+0.005%) from 64.109%
22219697489

push

github

GitHub
chore(go): bump go version (#4460)

25429 of 39662 relevant lines covered (64.11%)

0.81 hits per line

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

81.18
/internal/plugin/plugin.go
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 plugin
16

17
import (
18
        "context"
19
        "errors"
20
        "fmt"
21
        "os"
22
        "os/exec"
23
        "path"
24
        "strings"
25

26
        "github.com/Masterminds/semver/v3"
27
        "github.com/mongodb/atlas-cli-core/config"
28
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/log"
29
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/set"
30
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/telemetry"
31
        "github.com/spf13/cobra"
32
)
33

34
var (
35
        errCreateDefaultPluginDir = errors.New("failed to create default plugin directory")
36
        minimumPluginVersions     = map[Github]string{
37
                {Owner: "mongodb", Name: "atlas-cli-plugin-kubernetes"}: "v1.1.7",
38
                {Owner: "mongodb", Name: "atlas-cli-plugin-gsa"}:        "v0.0.2", // TODO: ensure this version is correct version after work in CLOUDP-333246
39
        }
40
)
41

42
const (
43
        PluginSourceType = "plugin"
44
        sourceType       = "sourceType"
45
        sourcePluginName = "sourcePluginName"
46
)
47

48
func IsPluginCmd(cmd *cobra.Command) bool {
1✔
49
        if cmdSourceType, ok := cmd.Annotations[sourceType]; ok && cmdSourceType == PluginSourceType {
2✔
50
                return true
1✔
51
        }
1✔
52
        return false
1✔
53
}
54

55
func GetPluginWithName(name string, existingCommandsSet set.Set[string], onlySearchValidPlugins bool) (*Plugin, error) {
1✔
56
        var plugins []*Plugin
1✔
57
        if onlySearchValidPlugins {
1✔
58
                plugins = GetAllPluginsValidated(existingCommandsSet).GetValidPlugins()
×
59
        } else {
1✔
60
                plugins = getAllPlugins()
1✔
61
        }
1✔
62

63
        for _, plugin := range plugins {
2✔
64
                if plugin.Name == name {
2✔
65
                        return plugin, nil
1✔
66
                }
1✔
67
        }
68

69
        return nil, fmt.Errorf("could not find plugin %s", name)
×
70
}
71

72
func getAllPlugins() []*Plugin {
1✔
73
        // Load manifests from plugin directories
1✔
74
        manifests := loadManifestsFromPluginDirectories()
1✔
75

1✔
76
        // Convert manifests to plugins
1✔
77
        plugins := convertManifestsToPlugins(manifests)
1✔
78

1✔
79
        return plugins
1✔
80
}
1✔
81

82
type ValidatedPlugins struct {
83
        ValidPlugins                     []*Plugin
84
        PluginsWithDuplicateManifestName []*Plugin
85
        PluginsWithDuplicateCommands     []*Plugin
86
}
87

88
func (v *ValidatedPlugins) GetValidPlugins() []*Plugin {
1✔
89
        return v.ValidPlugins
1✔
90
}
1✔
91

92
func (v *ValidatedPlugins) GetValidAndInvalidPlugins() []*Plugin {
1✔
93
        return append(append(v.ValidPlugins, v.PluginsWithDuplicateManifestName...), v.PluginsWithDuplicateCommands...)
1✔
94
}
1✔
95

96
func GetAllPluginsValidated(existingCommandsSet set.Set[string]) *ValidatedPlugins {
1✔
97
        // Load manifests from plugin directories
1✔
98
        manifests := loadManifestsFromPluginDirectories()
1✔
99

1✔
100
        // Remove manifests with duplicate names
1✔
101
        manifests, duplicateManifestNames := removeManifestsWithDuplicateNames(manifests)
1✔
102
        duplicateManifests := make([]*Manifest, 0, len(duplicateManifestNames))
1✔
103
        for _, duplicate := range duplicateManifestNames {
1✔
104
                duplicateManifests = append(duplicateManifests, duplicate.Manifest)
×
105
                logPluginWarning(`could not load plugin "%s" because there are multiple plugins with that name`, duplicate.DuplicateName)
×
106
        }
×
107

108
        // Remove manifests that contain already existing commands
109
        manifests, duplicateManifest := getUniqueManifests(manifests, existingCommandsSet)
1✔
110
        duplicateCommands := make([]*Manifest, 0, len(duplicateManifest))
1✔
111
        for _, manifest := range duplicateManifest {
1✔
112
                duplicateCommands = append(duplicateCommands, manifest)
×
113
                logPluginWarning(`could not load plugin "%s" because it contains a command that already exists in the AtlasCLI or another plugin`, manifest.Name)
×
114
        }
×
115

116
        // Convert manifests to validated plugins
117
        return &ValidatedPlugins{
1✔
118
                ValidPlugins:                     convertManifestsToPlugins(manifests),
1✔
119
                PluginsWithDuplicateManifestName: convertManifestsToPlugins(duplicateManifests),
1✔
120
                PluginsWithDuplicateCommands:     convertManifestsToPlugins(duplicateCommands),
1✔
121
        }
1✔
122
}
123

124
func GetDefaultPluginDirectory() (string, error) {
1✔
125
        configHome, err := config.CLIConfigHome()
1✔
126

1✔
127
        if err != nil {
1✔
128
                return "", fmt.Errorf("failed to retrieve CLI config home: %w", err)
×
129
        }
×
130

131
        pluginDirectoryPath := path.Join(configHome, "plugins")
1✔
132

1✔
133
        err = os.MkdirAll(pluginDirectoryPath, os.ModePerm)
1✔
134
        if err != nil {
1✔
135
                return "", errCreateDefaultPluginDir
×
136
        }
×
137

138
        return pluginDirectoryPath, nil
1✔
139
}
140

141
type Command struct {
142
        Name        string
143
        Description string
144
        Aliases     []string
145
}
146

147
type Github struct {
148
        Owner string
149
        Name  string
150
}
151

152
func (g *Github) Equals(owner string, name string) bool {
1✔
153
        if g.Owner == owner && g.Name == name {
2✔
154
                return true
1✔
155
        }
1✔
156

157
        return false
×
158
}
159

160
type Plugin struct {
161
        Name                string
162
        Description         string
163
        PluginDirectoryPath string
164
        BinaryName          string
165
        Version             *semver.Version
166
        Commands            []*Command
167
        Github              *Github
168
}
169

170
func (p *Plugin) Run(cmd *cobra.Command, args []string) error {
1✔
171
        if err := ValidateVersion(*p.Github, p.Version); err != nil {
1✔
172
                return err
×
173
        }
×
174

175
        p.setTelemetry()
1✔
176

1✔
177
        binaryPath := path.Join(p.PluginDirectoryPath, p.BinaryName)
1✔
178
        execCmd := exec.CommandContext(context.Background(), binaryPath, append([]string{cmd.Use}, args...)...)
1✔
179
        execCmd.Stdin = cmd.InOrStdin()
1✔
180
        execCmd.Stdout = cmd.OutOrStdout()
1✔
181
        execCmd.Stderr = cmd.OutOrStderr()
1✔
182
        execCmd.Env = os.Environ()
1✔
183
        if err := execCmd.Run(); err != nil {
1✔
184
                var exitErr *exec.ExitError
×
185
                if errors.As(err, &exitErr) {
×
186
                        cmd.SilenceErrors = true
×
187
                        _, _ = log.Debugf("Silenced error: %v", exitErr)
×
188
                }
×
189
                return err
×
190
        }
191
        return nil
1✔
192
}
193

194
func (p *Plugin) Uninstall() error {
1✔
195
        return os.RemoveAll(p.PluginDirectoryPath)
1✔
196
}
1✔
197

198
func (p *Plugin) HasGithub() bool {
1✔
199
        return p.Github != nil && p.Github.Name != "" && p.Github.Owner != ""
1✔
200
}
1✔
201

202
func (p *Plugin) GetCobraCommands() []*cobra.Command {
2✔
203
        commands := make([]*cobra.Command, 0, len(p.Commands))
2✔
204

2✔
205
        for _, pluginCmd := range p.Commands {
4✔
206
                command := &cobra.Command{
2✔
207
                        Use:   pluginCmd.Name,
2✔
208
                        Short: pluginCmd.Description,
2✔
209
                        Annotations: map[string]string{
2✔
210
                                sourceType:       PluginSourceType,
2✔
211
                                sourcePluginName: p.Name,
2✔
212
                        },
2✔
213
                        RunE:    p.Run,
2✔
214
                        Aliases: pluginCmd.Aliases,
2✔
215
                }
2✔
216

2✔
217
                // Disable the default cobra help function.
2✔
218
                // Instead redirect help to the plugin.
2✔
219
                // Example: atlas example-plugin --help -> [example-binary] example-plugin --help
2✔
220
                command.SetHelpFunc(func(cmd *cobra.Command, args []string) {
2✔
221
                        // args contains all arguments + the name of the command
×
222
                        // we don't need the name of the subcommand
×
223
                        if err := p.Run(cmd, args[1:]); err != nil {
×
224
                                _, _ = log.Warningf("failed to generate help for plugin command '%v': %v", args[0], err)
×
225
                        }
×
226
                })
227

228
                command.DisableFlagParsing = true
2✔
229

2✔
230
                commands = append(commands, command)
2✔
231
        }
232

233
        return commands
2✔
234
}
235

236
func convertManifestsToPlugins(manifests []*Manifest) []*Plugin {
1✔
237
        plugins := make([]*Plugin, 0, len(manifests))
1✔
238
        for _, manifest := range manifests {
2✔
239
                plugin, err := createPluginFromManifest(manifest)
1✔
240
                if err != nil {
1✔
241
                        logPluginWarning(err.Error())
×
242
                        continue
×
243
                }
244
                plugins = append(plugins, plugin)
1✔
245
        }
246

247
        return plugins
1✔
248
}
249

250
func createPluginFromManifest(manifest *Manifest) (*Plugin, error) {
2✔
251
        version, err := semver.NewVersion(manifest.Version)
2✔
252
        if err != nil {
2✔
253
                return nil, fmt.Errorf("invalid version in manifest file %s", manifest.Name)
×
254
        }
×
255

256
        plugin := Plugin{
2✔
257
                Name:                manifest.Name,
2✔
258
                Description:         manifest.Description,
2✔
259
                PluginDirectoryPath: manifest.PluginDirectoryPath,
2✔
260
                BinaryName:          manifest.Binary,
2✔
261
                Version:             version,
2✔
262
                Commands:            make([]*Command, 0, len(manifest.Commands)),
2✔
263
        }
2✔
264

2✔
265
        if manifest.Github != nil {
4✔
266
                plugin.Github = &Github{
2✔
267
                        Owner: strings.TrimSuffix(manifest.Github.Owner, "/"),
2✔
268
                        Name:  strings.TrimSuffix(manifest.Github.Name, "/"),
2✔
269
                }
2✔
270
        }
2✔
271

272
        for cmdName, value := range manifest.Commands {
4✔
273
                plugin.Commands = append(plugin.Commands, &Command{Name: cmdName, Description: value.Description, Aliases: value.Aliases})
2✔
274
        }
2✔
275

276
        return &plugin, nil
2✔
277
}
278

279
func logPluginWarning(message string, args ...any) {
×
280
        _, _ = log.Warningf(fmt.Sprintf("-- plugin warning: %s\n", message), args...)
×
281
}
×
282

283
func (p *Plugin) setTelemetry() {
1✔
284
        info := telemetry.PluginExecutionInfo{
1✔
285
                Version: p.Version,
1✔
286
        }
1✔
287

1✔
288
        if p.Github != nil {
2✔
289
                info.GithubOwner = &p.Github.Owner
1✔
290
                info.GithubRepository = &p.Github.Name
1✔
291
        }
1✔
292

293
        telemetry.AppendOption(telemetry.WithPluginExecutionInfo(info))
1✔
294
}
295

296
// ValidateVersion validates the version of a plugin against the minimum required version.
297
// If a plugin is not listed in the minimumPluginVersions map, it is considered valid.
298
func ValidateVersion(gh Github, version *semver.Version) error {
2✔
299
        minVersionStr, exists := minimumPluginVersions[gh]
2✔
300
        if !exists {
4✔
301
                return nil // No version requirement for this plugin
2✔
302
        }
2✔
303

304
        minVersion, err := semver.NewVersion(minVersionStr)
2✔
305
        if err != nil {
2✔
306
                return err
×
307
        }
×
308

309
        if version.LessThan(minVersion) {
3✔
310
                return fmt.Errorf("plugin %s/%s version v%s is below minimum required version %s for this version of AtlasCLI.\nPlease update the plugin using 'atlas plugin update %s'",
1✔
311
                        gh.Owner, gh.Name, version.String(), minVersionStr, gh.Name)
1✔
312
        }
1✔
313

314
        return nil
2✔
315
}
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