Merge pull request #5611 from woodcockjosh/json-output-for-status

Add json output for status
pull/5692/head
Thomas Strömberg 2019-10-21 18:37:34 -07:00 committed by GitHub
commit fbc1630fc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 12 deletions

View File

@ -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))
}

View File

@ -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"}