Skip to content

Commit

Permalink
Merge pull request #15831 from OmSaran/main
Browse files Browse the repository at this point in the history
Add ability to get json for "minikube service list"
  • Loading branch information
spowelljr committed Feb 22, 2023
2 parents f033017 + c721b05 commit 6cc5ddc
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 14 deletions.
62 changes: 48 additions & 14 deletions cmd/minikube/cmd/service_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ limitations under the License.
package cmd

import (
"encoding/json"
"fmt"
"os"
"runtime"
"strings"

"github.com/spf13/cobra"
core "k8s.io/api/core/v1"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
Expand All @@ -32,6 +35,7 @@ import (
)

var serviceListNamespace string
var profileOutput string

// serviceListCmd represents the service list command
var serviceListCmd = &cobra.Command{
Expand All @@ -40,6 +44,7 @@ var serviceListCmd = &cobra.Command{
Long: `Lists the URLs for the services in your local cluster`,
Run: func(cmd *cobra.Command, args []string) {
co := mustload.Healthy(ClusterFlagValue())
output := strings.ToLower(profileOutput)

serviceURLs, err := service.GetServiceURLs(co.API, co.Config.Name, serviceListNamespace, serviceURLTemplate)
if err != nil {
Expand All @@ -48,28 +53,57 @@ var serviceListCmd = &cobra.Command{
os.Exit(reason.ExSvcUnavailable)
}

var data [][]string
for _, serviceURL := range serviceURLs {
if len(serviceURL.URLs) == 0 {
data = append(data, []string{serviceURL.Namespace, serviceURL.Name, "No node port"})
} else {
servicePortNames := strings.Join(serviceURL.PortNames, "\n")
serviceURLs := strings.Join(serviceURL.URLs, "\n")
switch output {
case "table":
printServicesTable(serviceURLs, co)
case "json":
printServicesJSON(serviceURLs, co)
default:
exit.Message(reason.Usage, fmt.Sprintf("invalid output format: %s. Valid values: 'table', 'json'", output))
}
},
}

// if we are running Docker on OSX we empty the internal service URLs
if runtime.GOOS == "darwin" && co.Config.Driver == oci.Docker {
serviceURLs = ""
}
func printServicesTable(serviceURLs service.URLs, co mustload.ClusterController) {
var data [][]string
for _, serviceURL := range serviceURLs {
if len(serviceURL.URLs) == 0 {
data = append(data, []string{serviceURL.Namespace, serviceURL.Name, "No node port"})
} else {
servicePortNames := strings.Join(serviceURL.PortNames, "\n")
serviceURLs := strings.Join(serviceURL.URLs, "\n")

data = append(data, []string{serviceURL.Namespace, serviceURL.Name, servicePortNames, serviceURLs})
// if we are running Docker on OSX we empty the internal service URLs
if runtime.GOOS == "darwin" && co.Config.Driver == oci.Docker {
serviceURLs = ""
}

data = append(data, []string{serviceURL.Namespace, serviceURL.Name, servicePortNames, serviceURLs})
}
}

service.PrintServiceList(os.Stdout, data)
},
service.PrintServiceList(os.Stdout, data)
}

func printServicesJSON(serviceURLs service.URLs, co mustload.ClusterController) {
processedServiceURLs := serviceURLs

if runtime.GOOS == "darwin" && co.Config.Driver == oci.Docker {
// To ensure we don't modify the original serviceURLs
processedServiceURLs = make(service.URLs, len(serviceURLs))
copy(processedServiceURLs, serviceURLs)

for idx := range processedServiceURLs {
processedServiceURLs[idx].URLs = make([]string, 0)
}
}

jsonString, _ := json.Marshal(processedServiceURLs)
os.Stdout.Write(jsonString)
}

func init() {
serviceListCmd.Flags().StringVarP(&profileOutput, "output", "o", "table", "The output format. One of 'json', 'table'")
serviceListCmd.Flags().StringVarP(&serviceListNamespace, "namespace", "n", core.NamespaceAll, "The services namespace")
serviceCmd.AddCommand(serviceListCmd)
}
1 change: 1 addition & 0 deletions site/content/en/docs/commands/service.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ minikube service list [flags]

```
-n, --namespace string The services namespace
-o, --output string The output format. One of 'json', 'table' (default "table")
```

### Options inherited from parent commands
Expand Down
5 changes: 5 additions & 0 deletions site/content/en/docs/contrib/tests.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ Steps:
- Run `minikube service` with `--url --format={{.IP}}` to make sure the IP address of the service is printed
- Run `minikube service` with a regular `--url` to make sure the HTTP endpoint URL of the service is printed

#### validateServiceCmdJSON

Steps:
- Run `minikube service list -o JSON` and make sure the services are correctly listed as JSON output

#### validateServiceCmdConnect

Steps:
Expand Down
38 changes: 38 additions & 0 deletions test/integration/functional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"k8s.io/minikube/pkg/minikube/detect"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/util/retry"

"github.com/blang/semver/v4"
Expand Down Expand Up @@ -1454,6 +1455,8 @@ func validateServiceCmd(ctx context.Context, t *testing.T, profile string) {
t.Errorf("expected 'service list' to contain *hello-node* but got -%q-", rr.Stdout.String())
}

validateServiceCmdJSON(ctx, t, profile)

// docs: Run `minikube service` with `--https --url` to make sure the HTTPS endpoint URL of the service is printed
cmdContext := exec.CommandContext(ctx, Target(), "-p", profile, "service", "--namespace=default", "--https", "--url", "hello-node")
if NeedsPortForward() {
Expand Down Expand Up @@ -1520,6 +1523,41 @@ func validateServiceCmd(ctx context.Context, t *testing.T, profile string) {
}
}

func validateServiceCmdJSON(ctx context.Context, t *testing.T, profile string) {
// docs: Run `minikube service list -o JSON` and make sure the services are correctly listed as JSON output
t.Run("ServiceJSONOutput", func(t *testing.T) {
targetSvcName := "hello-node"
// helper function to run command then, return target service object from json output.
extractServiceObjFunc := func(rr *RunResult) *service.SvcURL {
var jsonObjects service.URLs
err := json.Unmarshal(rr.Stdout.Bytes(), &jsonObjects)
if err != nil {
t.Fatalf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
}

for _, svc := range jsonObjects {
if svc.Name == targetSvcName {
return &svc
}
}
return nil
}

start := time.Now()
rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "list", "-o", "json"))
elapsed := time.Since(start)
if err != nil {
t.Fatalf("failed to list services with json format. args %q: %v", rr.Command(), err)
}
t.Logf("Took %q to run %q", elapsed, rr.Command())

pr := extractServiceObjFunc(rr)
if pr == nil {
t.Errorf("expected the json of 'service list' to include %q but got *%q*. args: %q", targetSvcName, rr.Stdout.String(), rr.Command())
}
})
}

func validateServiceCmdConnect(ctx context.Context, t *testing.T, profile string) {
defer PostMortemLogs(t, profile)

Expand Down

0 comments on commit 6cc5ddc

Please sign in to comment.