mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Allow kubectl get to fetch multiple resource types
Like Delete, which can now run over multiple types: kubectl delete pods,services -l name=foo Get should be able to span items for label selection kubectl get pods,services -l name=foo
This commit is contained in:
parent
b8333bdeef
commit
2151afe334
@ -28,6 +28,7 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
|
||||
@ -42,6 +43,7 @@ const (
|
||||
|
||||
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
|
||||
// of resources and different API sets.
|
||||
// TODO: make the functions interfaces
|
||||
type Factory struct {
|
||||
clients *clientCache
|
||||
flags *pflag.FlagSet
|
||||
@ -218,6 +220,13 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
||||
return clientConfig
|
||||
}
|
||||
|
||||
// ClientMapperForCommand returns a ClientMapper for the given command and factory.
|
||||
func ClientMapperForCommand(cmd *cobra.Command, f *Factory) resource.ClientMapper {
|
||||
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||
return f.RESTClient(cmd, mapping)
|
||||
})
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
glog.FatalDepth(1, err)
|
||||
|
@ -22,6 +22,8 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
. "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
|
||||
@ -66,12 +68,12 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
|
||||
}
|
||||
|
||||
type testPrinter struct {
|
||||
Obj runtime.Object
|
||||
Err error
|
||||
Objects []runtime.Object
|
||||
Err error
|
||||
}
|
||||
|
||||
func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||
t.Obj = obj
|
||||
t.Objects = append(t.Objects, obj)
|
||||
fmt.Fprintf(out, "%#v", obj)
|
||||
return t.Err
|
||||
}
|
||||
@ -112,6 +114,23 @@ func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
|
||||
}, t, codec
|
||||
}
|
||||
|
||||
func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) {
|
||||
t := &testFactory{}
|
||||
return &Factory{
|
||||
Mapper: latest.RESTMapper,
|
||||
Typer: api.Scheme,
|
||||
RESTClient: func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error) {
|
||||
return t.Client, t.Err
|
||||
},
|
||||
Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) {
|
||||
return t.Describer, t.Err
|
||||
},
|
||||
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
|
||||
return t.Printer, t.Err
|
||||
},
|
||||
}, t, latest.Codec
|
||||
}
|
||||
|
||||
func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ Examples:
|
||||
checkErr(err)
|
||||
selector := GetFlagString(cmd, "selector")
|
||||
found := 0
|
||||
ResourcesFromArgsOrFile(cmd, args, filename, selector, f.Typer, f.Mapper, f.RESTClient, schema).Visit(func(r *resource.Info) error {
|
||||
ResourcesFromArgsOrFile(cmd, args, filename, selector, f.Typer, f.Mapper, f.RESTClient, schema, true).Visit(func(r *resource.Info) error {
|
||||
found++
|
||||
if err := resource.NewHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil {
|
||||
return err
|
||||
|
@ -20,14 +20,17 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewCmdGet creates a command object for the generic "get" action, which
|
||||
// retrieves one or more resources from a server.
|
||||
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [(-o|--output=)json|yaml|...] <resource> [<id>]",
|
||||
@ -48,66 +51,134 @@ Examples:
|
||||
<list single replication controller in ps output format>
|
||||
|
||||
$ kubectl get -o json pod 1234-56-7890-234234-456456
|
||||
<list single pod in json output format>`,
|
||||
<list single pod in json output format>
|
||||
|
||||
$ kubectl get rc,services
|
||||
<list replication controllers and services together in ps output format>`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, f.Mapper)
|
||||
|
||||
selector := GetFlagString(cmd, "selector")
|
||||
labelSelector, err := labels.ParseSelector(selector)
|
||||
checkErr(err)
|
||||
|
||||
client, err := f.RESTClient(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
outputFormat := GetFlagString(cmd, "output")
|
||||
templateFile := GetFlagString(cmd, "template")
|
||||
defaultPrinter, err := f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers"))
|
||||
checkErr(err)
|
||||
|
||||
outputVersion := GetFlagString(cmd, "output-version")
|
||||
if len(outputVersion) == 0 {
|
||||
outputVersion = mapping.APIVersion
|
||||
}
|
||||
|
||||
printer, err := kubectl.GetPrinter(outputFormat, templateFile, outputVersion, mapping.ObjectConvertor, defaultPrinter)
|
||||
checkErr(err)
|
||||
|
||||
restHelper := resource.NewHelper(client, mapping)
|
||||
var obj runtime.Object
|
||||
if len(name) == 0 {
|
||||
obj, err = restHelper.List(namespace, labelSelector)
|
||||
} else {
|
||||
obj, err = restHelper.Get(namespace, name)
|
||||
}
|
||||
checkErr(err)
|
||||
|
||||
isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only")
|
||||
|
||||
// print the current object
|
||||
if !isWatchOnly {
|
||||
if err := printer.PrintObj(obj, out); err != nil {
|
||||
checkErr(fmt.Errorf("unable to output the provided object: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// print watched changes
|
||||
if isWatch || isWatchOnly {
|
||||
rv, err := mapping.MetadataAccessor.ResourceVersion(obj)
|
||||
checkErr(err)
|
||||
|
||||
w, err := restHelper.Watch(namespace, rv, labelSelector, labels.Everything())
|
||||
checkErr(err)
|
||||
|
||||
kubectl.WatchLoop(w, printer, out)
|
||||
}
|
||||
RunGet(f, out, cmd, args)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP("output", "o", "", "Output format: json|yaml|template|templatefile")
|
||||
cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version)")
|
||||
cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers")
|
||||
cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when --output=template or --output=templatefile")
|
||||
cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")
|
||||
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
|
||||
cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.")
|
||||
cmd.Flags().Bool("watch-only", false, "Watch for changes to the requseted object(s), without listing/getting first.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunGet implements the generic Get command
|
||||
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
|
||||
// TODO: return an error instead of using glog.Fatal and checkErr
|
||||
func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
|
||||
selector := GetFlagString(cmd, "selector")
|
||||
|
||||
// handle watch separately since we cannot watch multiple resource types
|
||||
isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only")
|
||||
if isWatch || isWatchOnly {
|
||||
r := resource.NewBuilder(f.Mapper, f.Typer, ClientMapperForCommand(cmd, f)).
|
||||
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
|
||||
SelectorParam(selector).
|
||||
ResourceTypeOrNameArgs(args...).
|
||||
SingleResourceType().
|
||||
Do()
|
||||
|
||||
mapping, err := r.ResourceMapping()
|
||||
checkErr(err)
|
||||
|
||||
printer, err := printerForMapping(f, cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
obj, err := r.Object()
|
||||
checkErr(err)
|
||||
|
||||
rv, err := mapping.MetadataAccessor.ResourceVersion(obj)
|
||||
checkErr(err)
|
||||
|
||||
// print the current object
|
||||
if !isWatchOnly {
|
||||
if err := printer.PrintObj(obj, out); err != nil {
|
||||
checkErr(fmt.Errorf("unable to output the provided object: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// print watched changes
|
||||
w, err := r.Watch(rv)
|
||||
checkErr(err)
|
||||
|
||||
kubectl.WatchLoop(w, func(e watch.Event) error {
|
||||
return printer.PrintObj(e.Object, out)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
printer, generic, err := printerForCommand(cmd)
|
||||
checkErr(err)
|
||||
|
||||
b := resource.NewBuilder(f.Mapper, f.Typer, ClientMapperForCommand(cmd, f)).
|
||||
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
|
||||
SelectorParam(selector).
|
||||
ResourceTypeOrNameArgs(args...).
|
||||
Latest()
|
||||
|
||||
if generic {
|
||||
// the outermost object will be converted to the output-version
|
||||
printer = kubectl.NewVersionedPrinter(printer, api.Scheme, outputVersion(cmd))
|
||||
|
||||
obj, err := b.Flatten().Do().Object()
|
||||
checkErr(err)
|
||||
|
||||
err = printer.PrintObj(obj, out)
|
||||
checkErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
// use the default printer for each object
|
||||
err = b.Do().Visit(func(r *resource.Info) error {
|
||||
printer, err := printerForMapping(f, cmd, r.Mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(r.Object, out)
|
||||
})
|
||||
checkErr(err)
|
||||
}
|
||||
|
||||
// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
|
||||
func outputVersion(cmd *cobra.Command) string {
|
||||
outputVersion := GetFlagString(cmd, "output-version")
|
||||
if len(outputVersion) == 0 {
|
||||
outputVersion = GetFlagString(cmd, "api-version")
|
||||
}
|
||||
return outputVersion
|
||||
}
|
||||
|
||||
// printerForCommand returns the default printer for this command.
|
||||
func printerForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) {
|
||||
outputFormat := GetFlagString(cmd, "output")
|
||||
templateFile := GetFlagString(cmd, "template")
|
||||
if len(outputFormat) == 0 && len(templateFile) != 0 {
|
||||
outputFormat = "template"
|
||||
}
|
||||
|
||||
return kubectl.GetPrinter(outputFormat, templateFile)
|
||||
}
|
||||
|
||||
// printerForMapping returns a printer suitable for displaying the provided resource type.
|
||||
func printerForMapping(f *Factory, cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
|
||||
printer, ok, err := printerForCommand(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, outputVersion(cmd))
|
||||
} else {
|
||||
printer, err = f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return printer, nil
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package cmd_test
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -27,9 +29,38 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch/json"
|
||||
)
|
||||
|
||||
func testData() (*api.PodList, *api.ServiceList) {
|
||||
pods := &api.PodList{
|
||||
ListMeta: api.ListMeta{
|
||||
ResourceVersion: "15",
|
||||
},
|
||||
Items: []api.Pod{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
|
||||
},
|
||||
},
|
||||
}
|
||||
svc := &api.ServiceList{
|
||||
ListMeta: api.ListMeta{
|
||||
ResourceVersion: "16",
|
||||
},
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
||||
},
|
||||
},
|
||||
}
|
||||
return pods, svc
|
||||
}
|
||||
|
||||
// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
|
||||
func TestGetUnknownSchemaObject(t *testing.T) {
|
||||
f, tf, codec := NewTestFactory()
|
||||
@ -41,12 +72,13 @@ func TestGetUnknownSchemaObject(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("api-version", "default", "")
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"type", "foo"})
|
||||
|
||||
expected := &internalType{Name: "foo"}
|
||||
actual := tf.Printer.(*testPrinter).Obj
|
||||
actual := tf.Printer.(*testPrinter).Objects[0]
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v", actual)
|
||||
}
|
||||
@ -73,12 +105,347 @@ func TestGetSchemaObject(t *testing.T) {
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers", "foo"})
|
||||
|
||||
expected := &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: api.ReplicationControllerSpec{Template: &api.PodTemplateSpec{}}}
|
||||
actual := tf.Printer.(*testPrinter).Obj
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %s", util.ObjectGoPrintDiff(expected, actual))
|
||||
}
|
||||
if !strings.Contains(buf.String(), "\"foo\"") {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetObjects(t *testing.T) {
|
||||
pods, _ := testData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])},
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods", "foo"})
|
||||
|
||||
expected := []runtime.Object{&pods.Items[0]}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v", actual)
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetListObjects(t *testing.T) {
|
||||
pods, _ := testData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)},
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
|
||||
expected := []runtime.Object{pods}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v %#v", expected, actual)
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMultipleTypeObjects(t *testing.T) {
|
||||
pods, svc := testData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Path {
|
||||
case "/ns/test/pods":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil
|
||||
case "/ns/test/services":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
expected := []runtime.Object{pods, svc}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v", actual)
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
||||
pods, svc := testData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Path {
|
||||
case "/ns/test/pods":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil
|
||||
case "/ns/test/services":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
cmd.Flags().String("api-version", "v1beta1", "")
|
||||
|
||||
cmd.Flags().Set("output", "json")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
if tf.Printer.(*testPrinter).Objects != nil {
|
||||
t.Errorf("unexpected print to default printer")
|
||||
}
|
||||
|
||||
out, err := codec.Decode(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expected := &api.List{
|
||||
Items: []runtime.Object{
|
||||
&pods.Items[0],
|
||||
&pods.Items[1],
|
||||
&svc.Items[0],
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(expected, out) {
|
||||
t.Errorf("unexpected output: %#v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMultipleTypeObjectsWithSelector(t *testing.T) {
|
||||
pods, svc := testData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Query().Get("labels") != "a=b" {
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
switch req.URL.Path {
|
||||
case "/ns/test/pods":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil
|
||||
case "/ns/test/services":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
expected := []runtime.Object{pods, svc}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v", actual)
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func watchTestData() ([]api.Pod, []watch.Event) {
|
||||
pods := []api.Pod{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "test",
|
||||
ResourceVersion: "10",
|
||||
},
|
||||
},
|
||||
}
|
||||
events := []watch.Event{
|
||||
{
|
||||
Type: watch.Modified,
|
||||
Object: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "test",
|
||||
ResourceVersion: "11",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: watch.Deleted,
|
||||
Object: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "test",
|
||||
ResourceVersion: "12",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return pods, events
|
||||
}
|
||||
|
||||
func TestWatchSelector(t *testing.T) {
|
||||
pods, events := watchTestData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Query().Get("labels") != "a=b" {
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
switch req.URL.Path {
|
||||
case "/ns/test/pods":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, &api.PodList{Items: pods})}, nil
|
||||
case "/watch/ns/test/pods":
|
||||
return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("watch", "true")
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
|
||||
expected := []runtime.Object{&api.PodList{Items: pods}, events[0].Object, events[1].Object}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v %#v", expected[0], actual[0])
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchResource(t *testing.T) {
|
||||
pods, events := watchTestData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Path {
|
||||
case "/ns/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil
|
||||
case "/watch/ns/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("watch", "true")
|
||||
cmd.Run(cmd, []string{"pods", "foo"})
|
||||
|
||||
expected := []runtime.Object{&pods[0], events[0].Object, events[1].Object}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v", actual)
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchOnlyResource(t *testing.T) {
|
||||
pods, events := watchTestData()
|
||||
|
||||
f, tf, codec := NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Path {
|
||||
case "/ns/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil
|
||||
case "/watch/ns/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := f.NewCmdGet(buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().String("namespace", "test", "")
|
||||
|
||||
cmd.Flags().Set("watch-only", "true")
|
||||
cmd.Run(cmd, []string{"pods", "foo"})
|
||||
|
||||
expected := []runtime.Object{events[0].Object, events[1].Object}
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("unexpected object: %#v", actual)
|
||||
}
|
||||
if len(buf.String()) == 0 {
|
||||
t.Errorf("unexpected empty output")
|
||||
}
|
||||
}
|
||||
|
||||
func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
enc := json.NewEncoder(buf, codec)
|
||||
for i := range events {
|
||||
enc.Encode(&events[i])
|
||||
}
|
||||
return ioutil.NopCloser(buf)
|
||||
}
|
||||
|
@ -41,15 +41,23 @@ func ResourcesFromArgsOrFile(
|
||||
mapper meta.RESTMapper,
|
||||
clientBuilder func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error),
|
||||
schema validation.Schema,
|
||||
requireNames bool,
|
||||
) resource.Visitor {
|
||||
|
||||
// handling filename & resource id
|
||||
if len(selector) == 0 {
|
||||
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, typer, mapper, schema)
|
||||
client, err := clientBuilder(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
return resource.NewInfo(client, mapping, namespace, name)
|
||||
if requireNames || len(filename) > 0 {
|
||||
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, typer, mapper, schema)
|
||||
client, err := clientBuilder(cmd, mapping)
|
||||
checkErr(err)
|
||||
return resource.NewInfo(client, mapping, namespace, name)
|
||||
}
|
||||
if len(args) == 2 {
|
||||
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, mapper)
|
||||
client, err := clientBuilder(cmd, mapping)
|
||||
checkErr(err)
|
||||
return resource.NewInfo(client, mapping, namespace, name)
|
||||
}
|
||||
}
|
||||
|
||||
labelSelector, err := labels.ParseSelector(selector)
|
||||
@ -58,9 +66,12 @@ func ResourcesFromArgsOrFile(
|
||||
namespace := GetKubeNamespace(cmd)
|
||||
visitors := resource.VisitorList{}
|
||||
|
||||
if len(args) != 1 {
|
||||
if len(args) < 1 {
|
||||
usageError(cmd, "Must specify the type of resource")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
usageError(cmd, "Too many arguments")
|
||||
}
|
||||
types := SplitResourceArgument(args[0])
|
||||
for _, arg := range types {
|
||||
resourceName := arg
|
||||
|
@ -35,66 +35,94 @@ import (
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// GetPrinter takes a format type, an optional format argument, a version and a convertor
|
||||
// to be used if the underlying printer requires the object to be in a specific schema (
|
||||
// any of the generic formatters), and the default printer to use for this object.
|
||||
func GetPrinter(format, formatArgument, version string, convertor runtime.ObjectConvertor, defaultPrinter ResourcePrinter) (ResourcePrinter, error) {
|
||||
// GetPrinter takes a format type, an optional format argument. It will return true
|
||||
// if the format is generic (untyped), otherwise it will return false. The printer
|
||||
// is agnostic to schema versions, so you must send arguments to PrintObj in the
|
||||
// version you wish them to be shown using a VersionedPrinter (typically when
|
||||
// generic is true).
|
||||
func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
|
||||
var printer ResourcePrinter
|
||||
switch format {
|
||||
case "json":
|
||||
printer = &JSONPrinter{version, convertor}
|
||||
printer = &JSONPrinter{}
|
||||
case "yaml":
|
||||
printer = &YAMLPrinter{version, convertor}
|
||||
printer = &YAMLPrinter{}
|
||||
case "template":
|
||||
if len(formatArgument) == 0 {
|
||||
return nil, fmt.Errorf("template format specified but no template given")
|
||||
return nil, false, fmt.Errorf("template format specified but no template given")
|
||||
}
|
||||
var err error
|
||||
printer, err = NewTemplatePrinter([]byte(formatArgument), version, convertor)
|
||||
printer, err = NewTemplatePrinter([]byte(formatArgument))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing template %s, %v\n", formatArgument, err)
|
||||
return nil, false, fmt.Errorf("error parsing template %s, %v\n", formatArgument, err)
|
||||
}
|
||||
case "templatefile":
|
||||
if len(formatArgument) == 0 {
|
||||
return nil, fmt.Errorf("templatefile format specified but no template file given")
|
||||
return nil, false, fmt.Errorf("templatefile format specified but no template file given")
|
||||
}
|
||||
data, err := ioutil.ReadFile(formatArgument)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
|
||||
return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
|
||||
}
|
||||
printer, err = NewTemplatePrinter(data, version, convertor)
|
||||
printer, err = NewTemplatePrinter(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
|
||||
return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
|
||||
}
|
||||
case "":
|
||||
printer = defaultPrinter
|
||||
return nil, false, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("output format %q not recognized", format)
|
||||
return nil, false, fmt.Errorf("output format %q not recognized", format)
|
||||
}
|
||||
return printer, nil
|
||||
return printer, true, nil
|
||||
}
|
||||
|
||||
// ResourcePrinter is an interface that knows how to print runtime objects.
|
||||
type ResourcePrinter interface {
|
||||
// Print receives an arbitrary object, formats it and prints it to a writer.
|
||||
// Print receives a runtime object, formats it and prints it to a writer.
|
||||
PrintObj(runtime.Object, io.Writer) error
|
||||
}
|
||||
|
||||
// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON.
|
||||
// The input object is assumed to be in the internal version of an API and is converted
|
||||
// to the given version first.
|
||||
type JSONPrinter struct {
|
||||
version string
|
||||
// ResourcePrinterFunc is a function that can print objects
|
||||
type ResourcePrinterFunc func(runtime.Object, io.Writer) error
|
||||
|
||||
// PrintObj implements ResourcePrinter
|
||||
func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
return fn(obj, w)
|
||||
}
|
||||
|
||||
// VersionedPrinter takes runtime objects and ensures they are converted to a given API version
|
||||
// prior to being passed to a nested printer.
|
||||
type VersionedPrinter struct {
|
||||
printer ResourcePrinter
|
||||
convertor runtime.ObjectConvertor
|
||||
version string
|
||||
}
|
||||
|
||||
// NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing.
|
||||
func NewVersionedPrinter(printer ResourcePrinter, convertor runtime.ObjectConvertor, version string) ResourcePrinter {
|
||||
return &VersionedPrinter{
|
||||
printer: printer,
|
||||
convertor: convertor,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// PrintObj implements ResourcePrinter
|
||||
func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
converted, err := p.convertor.ConvertToVersion(obj, p.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.printer.PrintObj(converted, w)
|
||||
}
|
||||
|
||||
// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON.
|
||||
type JSONPrinter struct {
|
||||
}
|
||||
|
||||
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
|
||||
func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
outObj, err := p.convertor.ConvertToVersion(obj, p.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.Marshal(outObj)
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -115,11 +143,7 @@ type YAMLPrinter struct {
|
||||
|
||||
// PrintObj prints the data as YAML.
|
||||
func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
outObj, err := p.convertor.ConvertToVersion(obj, p.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := yaml.Marshal(outObj)
|
||||
output, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -387,11 +411,9 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
||||
type TemplatePrinter struct {
|
||||
rawTemplate string
|
||||
template *template.Template
|
||||
version string
|
||||
convertor runtime.ObjectConvertor
|
||||
}
|
||||
|
||||
func NewTemplatePrinter(tmpl []byte, asVersion string, convertor runtime.ObjectConvertor) (*TemplatePrinter, error) {
|
||||
func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
|
||||
t, err := template.New("output").
|
||||
Funcs(template.FuncMap{"exists": exists}).
|
||||
Parse(string(tmpl))
|
||||
@ -401,18 +423,12 @@ func NewTemplatePrinter(tmpl []byte, asVersion string, convertor runtime.ObjectC
|
||||
return &TemplatePrinter{
|
||||
rawTemplate: string(tmpl),
|
||||
template: t,
|
||||
version: asVersion,
|
||||
convertor: convertor,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PrintObj formats the obj with the Go Template.
|
||||
func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
outObj, err := p.convertor.ConvertToVersion(obj, p.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.Marshal(outObj)
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
|
||||
@ -56,38 +57,56 @@ var testData = testStruct{
|
||||
IntList: []int{1, 2, 3},
|
||||
}
|
||||
|
||||
func TestVersionedPrinter(t *testing.T) {
|
||||
original := &testStruct{Key: "value"}
|
||||
p := NewVersionedPrinter(
|
||||
ResourcePrinterFunc(func(obj runtime.Object, w io.Writer) error {
|
||||
if obj == original {
|
||||
t.Fatalf("object should not be identical: %#v", obj)
|
||||
}
|
||||
if obj.(*testStruct).Key != "value" {
|
||||
t.Fatalf("object was not converted: %#v", obj)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
api.Scheme,
|
||||
testapi.Version(),
|
||||
)
|
||||
if err := p.PrintObj(original, nil); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestYAMLPrinter(t *testing.T) {
|
||||
testPrinter(t, &YAMLPrinter{testapi.Version(), api.Scheme}, yaml.Unmarshal)
|
||||
testPrinter(t, &YAMLPrinter{}, yaml.Unmarshal)
|
||||
}
|
||||
|
||||
func TestJSONPrinter(t *testing.T) {
|
||||
testPrinter(t, &JSONPrinter{testapi.Version(), api.Scheme}, json.Unmarshal)
|
||||
testPrinter(t, &JSONPrinter{}, json.Unmarshal)
|
||||
}
|
||||
|
||||
func TestPrintDefault(t *testing.T) {
|
||||
printer, found, err := GetPrinter("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
if found {
|
||||
t.Errorf("no printer should have been found: %#v / %v", printer, err)
|
||||
}
|
||||
}
|
||||
|
||||
type internalType struct {
|
||||
Name string
|
||||
Kind string
|
||||
}
|
||||
|
||||
type externalType struct {
|
||||
Name string
|
||||
Kind string `json:"kind"`
|
||||
func (*internalType) IsAnAPIObject() {
|
||||
|
||||
}
|
||||
|
||||
func (*internalType) IsAnAPIObject() {}
|
||||
func (*externalType) IsAnAPIObject() {}
|
||||
|
||||
func newExternalScheme() *runtime.Scheme {
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName("", "Type", &internalType{})
|
||||
scheme.AddKnownTypeWithName("unlikelyversion", "Type", &externalType{})
|
||||
return scheme
|
||||
}
|
||||
|
||||
func TestPrintJSONForUnknownSchema(t *testing.T) {
|
||||
func TestPrintJSONForObject(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
printer, err := GetPrinter("json", "", "unlikelyversion", newExternalScheme(), nil)
|
||||
if err != nil {
|
||||
printer, found, err := GetPrinter("json", "")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
if err := printer.PrintObj(&internalType{Name: "foo"}, buf); err != nil {
|
||||
@ -102,21 +121,10 @@ func TestPrintJSONForUnknownSchema(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintJSONForUnknownSchemaAndWrongVersion(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
printer, err := GetPrinter("json", "", "badversion", newExternalScheme(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
if err := printer.PrintObj(&internalType{Name: "foo"}, buf); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintJSON(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
printer, err := GetPrinter("json", "", testapi.Version(), api.Scheme, nil)
|
||||
if err != nil {
|
||||
printer, found, err := GetPrinter("json", "")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
printer.PrintObj(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, buf)
|
||||
@ -128,8 +136,8 @@ func TestPrintJSON(t *testing.T) {
|
||||
|
||||
func TestPrintYAML(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
printer, err := GetPrinter("yaml", "", testapi.Version(), api.Scheme, nil)
|
||||
if err != nil {
|
||||
printer, found, err := GetPrinter("yaml", "")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
printer.PrintObj(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, buf)
|
||||
@ -141,11 +149,11 @@ func TestPrintYAML(t *testing.T) {
|
||||
|
||||
func TestPrintTemplate(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
printer, err := GetPrinter("template", "{{.id}}", "v1beta1", api.Scheme, nil)
|
||||
if err != nil {
|
||||
printer, found, err := GetPrinter("template", "{{.id}}")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
err = printer.PrintObj(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, buf)
|
||||
err = printer.PrintObj(&v1beta1.Pod{TypeMeta: v1beta1.TypeMeta{ID: "foo"}}, buf)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
}
|
||||
@ -155,19 +163,19 @@ func TestPrintTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPrintEmptyTemplate(t *testing.T) {
|
||||
if _, err := GetPrinter("template", "", testapi.Version(), api.Scheme, nil); err == nil {
|
||||
if _, _, err := GetPrinter("template", ""); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintBadTemplate(t *testing.T) {
|
||||
if _, err := GetPrinter("template", "{{ .Name", testapi.Version(), api.Scheme, nil); err == nil {
|
||||
if _, _, err := GetPrinter("template", "{{ .Name"); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintBadTemplateFile(t *testing.T) {
|
||||
if _, err := GetPrinter("templatefile", "", testapi.Version(), api.Scheme, nil); err == nil {
|
||||
if _, _, err := GetPrinter("templatefile", ""); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
@ -276,12 +284,17 @@ func TestUnknownTypePrinting(t *testing.T) {
|
||||
|
||||
func TestTemplateEmitsVersionedObjects(t *testing.T) {
|
||||
// kind is always blank in memory and set on the wire
|
||||
printer, err := NewTemplatePrinter([]byte(`{{.kind}}`), testapi.Version(), api.Scheme)
|
||||
printer, err := NewTemplatePrinter([]byte(`{{.kind}}`))
|
||||
if err != nil {
|
||||
t.Fatalf("tmpl fail: %v", err)
|
||||
}
|
||||
obj, err := api.Scheme.ConvertToVersion(&api.Pod{}, "v1beta1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
err = printer.PrintObj(&api.Pod{}, buffer)
|
||||
err = printer.PrintObj(obj, buffer)
|
||||
if err != nil {
|
||||
t.Fatalf("print fail: %v", err)
|
||||
}
|
||||
@ -292,7 +305,7 @@ func TestTemplateEmitsVersionedObjects(t *testing.T) {
|
||||
|
||||
func TestTemplatePanic(t *testing.T) {
|
||||
tmpl := `{{and ((index .currentState.info "update-demo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
|
||||
printer, err := NewTemplatePrinter([]byte(tmpl), testapi.Version(), api.Scheme)
|
||||
printer, err := NewTemplatePrinter([]byte(tmpl))
|
||||
if err != nil {
|
||||
t.Fatalf("tmpl fail: %v", err)
|
||||
}
|
||||
@ -396,11 +409,13 @@ e: {{exists . "currentState" "info" "update-demo" "state" "running"}}
|
||||
f: {{exists . "currentState" "info" "update-demo" "state" "running" "startedAt"}}`
|
||||
_ = useThisToDebug // don't complain about unused var
|
||||
|
||||
printer, err := NewTemplatePrinter([]byte(tmpl), "v1beta1", api.Scheme)
|
||||
p, err := NewTemplatePrinter([]byte(tmpl))
|
||||
if err != nil {
|
||||
t.Fatalf("tmpl fail: %v", err)
|
||||
}
|
||||
|
||||
printer := NewVersionedPrinter(p, api.Scheme, "v1beta1")
|
||||
|
||||
for name, item := range table {
|
||||
buffer := &bytes.Buffer{}
|
||||
err = printer.PrintObj(&item.pod, buffer)
|
||||
@ -416,19 +431,19 @@ f: {{exists . "currentState" "info" "update-demo" "state" "running" "startedAt"}
|
||||
|
||||
func TestPrinters(t *testing.T) {
|
||||
om := func(name string) api.ObjectMeta { return api.ObjectMeta{Name: name} }
|
||||
templatePrinter, err := NewTemplatePrinter([]byte("{{.name}}"), testapi.Version(), api.Scheme)
|
||||
templatePrinter, err := NewTemplatePrinter([]byte("{{.name}}"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
templatePrinter2, err := NewTemplatePrinter([]byte("{{len .items}}"), testapi.Version(), api.Scheme)
|
||||
templatePrinter2, err := NewTemplatePrinter([]byte("{{len .items}}"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
printers := map[string]ResourcePrinter{
|
||||
"humanReadable": NewHumanReadablePrinter(true),
|
||||
"humanReadableHeaders": NewHumanReadablePrinter(false),
|
||||
"json": &JSONPrinter{testapi.Version(), api.Scheme},
|
||||
"yaml": &YAMLPrinter{testapi.Version(), api.Scheme},
|
||||
"json": &JSONPrinter{},
|
||||
"yaml": &YAMLPrinter{},
|
||||
"template": templatePrinter,
|
||||
"template2": templatePrinter2,
|
||||
}
|
||||
|
@ -17,16 +17,15 @@ limitations under the License.
|
||||
package kubectl
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// WatchLoop loops, writing objects in the events from w to printer.
|
||||
// WatchLoop loops, passing events in w to fn.
|
||||
// If user sends interrupt signal, shut down cleanly. Otherwise, never return.
|
||||
func WatchLoop(w watch.Interface, printer ResourcePrinter, out io.Writer) {
|
||||
func WatchLoop(w watch.Interface, fn func(watch.Event) error) {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt)
|
||||
defer signal.Stop(signals)
|
||||
@ -36,8 +35,7 @@ func WatchLoop(w watch.Interface, printer ResourcePrinter, out io.Writer) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// TODO: need to print out added/modified/deleted!
|
||||
if err := printer.PrintObj(event.Object, out); err != nil {
|
||||
if err := fn(event); err != nil {
|
||||
w.Stop()
|
||||
}
|
||||
case <-signals:
|
||||
|
Loading…
Reference in New Issue
Block a user