Skip to content
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

Add faster profile list command with -l or --light option. #10380

Merged
merged 1 commit into from
Feb 11, 2021
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
23 changes: 21 additions & 2 deletions cmd/minikube/cmd/config/profile_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
)

var output string
var isLight bool

var profileListCmd = &cobra.Command{
Use: "list",
Expand All @@ -58,8 +59,18 @@ var profileListCmd = &cobra.Command{
},
}

func listProfiles() (validProfiles, invalidProfiles []*config.Profile, err error) {
if isLight {
validProfiles, err = config.ListValidProfiles()
medyagh marked this conversation as resolved.
Show resolved Hide resolved
} else {
validProfiles, invalidProfiles, err = config.ListProfiles()
}

return validProfiles, invalidProfiles, err
}

func printProfilesTable() {
validProfiles, invalidProfiles, err := config.ListProfiles()
validProfiles, invalidProfiles, err := listProfiles()

if err != nil {
klog.Warningf("error loading profiles: %v", err)
Expand All @@ -75,6 +86,13 @@ func printProfilesTable() {
}

func updateProfilesStatus(profiles []*config.Profile) {
if isLight {
for _, p := range profiles {
p.Status = "Skipped"
}
return
}

api, err := machine.NewAPIClient()
if err != nil {
klog.Errorf("failed to get machine api client %v", err)
Expand Down Expand Up @@ -168,7 +186,7 @@ func warnInvalidProfiles(invalidProfiles []*config.Profile) {
}

func printProfilesJSON() {
validProfiles, invalidProfiles, err := config.ListProfiles()
validProfiles, invalidProfiles, err := listProfiles()

updateProfilesStatus(validProfiles)

Expand All @@ -195,5 +213,6 @@ func profilesOrDefault(profiles []*config.Profile) []*config.Profile {

func init() {
profileListCmd.Flags().StringVarP(&output, "output", "o", "table", "The output format. One of 'json', 'table'")
profileListCmd.Flags().BoolVarP(&isLight, "light", "l", false, "If true, returns list of profiles faster by skipping validating the status of the cluster.")
ProfileCmd.AddCommand(profileListCmd)
}
1 change: 1 addition & 0 deletions site/content/en/docs/commands/profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ minikube profile list [flags]
### Options

```
-l, --light If true, returns list of profiles faster by skipping validating the status of the cluster.
-o, --output string The output format. One of 'json', 'table' (default "table")
```

Expand Down
100 changes: 74 additions & 26 deletions test/integration/functional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,50 +768,98 @@ func validateProfileCmd(ctx context.Context, t *testing.T, profile string) {
})

t.Run("profile_list", func(t *testing.T) {
// helper function to run command then, return target profile line from table output.
extractrofileListFunc := func(rr *RunResult) string {
listLines := strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n")
for i := 3; i < (len(listLines) - 1); i++ {
profileLine := listLines[i]
if strings.Contains(profileLine, profile) {
return profileLine
}
}
return ""
}

// List profiles
start := time.Now()
rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list"))
elapsed := time.Since(start)
if err != nil {
t.Errorf("failed to list profiles: args %q : %v", rr.Command(), err)
}
t.Logf("Took %q to run %q", elapsed, rr.Command())

// Table output
listLines := strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n")
profileExists := false
for i := 3; i < (len(listLines) - 1); i++ {
profileLine := listLines[i]
if strings.Contains(profileLine, profile) {
profileExists = true
break
}
}
if !profileExists {
profileLine := extractrofileListFunc(rr)
if profileLine == "" {
t.Errorf("expected 'profile list' output to include %q but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
}

// List profiles with light option.
start = time.Now()
lrr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "-l"))
lightElapsed := time.Since(start)
if err != nil {
t.Errorf("failed to list profiles: args %q : %v", lrr.Command(), err)
}
t.Logf("Took %q to run %q", lightElapsed, lrr.Command())

profileLine = extractrofileListFunc(lrr)
if profileLine == "" || !strings.Contains(profileLine, "Skipped") {
t.Errorf("expected 'profile list' output to include %q with 'Skipped' status but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
}

if lightElapsed > 3*time.Second {
t.Errorf("expected running time of '%q' is less than 3 seconds. Took %q ", lrr.Command(), lightElapsed)
}
})

t.Run("profile_json_output", func(t *testing.T) {
// Json output
rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
// helper function to run command then, return target profile object from json output.
extractProfileObjFunc := func(rr *RunResult) *config.Profile {
daehyeok marked this conversation as resolved.
Show resolved Hide resolved
var jsonObject map[string][]config.Profile
err := json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
if err != nil {
t.Errorf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
return nil
}

for _, profileObject := range jsonObject["valid"] {
if profileObject.Name == profile {
return &profileObject
}
}
return nil
}

start := time.Now()
rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "-o", "json"))
elapsed := time.Since(start)
daehyeok marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
t.Errorf("failed to list profiles with json format. args %q: %v", rr.Command(), err)
}
var jsonObject map[string][]map[string]interface{}
err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
if err != nil {
t.Errorf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
t.Logf("Took %q to run %q", elapsed, rr.Command())

pr := extractProfileObjFunc(rr)
if pr == nil {
t.Errorf("expected the json of 'profile list' to include %q but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
}
validProfiles := jsonObject["valid"]
profileExists := false
for _, profileObject := range validProfiles {
if profileObject["Name"] == profile {
profileExists = true
break
}

start = time.Now()
lrr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "-o", "json", "--light"))
lightElapsed := time.Since(start)
daehyeok marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
t.Errorf("failed to list profiles with json format. args %q: %v", lrr.Command(), err)
}
if !profileExists {
t.Errorf("expected the json of 'profile list' to include %q but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
t.Logf("Took %q to run %q", lightElapsed, lrr.Command())

pr = extractProfileObjFunc(lrr)
if pr == nil || pr.Status != "Skipped" {
t.Errorf("expected the json of 'profile list' to include 'Skipped' status for %q but got *%q*. args: %q", profile, lrr.Stdout.String(), lrr.Command())
}

if lightElapsed > 3*time.Second {
t.Errorf("expected running time of '%q' is less than 3 seconds. Took %q ", lrr.Command(), lightElapsed)
}
})
}

Expand Down