diff --git a/cmd/minikube/cmd/status.go b/cmd/minikube/cmd/status.go index 2171661078..65f0fbf1db 100644 --- a/cmd/minikube/cmd/status.go +++ b/cmd/minikube/cmd/status.go @@ -17,7 +17,10 @@ limitations under the License. package cmd import ( + "encoding/json" + "fmt" "os" + "strings" "text/template" "github.com/docker/machine/libmachine/state" @@ -35,6 +38,15 @@ import ( ) var statusFormat string +var output string + +var KubeconfigStatus = struct { + Configured string + Misconfigured string +}{ + Configured: `Configured`, + Misconfigured: `Misconfigured`, +} // Status represents the status type Status struct { @@ -51,7 +63,7 @@ const ( defaultStatusFormat = `host: {{.Host}} kubelet: {{.Kubelet}} apiserver: {{.APIServer}} -kubectl: {{.Kubeconfig}} +kubeconfig: {{.Kubeconfig}} ` ) @@ -63,6 +75,11 @@ var statusCmd = &cobra.Command{ Exit status contains the status of minikube's VM, cluster and kubernetes encoded on it's bits in this order from right to left. Eg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for kubernetes NOK)`, Run: func(cmd *cobra.Command, args []string) { + + if output != "text" && statusFormat != defaultStatusFormat { + exit.UsageT("Cannot use both --output and --format options") + } + var returnCode = 0 api, err := machine.NewAPIClient() if err != nil { @@ -115,10 +132,9 @@ var statusCmd = &cobra.Command{ glog.Errorln("Error kubeconfig status:", err) } if ks { - kubeconfigSt = "Correctly Configured: pointing to minikube-vm at " + ip.String() + kubeconfigSt = KubeconfigStatus.Configured } else { - kubeconfigSt = "Misconfigured: pointing to stale minikube-vm." + - "\nTo fix the kubectl context, run minikube update-context" + kubeconfigSt = KubeconfigStatus.Misconfigured returnCode |= k8sNotRunningStatusFlag } } else { @@ -131,13 +147,14 @@ var statusCmd = &cobra.Command{ APIServer: apiserverSt, Kubeconfig: kubeconfigSt, } - tmpl, err := template.New("status").Parse(statusFormat) - if err != nil { - exit.WithError("Error creating status template", err) - } - err = tmpl.Execute(os.Stdout, status) - if err != nil { - exit.WithError("Error executing status template", err) + + switch strings.ToLower(output) { + case "text": + printStatusText(status) + case "json": + printStatusJSON(status) + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output)) } os.Exit(returnCode) @@ -145,7 +162,32 @@ var statusCmd = &cobra.Command{ } func init() { - statusCmd.Flags().StringVar(&statusFormat, "format", defaultStatusFormat, + statusCmd.Flags().StringVarP(&statusFormat, "format", "f", defaultStatusFormat, `Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ For the list accessible variables for the template, see the struct values here: https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status`) + statusCmd.Flags().StringVarP(&output, "output", "o", "text", + `minikube status --output OUTPUT. json, text`) +} + +var printStatusText = func(status Status) { + tmpl, err := template.New("status").Parse(statusFormat) + if err != nil { + exit.WithError("Error creating status template", err) + } + err = tmpl.Execute(os.Stdout, status) + if err != nil { + exit.WithError("Error executing status template", err) + } + if status.Kubeconfig == KubeconfigStatus.Misconfigured { + out.WarningT("Warning: Your kubectl is pointing to stale minikube-vm.\nTo fix the kubectl context, run `minikube update-context`") + } +} + +var printStatusJSON = func(status Status) { + + jsonString, err := json.Marshal(status) + if err != nil { + exit.WithError("Error converting status to json", err) + } + out.String(string(jsonString)) } diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 5d03ca370d..b3af56ec5c 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -84,6 +84,7 @@ func TestFunctional(t *testing.T) { {"ConfigCmd", validateConfigCmd}, {"DashboardCmd", validateDashboardCmd}, {"DNS", validateDNS}, + {"StatusCmd", validateStatusCmd}, {"LogsCmd", validateLogsCmd}, {"MountCmd", validateMountCmd}, {"ProfileCmd", validateProfileCmd}, @@ -175,6 +176,46 @@ func validateComponentHealth(ctx context.Context, t *testing.T, profile string) } } +func validateStatusCmd(ctx context.Context, t *testing.T, profile string) { + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + + // Custom format + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-f", "host:{{.Host}},kublet:{{.Kubelet}},apiserver:{{.APIServer}},kubeconfig:{{.Kubeconfig}}")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + match, _ := regexp.MatchString(`host:([A-z]+),kublet:([A-z]+),apiserver:([A-z]+),kubeconfig:([A-z]+)`, rr.Stdout.String()) + if !match { + t.Errorf("%s failed: %v. Output for custom format did not match", rr.Args, err) + } + + // Json output + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-o", "json")) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + var jsonObject map[string]interface{} + err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject) + if err != nil { + t.Errorf("%s failed: %v", rr.Args, err) + } + if _, ok := jsonObject["Host"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "Host") + } + if _, ok := jsonObject["Kubelet"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "Kubelet") + } + if _, ok := jsonObject["APIServer"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "APIServer") + } + if _, ok := jsonObject["Kubeconfig"]; !ok { + t.Errorf("%s failed: %v. Missing key %s in json object", rr.Args, err, "Kubeconfig") + } +} + // validateDashboardCmd asserts that the dashboard command works func validateDashboardCmd(ctx context.Context, t *testing.T, profile string) { args := []string{"dashboard", "--url", "-p", profile, "--alsologtostderr", "-v=1"}