mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #26 from brendandburns/ux
Extend the CLI output to allow JSON, YAML and Human Readable output
This commit is contained in:
		@@ -40,6 +40,8 @@ var (
 | 
				
			|||||||
	portSpec     = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...")
 | 
						portSpec     = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...")
 | 
				
			||||||
	servicePort  = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'")
 | 
						servicePort  = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'")
 | 
				
			||||||
	authConfig   = flag.String("auth", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file.  If missing, prompt the user")
 | 
						authConfig   = flag.String("auth", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file.  If missing, prompt the user")
 | 
				
			||||||
 | 
						json         = flag.Bool("json", false, "If true, print raw JSON for responses")
 | 
				
			||||||
 | 
						yaml         = flag.Bool("yaml", false, "If true, print raw YAML for responses")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func usage() {
 | 
					func usage() {
 | 
				
			||||||
@@ -63,6 +65,15 @@ func main() {
 | 
				
			|||||||
	var request *http.Request
 | 
						var request *http.Request
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var printer cloudcfg.ResourcePrinter
 | 
				
			||||||
 | 
						if *json {
 | 
				
			||||||
 | 
							printer = &cloudcfg.IdentityPrinter{}
 | 
				
			||||||
 | 
						} else if *yaml {
 | 
				
			||||||
 | 
							printer = &cloudcfg.YAMLPrinter{}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							printer = &cloudcfg.HumanReadablePrinter{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auth, err := cloudcfg.LoadAuthInfo(*authConfig)
 | 
						auth, err := cloudcfg.LoadAuthInfo(*authConfig)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("Error loading auth: %#v", err)
 | 
							log.Fatalf("Error loading auth: %#v", err)
 | 
				
			||||||
@@ -124,5 +135,9 @@ func main() {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("Error: %#v", err)
 | 
							log.Fatalf("Error: %#v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fmt.Println(body)
 | 
						err = printer.Print(body, os.Stdout)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("Failed to print: %#v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Print("\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										212
									
								
								pkg/cloudcfg/resource_printer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								pkg/cloudcfg/resource_printer.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,212 @@
 | 
				
			|||||||
 | 
					package cloudcfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"text/tabwriter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"gopkg.in/v1/yaml"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ResourcePrinter is an interface that knows how to print API resources
 | 
				
			||||||
 | 
					type ResourcePrinter interface {
 | 
				
			||||||
 | 
						// Print receives an arbitrary JSON body, formats it and prints it to a writer
 | 
				
			||||||
 | 
						Print(string, io.Writer) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Identity printer simply copies the body out to the output stream
 | 
				
			||||||
 | 
					type IdentityPrinter struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *IdentityPrinter) Print(data string, w io.Writer) error {
 | 
				
			||||||
 | 
						_, err := fmt.Fprint(w, data)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// YAMLPrinter parses JSON, and re-formats as YAML
 | 
				
			||||||
 | 
					type YAMLPrinter struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (y *YAMLPrinter) Print(data string, w io.Writer) error {
 | 
				
			||||||
 | 
						var obj interface{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal([]byte(data), &obj); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						output, err := yaml.Marshal(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = fmt.Fprint(w, string(output))
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HumanReadablePrinter attempts to provide more elegant output
 | 
				
			||||||
 | 
					type HumanReadablePrinter struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var podColumns = []string{"Name", "Image(s)", "Host", "Labels"}
 | 
				
			||||||
 | 
					var replicationControllerColumns = []string{"Name", "Image(s)", "Label Query", "Replicas"}
 | 
				
			||||||
 | 
					var serviceColumns = []string{"Name", "Label Query", "Port"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) unknown(data string, w io.Writer) error {
 | 
				
			||||||
 | 
						_, err := fmt.Fprintf(w, "Unknown object: %s", data)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) error {
 | 
				
			||||||
 | 
						if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var lines []string
 | 
				
			||||||
 | 
						for _, _ = range columnNames {
 | 
				
			||||||
 | 
							lines = append(lines, "----------")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err := fmt.Fprintf(w, "%s\n", strings.Join(lines, "\t"))
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) makeImageList(manifest api.ContainerManifest) string {
 | 
				
			||||||
 | 
						var images []string
 | 
				
			||||||
 | 
						for _, container := range manifest.Containers {
 | 
				
			||||||
 | 
							images = append(images, container.Image)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.Join(images, ",")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) makeLabelsList(labels map[string]string) string {
 | 
				
			||||||
 | 
						var vals []string
 | 
				
			||||||
 | 
						for key, value := range labels {
 | 
				
			||||||
 | 
							vals = append(vals, fmt.Sprintf("%s=%s", key, value))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.Join(vals, ",")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printPod(pod api.Pod, w io.Writer) error {
 | 
				
			||||||
 | 
						_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n",
 | 
				
			||||||
 | 
							pod.ID, h.makeImageList(pod.DesiredState.Manifest), pod.CurrentState.Host, h.makeLabelsList(pod.Labels))
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printPodList(podList api.PodList, w io.Writer) error {
 | 
				
			||||||
 | 
						for _, pod := range podList.Items {
 | 
				
			||||||
 | 
							if err := h.printPod(pod, w); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printReplicationController(ctrl api.ReplicationController, w io.Writer) error {
 | 
				
			||||||
 | 
						_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n",
 | 
				
			||||||
 | 
							ctrl.ID, h.makeImageList(ctrl.DesiredState.PodTemplate.DesiredState.Manifest), h.makeLabelsList(ctrl.DesiredState.ReplicasInSet), ctrl.DesiredState.Replicas)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printReplicationControllerList(list api.ReplicationControllerList, w io.Writer) error {
 | 
				
			||||||
 | 
						for _, ctrl := range list.Items {
 | 
				
			||||||
 | 
							if err := h.printReplicationController(ctrl, w); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printService(svc api.Service, w io.Writer) error {
 | 
				
			||||||
 | 
						_, err := fmt.Fprintf(w, "%s\t%s\t%d\n", svc.ID, h.makeLabelsList(svc.Labels), svc.Port)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) printServiceList(list api.ServiceList, w io.Writer) error {
 | 
				
			||||||
 | 
						for _, svc := range list.Items {
 | 
				
			||||||
 | 
							if err := h.printService(svc, w); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO replace this with something that returns a concrete printer object, rather than
 | 
				
			||||||
 | 
					//  having the secondary switch below.
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) extractObject(data, kind string) (interface{}, error) {
 | 
				
			||||||
 | 
						// TODO: I think this can be replaced with some reflection and a map[string]type
 | 
				
			||||||
 | 
						switch kind {
 | 
				
			||||||
 | 
						case "cluster#pod":
 | 
				
			||||||
 | 
							var obj api.Pod
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(data), &obj); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return obj, nil
 | 
				
			||||||
 | 
						case "cluster#podList":
 | 
				
			||||||
 | 
							var list api.PodList
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(data), &list); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return list, nil
 | 
				
			||||||
 | 
						case "cluster#replicationController":
 | 
				
			||||||
 | 
							var ctrl api.ReplicationController
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(data), &ctrl); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ctrl, nil
 | 
				
			||||||
 | 
						case "cluster#replicationControllerList":
 | 
				
			||||||
 | 
							var list api.ReplicationControllerList
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(data), &list); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return list, nil
 | 
				
			||||||
 | 
						case "cluster#service":
 | 
				
			||||||
 | 
							var ctrl api.Service
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(data), &ctrl); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ctrl, nil
 | 
				
			||||||
 | 
						case "cluster#serviceList":
 | 
				
			||||||
 | 
							var list api.ServiceList
 | 
				
			||||||
 | 
							if err := json.Unmarshal([]byte(data), &list); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return list, nil
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("Unknown kind: %s", kind)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *HumanReadablePrinter) Print(data string, output io.Writer) error {
 | 
				
			||||||
 | 
						w := tabwriter.NewWriter(output, 20, 5, 3, ' ', 0)
 | 
				
			||||||
 | 
						defer w.Flush()
 | 
				
			||||||
 | 
						var obj interface{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal([]byte(data), &obj); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, contains := obj.(map[string]interface{})["kind"]; !contains {
 | 
				
			||||||
 | 
							return fmt.Errorf("Unexpected object with no 'kind' field: %s", data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						kind := (obj.(map[string]interface{})["kind"]).(string)
 | 
				
			||||||
 | 
						obj, err := h.extractObject(data, kind)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch obj.(type) {
 | 
				
			||||||
 | 
						case api.Pod:
 | 
				
			||||||
 | 
							h.printHeader(podColumns, w)
 | 
				
			||||||
 | 
							return h.printPod(obj.(api.Pod), w)
 | 
				
			||||||
 | 
						case api.PodList:
 | 
				
			||||||
 | 
							h.printHeader(podColumns, w)
 | 
				
			||||||
 | 
							return h.printPodList(obj.(api.PodList), w)
 | 
				
			||||||
 | 
						case api.ReplicationController:
 | 
				
			||||||
 | 
							h.printHeader(replicationControllerColumns, w)
 | 
				
			||||||
 | 
							return h.printReplicationController(obj.(api.ReplicationController), w)
 | 
				
			||||||
 | 
						case api.ReplicationControllerList:
 | 
				
			||||||
 | 
							h.printHeader(replicationControllerColumns, w)
 | 
				
			||||||
 | 
							return h.printReplicationControllerList(obj.(api.ReplicationControllerList), w)
 | 
				
			||||||
 | 
						case api.Service:
 | 
				
			||||||
 | 
							h.printHeader(serviceColumns, w)
 | 
				
			||||||
 | 
							return h.printService(obj.(api.Service), w)
 | 
				
			||||||
 | 
						case api.ServiceList:
 | 
				
			||||||
 | 
							h.printHeader(serviceColumns, w)
 | 
				
			||||||
 | 
							return h.printServiceList(obj.(api.ServiceList), w)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return h.unknown(data, w)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -59,6 +59,7 @@ func (storage *ControllerRegistryStorage) Delete(id string) error {
 | 
				
			|||||||
func (storage *ControllerRegistryStorage) Extract(body string) (interface{}, error) {
 | 
					func (storage *ControllerRegistryStorage) Extract(body string) (interface{}, error) {
 | 
				
			||||||
	result := ReplicationController{}
 | 
						result := ReplicationController{}
 | 
				
			||||||
	err := json.Unmarshal([]byte(body), &result)
 | 
						err := json.Unmarshal([]byte(body), &result)
 | 
				
			||||||
 | 
						result.Kind = "cluster#replicationController"
 | 
				
			||||||
	return result, err
 | 
						return result, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,9 +124,9 @@ func TestExtractControllerJson(t *testing.T) {
 | 
				
			|||||||
	expectNoError(t, err)
 | 
						expectNoError(t, err)
 | 
				
			||||||
	controllerOut, err := storage.Extract(string(body))
 | 
						controllerOut, err := storage.Extract(string(body))
 | 
				
			||||||
	expectNoError(t, err)
 | 
						expectNoError(t, err)
 | 
				
			||||||
	jsonOut, err := json.Marshal(controllerOut)
 | 
						// Extract adds a Kind
 | 
				
			||||||
	expectNoError(t, err)
 | 
						controller.Kind = "cluster#replicationController"
 | 
				
			||||||
	if string(body) != string(jsonOut) {
 | 
						if !reflect.DeepEqual(controller, controllerOut) {
 | 
				
			||||||
		t.Errorf("Expected %#v, found %#v", controller, controllerOut)
 | 
							t.Errorf("Expected %#v, found %#v", controller, controllerOut)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,7 @@ func (storage *PodRegistryStorage) Delete(id string) error {
 | 
				
			|||||||
func (storage *PodRegistryStorage) Extract(body string) (interface{}, error) {
 | 
					func (storage *PodRegistryStorage) Extract(body string) (interface{}, error) {
 | 
				
			||||||
	pod := Pod{}
 | 
						pod := Pod{}
 | 
				
			||||||
	err := json.Unmarshal([]byte(body), &pod)
 | 
						err := json.Unmarshal([]byte(body), &pod)
 | 
				
			||||||
 | 
						pod.Kind = "cluster#pod"
 | 
				
			||||||
	return pod, err
 | 
						return pod, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ package registry
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	. "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
						. "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
				
			||||||
@@ -127,9 +128,9 @@ func TestExtractJson(t *testing.T) {
 | 
				
			|||||||
	expectNoError(t, err)
 | 
						expectNoError(t, err)
 | 
				
			||||||
	podOut, err := storage.Extract(string(body))
 | 
						podOut, err := storage.Extract(string(body))
 | 
				
			||||||
	expectNoError(t, err)
 | 
						expectNoError(t, err)
 | 
				
			||||||
	jsonOut, err := json.Marshal(podOut)
 | 
						// Extract adds in a kind
 | 
				
			||||||
	expectNoError(t, err)
 | 
						pod.Kind = "cluster#pod"
 | 
				
			||||||
	if string(body) != string(jsonOut) {
 | 
						if !reflect.DeepEqual(pod, podOut) {
 | 
				
			||||||
		t.Errorf("Expected %#v, found %#v", pod, podOut)
 | 
							t.Errorf("Expected %#v, found %#v", pod, podOut)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,6 +78,7 @@ func (sr *ServiceRegistryStorage) Delete(id string) error {
 | 
				
			|||||||
func (sr *ServiceRegistryStorage) Extract(body string) (interface{}, error) {
 | 
					func (sr *ServiceRegistryStorage) Extract(body string) (interface{}, error) {
 | 
				
			||||||
	var svc Service
 | 
						var svc Service
 | 
				
			||||||
	err := json.Unmarshal([]byte(body), &svc)
 | 
						err := json.Unmarshal([]byte(body), &svc)
 | 
				
			||||||
 | 
						svc.Kind = "cluster#service"
 | 
				
			||||||
	return svc, err
 | 
						return svc, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user