Skip to content

CLOUDP-113344: Improve time of new version checker #1010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
4 changes: 3 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bradleyfalzon/ghinstallation/v2 v2.0.3/go.mod h1:tlgi+JWCXnKFx/Y4WtnDbZEINo31N5bcvnCoqieefmk=
github.com/briandowns/spinner v1.18.1 h1:yhQmQtM1zsqFsouh09Bk/jCjd50pC3EOGsh28gLVvwY=
github.com/briandowns/spinner v1.18.1/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down Expand Up @@ -138,6 +139,7 @@ github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncV
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down Expand Up @@ -188,9 +190,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v39 v39.0.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec=
github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down
13 changes: 3 additions & 10 deletions internal/cli/root/atlas/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package atlas

import (
"context"
"fmt"
"runtime"
"strings"
Expand Down Expand Up @@ -63,10 +62,6 @@ import (

const atlas = "atlas"

type BuilderOpts struct {
store latestrelease.Printer
}

// Builder conditionally adds children commands as needed.
func Builder(profile *string) *cobra.Command {
rootCmd := &cobra.Command{
Expand Down Expand Up @@ -105,11 +100,9 @@ func Builder(profile *string) *cobra.Command {
w := cmd.ErrOrStderr()

if !config.SkipUpdateCheck() && cli.IsTerminal(w) {
opts := &BuilderOpts{
store: latestrelease.NewPrinter(context.Background()),
}

_ = opts.store.PrintNewVersionAvailable(w, version.Version, config.ToolName, config.BinName())
printer := latestrelease.NewPrinter(w, config.ToolName, config.BinName())
checker := latestrelease.NewChecker(version.Version, config.ToolName, printer)
_ = checker.CheckAvailable()
}
},
}
Expand Down
12 changes: 3 additions & 9 deletions internal/cli/root/mongocli/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package mongocli

import (
"context"
"fmt"
"io"
"runtime"
Expand All @@ -36,10 +35,6 @@ import (
"github.com/spf13/cobra"
)

type BuilderOpts struct {
store latestrelease.Printer
}

// Builder conditionally adds children commands as needed.
// This is important in particular for Atlas as it dynamically sets flags for cluster creation and
// this can be slow to timeout on environments with limited internet access (Ops Manager).
Expand All @@ -61,11 +56,10 @@ func Builder(profile *string, argsWithoutProg []string) *cobra.Command {
if shouldSkipPrintNewVersion(w) {
return
}
opts := &BuilderOpts{
store: latestrelease.NewPrinter(context.Background()),
}

_ = opts.store.PrintNewVersionAvailable(w, version.Version, config.ToolName, config.BinName())
printer := latestrelease.NewPrinter(w, config.ToolName, config.BinName())
checker := latestrelease.NewChecker(version.Version, config.ToolName, printer)
_ = checker.CheckAvailable()
},
}
rootCmd.SetVersionTemplate(formattedVersion())
Expand Down
101 changes: 91 additions & 10 deletions internal/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,127 @@
package file

import (
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"strings"

"github.com/mongodb/mongocli/internal/config"
"github.com/mongodb/mongocli/internal/search"
"github.com/mongodb/mongocli/internal/version"
"github.com/spf13/afero"
"gopkg.in/yaml.v2"
)

var supportedExts = []string{"json", "yaml", "yml"}
const (
yamlName = "yaml"
jsonName = "json"
ymlName = "yml"
configPermission = 0700
filePermission = 0600
)

var supportedExts = []string{jsonName, yamlName, ymlName}

// configType gets the config type from a given file path.
func configType(filename string) (string, error) {
ext := filepath.Ext(filename)

if len(ext) <= 1 {
return "", fmt.Errorf("filename: %s requires valid extension", filename)
}

configType := ext[1:]
if !search.StringInSlice(supportedExts, configType) {
return "", fmt.Errorf("unsupported file type: %s", configType)
}
return configType, nil
}

// Load loads a given filename into the out interface.
// The file should be a valid json or yaml format.
func Load(fs afero.Fs, filename string, out interface{}) error {
if exists, err := afero.Exists(fs, filename); !exists || err != nil {
return fmt.Errorf("file not found: %s", filename)
}
ext := filepath.Ext(filename)
if len(ext) <= 1 {
return fmt.Errorf("filename: %s requires valid extension", filename)
}
configType := ext[1:]
if !search.StringInSlice(supportedExts, configType) {
return fmt.Errorf("unsupported file type: %s", configType)

configType, err := configType(filename)
if err != nil {
return err
}

file, err := afero.ReadFile(fs, filename)
if err != nil {
return err
}

switch configType {
case "yaml", "yml":
case yamlName, ymlName:
if err := yaml.Unmarshal(file, out); err != nil {
return err
}
case "json":
case jsonName:
if err := json.Unmarshal(file, out); err != nil {
return err
}
}

return nil
}

// Save saves a given data interface into a given file path
// The file should be a valid json or yaml format.
func Save(fs afero.Fs, filePath string, data interface{}) error {
var content []byte

configType, err := configType(filePath)
if err != nil {
return err
}

switch configType {
case yamlName, ymlName:
content, err = yaml.Marshal(data)
if err != nil {
return err
}
case jsonName:
content, err = json.Marshal(data)
if err != nil {
return err
}
}

err = fs.MkdirAll(filepath.Dir(filePath), configPermission)
if err != nil {
return err
}

err = afero.WriteFile(fs, filePath, content, filePermission)
return err
}

func Path(tool, fileName string) (string, error) {
var path bytes.Buffer
var home string
var err error

if tool == version.AtlasCLI {
home, err = config.AtlasCLIConfigHome()
} else {
home, err = config.MongoCLIConfigHome()
}
if err != nil {
return "", err
}

path.WriteString(home)
// Temporary until config home is changed
if !strings.Contains(home, tool) && tool == version.MongoCLI {
path.WriteString("/" + tool)
}

path.WriteString(fileName)
return path.String(), nil
}
81 changes: 70 additions & 11 deletions internal/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,58 +18,117 @@
package file_test

import (
"encoding/json"
"fmt"
"testing"

"github.com/mongodb/mongocli/internal/file"
"github.com/spf13/afero"
"gopkg.in/yaml.v2"
)

func TestLoad(t *testing.T) {
t.Run("file does not exists", func(t *testing.T) {
const (
noExtFileName = "test"
txtFileName = "test.txt"
jsonFileName = "test.json"
yamlFileName = "test.yaml"
unsupportedMsg = "unsupported file type: txt"
)

func TestFile(t *testing.T) {
t.Run("load file does not exists", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := "test.xml"
err := file.Load(appFS, filename, nil)
if err == nil || err.Error() != fmt.Sprintf("file not found: %s", filename) {
t.Errorf("Load() unexpected error: %v", err)
}
})
t.Run("file with no ext", func(t *testing.T) {
t.Run("load file with no ext", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := "test"
filename := noExtFileName
_ = afero.WriteFile(appFS, filename, []byte(""), 0600)
err := file.Load(appFS, filename, nil)
if err == nil || err.Error() != fmt.Sprintf("filename: %s requires valid extension", filename) {
t.Errorf("Load() unexpected error: %v", err)
}
})
t.Run("file with invalid ext", func(t *testing.T) {
t.Run("load file with invalid ext", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := "test.test"
filename := txtFileName
_ = afero.WriteFile(appFS, filename, []byte(""), 0600)
err := file.Load(appFS, filename, nil)
if err == nil || err.Error() != "unsupported file type: test" {
if err == nil || err.Error() != unsupportedMsg {
t.Errorf("Load() unexpected error: %v", err)
}
})
t.Run("valid json file", func(t *testing.T) {
t.Run("load valid json file", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := "test.json"
filename := jsonFileName
_ = afero.WriteFile(appFS, filename, []byte("{}"), 0600)
out := new(map[string]interface{})
err := file.Load(appFS, filename, out)
if err != nil {
t.Fatalf("Load() unexpected error: %v", err)
}
})
t.Run("valid yaml file", func(t *testing.T) {
t.Run("load valid yaml file", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := "test.yaml"
filename := yamlFileName
_ = afero.WriteFile(appFS, filename, []byte(""), 0600)
out := new(map[string]interface{})
err := file.Load(appFS, filename, out)
if err != nil {
t.Fatalf("Load() unexpected error: %v", err)
}
})
t.Run("save file with no ext", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := "test"
err := file.Save(appFS, filename, nil)
if err == nil || err.Error() != "filename: test requires valid extension" {
t.Errorf("Save() unexpected error: %v", err)
}
})
t.Run("save file with wrong ext", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := txtFileName
err := file.Save(appFS, filename, nil)
if err == nil || err.Error() != unsupportedMsg {
t.Errorf("Save() unexpected error: %v", err)
}
})
t.Run("save valid json file", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := jsonFileName

b := []byte(`{"Name":"MongoDB","Age":100,"Children":["mongocli", "atlascli"]}`)
j, _ := json.Marshal(b)

err := file.Save(appFS, filename, j)
if err != nil {
t.Fatalf("Save() unexpected error: %v", err)
}
})
t.Run("save valid yaml file", func(t *testing.T) {
appFS := afero.NewMemMapFs()
filename := yamlFileName

type Test struct {
name string
age int
}

tYaml := Test{
name: "MongoDB",
age: 100,
}

yamlData, _ := yaml.Marshal(&tYaml)

err := file.Save(appFS, filename, yamlData)
if err != nil {
t.Fatalf("Save() unexpected error: %v", err)
}
})
}
Loading