From 3d0231e0f8aa4e5eca1a4517f60b97a6460e7eb4 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Sun, 8 Jun 2014 20:29:30 -0700 Subject: [PATCH 1/5] Add a couple of printers. --- cmd/cloudcfg/cloudcfg.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cmd/cloudcfg/cloudcfg.go b/cmd/cloudcfg/cloudcfg.go index 29a34240b6c..f495de79fc3 100644 --- a/cmd/cloudcfg/cloudcfg.go +++ b/cmd/cloudcfg/cloudcfg.go @@ -40,6 +40,8 @@ var ( portSpec = flag.String("p", "", "The port spec, comma-separated list of :,...") 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") + 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() { @@ -63,6 +65,15 @@ func main() { var request *http.Request 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) if err != nil { log.Fatalf("Error loading auth: %#v", err) @@ -124,5 +135,9 @@ func main() { if err != nil { 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") } From fdcf273d5000ba1a7edecb7baf0ca066c6689353 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Sun, 8 Jun 2014 21:00:59 -0700 Subject: [PATCH 2/5] Add pretty printing. --- pkg/registry/controller_registry.go | 1 + pkg/registry/controller_registry_test.go | 6 +++--- pkg/registry/service_registry.go | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/registry/controller_registry.go b/pkg/registry/controller_registry.go index 126b006d1a9..38abbb51b7b 100644 --- a/pkg/registry/controller_registry.go +++ b/pkg/registry/controller_registry.go @@ -59,6 +59,7 @@ func (storage *ControllerRegistryStorage) Delete(id string) error { func (storage *ControllerRegistryStorage) Extract(body string) (interface{}, error) { result := ReplicationController{} err := json.Unmarshal([]byte(body), &result) + result.Kind = "cluster#replicationController" return result, err } diff --git a/pkg/registry/controller_registry_test.go b/pkg/registry/controller_registry_test.go index 0f99b1117b9..9df4bc524e3 100644 --- a/pkg/registry/controller_registry_test.go +++ b/pkg/registry/controller_registry_test.go @@ -124,9 +124,9 @@ func TestExtractControllerJson(t *testing.T) { expectNoError(t, err) controllerOut, err := storage.Extract(string(body)) expectNoError(t, err) - jsonOut, err := json.Marshal(controllerOut) - expectNoError(t, err) - if string(body) != string(jsonOut) { + // Extract adds a Kind + controller.Kind = "cluster#replicationController" + if !reflect.DeepEqual(controller, controllerOut) { t.Errorf("Expected %#v, found %#v", controller, controllerOut) } } diff --git a/pkg/registry/service_registry.go b/pkg/registry/service_registry.go index 542219f001f..39c3c61be89 100644 --- a/pkg/registry/service_registry.go +++ b/pkg/registry/service_registry.go @@ -78,6 +78,7 @@ func (sr *ServiceRegistryStorage) Delete(id string) error { func (sr *ServiceRegistryStorage) Extract(body string) (interface{}, error) { var svc Service err := json.Unmarshal([]byte(body), &svc) + svc.Kind = "cluster#service" return svc, err } From 3e211272a7122362c8ad92f0311cae2dc73f1e64 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Sun, 8 Jun 2014 21:01:25 -0700 Subject: [PATCH 3/5] Added. --- pkg/cloudcfg/resource_printer.go | 206 +++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 pkg/cloudcfg/resource_printer.go diff --git a/pkg/cloudcfg/resource_printer.go b/pkg/cloudcfg/resource_printer.go new file mode 100644 index 00000000000..aa3382d72b3 --- /dev/null +++ b/pkg/cloudcfg/resource_printer.go @@ -0,0 +1,206 @@ +package cloudcfg + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "strings" + "text/tabwriter" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "gopkg.in/v1/yaml" +) + +type ResourcePrinter interface { + Print(string, io.Writer) error +} + +type IdentityPrinter struct{} + +func (i *IdentityPrinter) Print(data string, w io.Writer) error { + _, err := io.Copy(w, bytes.NewBufferString(data)) + return err +} + +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 +} + +type HumanReadablePrinter struct{} + +var taskColumns = []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) printTask(task api.Task, w io.Writer) error { + _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", + task.ID, h.makeImageList(task.DesiredState.Manifest), task.CurrentState.Host, h.makeLabelsList(task.Labels)) + return err +} + +func (h *HumanReadablePrinter) printTaskList(taskList api.TaskList, w io.Writer) error { + for _, task := range taskList.Items { + if err := h.printTask(task, 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.TaskTemplate.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 +} + +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#task": + var obj api.Task + if err := json.Unmarshal([]byte(data), &obj); err != nil { + return nil, err + } + return obj, nil + case "cluster#taskList": + var list api.TaskList + 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.Task: + h.printHeader(taskColumns, w) + return h.printTask(obj.(api.Task), w) + case api.TaskList: + h.printHeader(taskColumns, w) + return h.printTaskList(obj.(api.TaskList), 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) + } +} From f204bd52bcfb5ca058742a16ba2528c3e56363c2 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Sun, 8 Jun 2014 21:05:43 -0700 Subject: [PATCH 4/5] Add some more docs. --- pkg/cloudcfg/resource_printer.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/cloudcfg/resource_printer.go b/pkg/cloudcfg/resource_printer.go index aa3382d72b3..61cedbb3a41 100644 --- a/pkg/cloudcfg/resource_printer.go +++ b/pkg/cloudcfg/resource_printer.go @@ -1,7 +1,6 @@ package cloudcfg import ( - "bytes" "encoding/json" "fmt" "io" @@ -12,17 +11,21 @@ import ( "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 := io.Copy(w, bytes.NewBufferString(data)) + _, 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 { @@ -38,6 +41,7 @@ func (y *YAMLPrinter) Print(data string, w io.Writer) error { return err } +// HumanReadablePrinter attempts to provide more elegant output type HumanReadablePrinter struct{} var taskColumns = []string{"Name", "Image(s)", "Host", "Labels"} From c96b14aba012f6109124486e1fee4df4aeff3053 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Sun, 8 Jun 2014 23:29:04 -0700 Subject: [PATCH 5/5] Fixes for task->pod changes & a TODO --- pkg/cloudcfg/resource_printer.go | 36 ++++++++++++++++--------------- pkg/registry/pod_registry.go | 1 + pkg/registry/pod_registry_test.go | 7 +++--- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/pkg/cloudcfg/resource_printer.go b/pkg/cloudcfg/resource_printer.go index 61cedbb3a41..5ad9fb7c14b 100644 --- a/pkg/cloudcfg/resource_printer.go +++ b/pkg/cloudcfg/resource_printer.go @@ -44,7 +44,7 @@ func (y *YAMLPrinter) Print(data string, w io.Writer) error { // HumanReadablePrinter attempts to provide more elegant output type HumanReadablePrinter struct{} -var taskColumns = []string{"Name", "Image(s)", "Host", "Labels"} +var podColumns = []string{"Name", "Image(s)", "Host", "Labels"} var replicationControllerColumns = []string{"Name", "Image(s)", "Label Query", "Replicas"} var serviceColumns = []string{"Name", "Label Query", "Port"} @@ -81,15 +81,15 @@ func (h *HumanReadablePrinter) makeLabelsList(labels map[string]string) string { return strings.Join(vals, ",") } -func (h *HumanReadablePrinter) printTask(task api.Task, w io.Writer) error { +func (h *HumanReadablePrinter) printPod(pod api.Pod, w io.Writer) error { _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", - task.ID, h.makeImageList(task.DesiredState.Manifest), task.CurrentState.Host, h.makeLabelsList(task.Labels)) + pod.ID, h.makeImageList(pod.DesiredState.Manifest), pod.CurrentState.Host, h.makeLabelsList(pod.Labels)) return err } -func (h *HumanReadablePrinter) printTaskList(taskList api.TaskList, w io.Writer) error { - for _, task := range taskList.Items { - if err := h.printTask(task, w); err != nil { +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 } } @@ -98,7 +98,7 @@ func (h *HumanReadablePrinter) printTaskList(taskList api.TaskList, w io.Writer) 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.TaskTemplate.DesiredState.Manifest), h.makeLabelsList(ctrl.DesiredState.ReplicasInSet), ctrl.DesiredState.Replicas) + ctrl.ID, h.makeImageList(ctrl.DesiredState.PodTemplate.DesiredState.Manifest), h.makeLabelsList(ctrl.DesiredState.ReplicasInSet), ctrl.DesiredState.Replicas) return err } @@ -125,17 +125,19 @@ func (h *HumanReadablePrinter) printServiceList(list api.ServiceList, w io.Write 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#task": - var obj api.Task + case "cluster#pod": + var obj api.Pod if err := json.Unmarshal([]byte(data), &obj); err != nil { return nil, err } return obj, nil - case "cluster#taskList": - var list api.TaskList + case "cluster#podList": + var list api.PodList if err := json.Unmarshal([]byte(data), &list); err != nil { return nil, err } @@ -186,12 +188,12 @@ func (h *HumanReadablePrinter) Print(data string, output io.Writer) error { return err } switch obj.(type) { - case api.Task: - h.printHeader(taskColumns, w) - return h.printTask(obj.(api.Task), w) - case api.TaskList: - h.printHeader(taskColumns, w) - return h.printTaskList(obj.(api.TaskList), w) + 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) diff --git a/pkg/registry/pod_registry.go b/pkg/registry/pod_registry.go index 3f2541e2de7..e25d7f74620 100644 --- a/pkg/registry/pod_registry.go +++ b/pkg/registry/pod_registry.go @@ -101,6 +101,7 @@ func (storage *PodRegistryStorage) Delete(id string) error { func (storage *PodRegistryStorage) Extract(body string) (interface{}, error) { pod := Pod{} err := json.Unmarshal([]byte(body), &pod) + pod.Kind = "cluster#pod" return pod, err } diff --git a/pkg/registry/pod_registry_test.go b/pkg/registry/pod_registry_test.go index 81b3468fd7d..c5bfc4d4ea6 100644 --- a/pkg/registry/pod_registry_test.go +++ b/pkg/registry/pod_registry_test.go @@ -18,6 +18,7 @@ package registry import ( "encoding/json" "fmt" + "reflect" "testing" . "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -127,9 +128,9 @@ func TestExtractJson(t *testing.T) { expectNoError(t, err) podOut, err := storage.Extract(string(body)) expectNoError(t, err) - jsonOut, err := json.Marshal(podOut) - expectNoError(t, err) - if string(body) != string(jsonOut) { + // Extract adds in a kind + pod.Kind = "cluster#pod" + if !reflect.DeepEqual(pod, podOut) { t.Errorf("Expected %#v, found %#v", pod, podOut) } }