ExtractList should handle v1.RawExtension correctly

Also fixes CustomColumnPrinter to pass decoder in, and ensures a test
case tests the combined path.
This commit is contained in:
Clayton Coleman 2016-01-27 14:14:27 -05:00
parent 738eae88f8
commit 71a13f7f4b
6 changed files with 56 additions and 15 deletions

View File

@ -848,6 +848,12 @@ __EOF__
# Post-condition: redis-master and redis-slave services are created # Post-condition: redis-master and redis-slave services are created
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:redis-slave:' kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:redis-slave:'
### Custom columns can be specified
# Pre-condition: generate output using custom columns
output_message=$(kubectl get services -o=custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion 2>&1 "${kube_flags[@]}")
# Post-condition: should contain name column
kube::test::if_has_string "${output_message}" 'redis-master'
### Delete multiple services at once ### Delete multiple services at once
# Pre-condition: redis-master and redis-slave services exist # Pre-condition: redis-master and redis-slave services exist
kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:redis-slave:' kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:redis-slave:'

View File

@ -72,12 +72,17 @@ func ExtractList(obj runtime.Object) ([]runtime.Object, error) {
for i := range list { for i := range list {
raw := items.Index(i) raw := items.Index(i)
switch item := raw.Interface().(type) { switch item := raw.Interface().(type) {
case runtime.RawExtension:
switch {
case item.Object != nil:
list[i] = item.Object
case item.RawJSON != nil:
list[i] = &runtime.Unknown{RawJSON: item.RawJSON}
default:
list[i] = nil
}
case runtime.Object: case runtime.Object:
list[i] = item list[i] = item
case runtime.RawExtension:
list[i] = &runtime.Unknown{
RawJSON: item.RawJSON,
}
default: default:
var found bool var found bool
if list[i], found = raw.Addr().Interface().(runtime.Object); !found { if list[i], found = raw.Addr().Interface().(runtime.Object); !found {

View File

@ -67,6 +67,28 @@ func TestExtractList(t *testing.T) {
} }
} }
func TestExtractListV1(t *testing.T) {
pl := &v1.PodList{
Items: []v1.Pod{
{ObjectMeta: v1.ObjectMeta{Name: "1"}},
{ObjectMeta: v1.ObjectMeta{Name: "2"}},
{ObjectMeta: v1.ObjectMeta{Name: "3"}},
},
}
list, err := meta.ExtractList(pl)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if e, a := len(list), len(pl.Items); e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
for i := range list {
if e, a := list[i].(*v1.Pod).Name, pl.Items[i].Name; e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
}
}
func TestExtractListGeneric(t *testing.T) { func TestExtractListGeneric(t *testing.T) {
pl := &api.List{ pl := &api.List{
Items: []runtime.Object{ Items: []runtime.Object{
@ -94,6 +116,7 @@ func TestExtractListGenericV1(t *testing.T) {
Items: []runtime.RawExtension{ Items: []runtime.RawExtension{
{RawJSON: []byte("foo")}, {RawJSON: []byte("foo")},
{RawJSON: []byte("bar")}, {RawJSON: []byte("bar")},
{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
}, },
} }
list, err := meta.ExtractList(pl) list, err := meta.ExtractList(pl)
@ -109,6 +132,9 @@ func TestExtractListGenericV1(t *testing.T) {
if obj, ok := list[1].(*runtime.Unknown); !ok { if obj, ok := list[1].(*runtime.Unknown); !ok {
t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj) t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj)
} }
if obj, ok := list[2].(*v1.Pod); !ok {
t.Fatalf("Expected list[2] to be *runtime.Unknown, it is %#v", obj)
}
} }
type fakePtrInterfaceList struct { type fakePtrInterfaceList struct {

View File

@ -73,7 +73,7 @@ func massageJSONPath(pathExpression string) (string, error) {
// //
// NAME API_VERSION // NAME API_VERSION
// foo bar // foo bar
func NewCustomColumnsPrinterFromSpec(spec string) (*CustomColumnsPrinter, error) { func NewCustomColumnsPrinterFromSpec(spec string, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
if len(spec) == 0 { if len(spec) == 0 {
return nil, fmt.Errorf("custom-columns format specified but no custom columns given") return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
} }
@ -90,7 +90,7 @@ func NewCustomColumnsPrinterFromSpec(spec string) (*CustomColumnsPrinter, error)
} }
columns[ix] = Column{Header: colSpec[0], FieldSpec: spec} columns[ix] = Column{Header: colSpec[0], FieldSpec: spec}
} }
return &CustomColumnsPrinter{Columns: columns}, nil return &CustomColumnsPrinter{Columns: columns, Decoder: decoder}, nil
} }
func splitOnWhitespace(line string) []string { func splitOnWhitespace(line string) []string {
@ -108,7 +108,7 @@ func splitOnWhitespace(line string) []string {
// For example the template below: // For example the template below:
// NAME API_VERSION // NAME API_VERSION
// {metadata.name} {apiVersion} // {metadata.name} {apiVersion}
func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader) (*CustomColumnsPrinter, error) { func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
scanner := bufio.NewScanner(templateReader) scanner := bufio.NewScanner(templateReader)
if !scanner.Scan() { if !scanner.Scan() {
return nil, fmt.Errorf("invalid template, missing header line. Expected format is one line of space separated headers, one line of space separated column specs.") return nil, fmt.Errorf("invalid template, missing header line. Expected format is one line of space separated headers, one line of space separated column specs.")
@ -135,7 +135,7 @@ func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader) (*CustomColum
FieldSpec: spec, FieldSpec: spec,
} }
} }
return &CustomColumnsPrinter{Columns: columns}, nil return &CustomColumnsPrinter{Columns: columns, Decoder: decoder}, nil
} }
// Column represents a user specified column // Column represents a user specified column
@ -191,9 +191,11 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
columns := make([]string, len(parsers)) columns := make([]string, len(parsers))
switch u := obj.(type) { switch u := obj.(type) {
case *runtime.Unknown: case *runtime.Unknown:
var err error if len(u.RawJSON) > 0 {
if obj, _, err = s.Decoder.Decode(u.RawJSON, nil, nil); err != nil { var err error
return err if obj, err = runtime.Decode(s.Decoder, u.RawJSON); err != nil {
return fmt.Errorf("can't decode object for printing: %v (%s)", err, u.RawJSON)
}
} }
} }
for ix := range parsers { for ix := range parsers {

View File

@ -21,6 +21,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
@ -103,7 +104,7 @@ func TestNewColumnPrinterFromSpec(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
printer, err := NewCustomColumnsPrinterFromSpec(test.spec) printer, err := NewCustomColumnsPrinterFromSpec(test.spec, api.Codecs.UniversalDecoder())
if test.expectErr { if test.expectErr {
if err == nil { if err == nil {
t.Errorf("[%s] unexpected non-error", test.name) t.Errorf("[%s] unexpected non-error", test.name)
@ -186,7 +187,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
reader := bytes.NewBufferString(test.spec) reader := bytes.NewBufferString(test.spec)
printer, err := NewCustomColumnsPrinterFromTemplate(reader) printer, err := NewCustomColumnsPrinterFromTemplate(reader, api.Codecs.UniversalDecoder())
if test.expectErr { if test.expectErr {
if err == nil { if err == nil {
t.Errorf("[%s] unexpected non-error", test.name) t.Errorf("[%s] unexpected non-error", test.name)
@ -262,6 +263,7 @@ foo baz
for _, test := range tests { for _, test := range tests {
printer := &CustomColumnsPrinter{ printer := &CustomColumnsPrinter{
Columns: test.columns, Columns: test.columns,
Decoder: api.Codecs.UniversalDecoder(),
} }
buffer := &bytes.Buffer{} buffer := &bytes.Buffer{}
if err := printer.PrintObj(test.obj, buffer); err != nil { if err := printer.PrintObj(test.obj, buffer); err != nil {

View File

@ -113,7 +113,7 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
} }
case "custom-columns": case "custom-columns":
var err error var err error
if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument); err != nil { if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, api.Codecs.UniversalDecoder()); err != nil {
return nil, false, err return nil, false, err
} }
case "custom-columns-file": case "custom-columns-file":
@ -121,7 +121,7 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
if err != nil { if err != nil {
return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
} }
if printer, err = NewCustomColumnsPrinterFromTemplate(file); err != nil { if printer, err = NewCustomColumnsPrinterFromTemplate(file, api.Codecs.UniversalDecoder()); err != nil {
return nil, false, err return nil, false, err
} }
case "wide": case "wide":