Skip to content

Commit 3230a0b

Browse files
Support Table and PartialObjectMetadata on watch
Clean up the code paths that lead to objects being transformed and output with negotiation. Remove some duplicate code that was not consistent. Now, watch will respond correctly to Table and PartialObjectMetadata requests. Add unit and integration tests. When transforming responses to Tables, only the first watch event for a given type will include the columns. Columns will not change unless the watch is restarted. Add a volume attachment printer and tighten up table validation error cases. Disable protobuf from table conversion because Tables don't have protobuf because they use `interface{}`
1 parent 3624c74 commit 3230a0b

File tree

42 files changed

+1261
-178
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1261
-178
lines changed

pkg/api/testing/fuzzer.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import (
2222
fuzz "github.com/google/gofuzz"
2323

2424
appsv1 "k8s.io/api/apps/v1"
25-
"k8s.io/api/core/v1"
25+
v1 "k8s.io/api/core/v1"
2626
apitesting "k8s.io/apimachinery/pkg/api/apitesting"
2727
"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
2828
genericfuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
29+
metafuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
2930
"k8s.io/apimachinery/pkg/runtime"
3031
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
3132
admissionregistrationfuzzer "k8s.io/kubernetes/pkg/apis/admissionregistration/fuzzer"
@@ -105,4 +106,5 @@ var FuzzerFuncs = fuzzer.MergeFuzzerFuncs(
105106
auditregistrationfuzzer.Funcs,
106107
storagefuzzer.Funcs,
107108
networkingfuzzer.Funcs,
109+
metafuzzer.Funcs,
108110
)

pkg/printers/humanreadable.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,17 @@ func (h *HumanReadablePrinter) PrintTable(obj runtime.Object, options PrintOptio
509509
return nil, results[1].Interface().(error)
510510
}
511511

512-
columns := handler.columnDefinitions
513-
if !options.Wide {
514-
columns = make([]metav1beta1.TableColumnDefinition, 0, len(handler.columnDefinitions))
515-
for i := range handler.columnDefinitions {
516-
if handler.columnDefinitions[i].Priority != 0 {
517-
continue
512+
var columns []metav1beta1.TableColumnDefinition
513+
if !options.NoHeaders {
514+
columns = handler.columnDefinitions
515+
if !options.Wide {
516+
columns = make([]metav1beta1.TableColumnDefinition, 0, len(handler.columnDefinitions))
517+
for i := range handler.columnDefinitions {
518+
if handler.columnDefinitions[i].Priority != 0 {
519+
continue
520+
}
521+
columns = append(columns, handler.columnDefinitions[i])
518522
}
519-
columns = append(columns, handler.columnDefinitions[i])
520523
}
521524
}
522525
table := &metav1beta1.Table{

pkg/printers/internalversion/printers.go

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,14 +430,14 @@ func AddHandlers(h printers.PrintHandler) {
430430
h.TableHandler(controllerRevisionColumnDefinition, printControllerRevision)
431431
h.TableHandler(controllerRevisionColumnDefinition, printControllerRevisionList)
432432

433-
resorceQuotaColumnDefinitions := []metav1beta1.TableColumnDefinition{
433+
resourceQuotaColumnDefinitions := []metav1beta1.TableColumnDefinition{
434434
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
435435
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
436436
{Name: "Request", Type: "string", Description: "Request represents a minimum amount of cpu/memory that a container may consume."},
437437
{Name: "Limit", Type: "string", Description: "Limits control the maximum amount of cpu/memory that a container may use independent of contention on the node."},
438438
}
439-
h.TableHandler(resorceQuotaColumnDefinitions, printResourceQuota)
440-
h.TableHandler(resorceQuotaColumnDefinitions, printResourceQuotaList)
439+
h.TableHandler(resourceQuotaColumnDefinitions, printResourceQuota)
440+
h.TableHandler(resourceQuotaColumnDefinitions, printResourceQuotaList)
441441

442442
priorityClassColumnDefinitions := []metav1beta1.TableColumnDefinition{
443443
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
@@ -456,6 +456,17 @@ func AddHandlers(h printers.PrintHandler) {
456456
h.TableHandler(runtimeClassColumnDefinitions, printRuntimeClass)
457457
h.TableHandler(runtimeClassColumnDefinitions, printRuntimeClassList)
458458

459+
volumeAttachmentColumnDefinitions := []metav1beta1.TableColumnDefinition{
460+
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
461+
{Name: "Attacher", Type: "string", Format: "name", Description: storagev1.VolumeAttachmentSpec{}.SwaggerDoc()["attacher"]},
462+
{Name: "PV", Type: "string", Description: storagev1.VolumeAttachmentSource{}.SwaggerDoc()["persistentVolumeName"]},
463+
{Name: "Node", Type: "string", Description: storagev1.VolumeAttachmentSpec{}.SwaggerDoc()["nodeName"]},
464+
{Name: "Attached", Type: "boolean", Description: storagev1.VolumeAttachmentStatus{}.SwaggerDoc()["attached"]},
465+
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
466+
}
467+
h.TableHandler(volumeAttachmentColumnDefinitions, printVolumeAttachment)
468+
h.TableHandler(volumeAttachmentColumnDefinitions, printVolumeAttachmentList)
469+
459470
AddDefaultHandlers(h)
460471
}
461472

@@ -1996,6 +2007,34 @@ func printRuntimeClassList(list *nodeapi.RuntimeClassList, options printers.Prin
19962007
rows := make([]metav1beta1.TableRow, 0, len(list.Items))
19972008
for i := range list.Items {
19982009
r, err := printRuntimeClass(&list.Items[i], options)
2010+
2011+
if err != nil {
2012+
return nil, err
2013+
}
2014+
rows = append(rows, r...)
2015+
}
2016+
return rows, nil
2017+
}
2018+
2019+
func printVolumeAttachment(obj *storage.VolumeAttachment, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
2020+
row := metav1beta1.TableRow{
2021+
Object: runtime.RawExtension{Object: obj},
2022+
}
2023+
2024+
name := obj.Name
2025+
pvName := ""
2026+
if obj.Spec.Source.PersistentVolumeName != nil {
2027+
pvName = *obj.Spec.Source.PersistentVolumeName
2028+
}
2029+
row.Cells = append(row.Cells, name, obj.Spec.Attacher, pvName, obj.Spec.NodeName, obj.Status.Attached, translateTimestampSince(obj.CreationTimestamp))
2030+
2031+
return []metav1beta1.TableRow{row}, nil
2032+
}
2033+
2034+
func printVolumeAttachmentList(list *storage.VolumeAttachmentList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
2035+
rows := make([]metav1beta1.TableRow, 0, len(list.Items))
2036+
for i := range list.Items {
2037+
r, err := printVolumeAttachment(&list.Items[i], options)
19992038
if err != nil {
20002039
return nil, err
20012040
}

pkg/printers/storage/storage.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package storage
1818

1919
import (
2020
"context"
21+
"fmt"
2122

2223
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
2324
"k8s.io/apimachinery/pkg/runtime"
@@ -29,5 +30,16 @@ type TableConvertor struct {
2930
}
3031

3132
func (c TableConvertor) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) {
32-
return c.TablePrinter.PrintTable(obj, printers.PrintOptions{Wide: true})
33+
noHeaders := false
34+
if tableOptions != nil {
35+
switch t := tableOptions.(type) {
36+
case *metav1beta1.TableOptions:
37+
if t != nil {
38+
noHeaders = t.NoHeaders
39+
}
40+
default:
41+
return nil, fmt.Errorf("unrecognized type %T for table options, can't display tabular output", tableOptions)
42+
}
43+
}
44+
return c.TablePrinter.PrintTable(obj, printers.PrintOptions{Wide: true, NoHeaders: noHeaders})
3345
}

pkg/registry/storage/volumeattachment/storage/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ go_library(
77
visibility = ["//visibility:public"],
88
deps = [
99
"//pkg/apis/storage:go_default_library",
10+
"//pkg/printers:go_default_library",
11+
"//pkg/printers/internalversion:go_default_library",
12+
"//pkg/printers/storage:go_default_library",
1013
"//pkg/registry/storage/volumeattachment:go_default_library",
1114
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
1215
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",

pkg/registry/storage/volumeattachment/storage/storage.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import (
2525
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
2626
"k8s.io/apiserver/pkg/registry/rest"
2727
storageapi "k8s.io/kubernetes/pkg/apis/storage"
28+
"k8s.io/kubernetes/pkg/printers"
29+
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
30+
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
2831
"k8s.io/kubernetes/pkg/registry/storage/volumeattachment"
2932
)
3033

@@ -50,6 +53,8 @@ func NewStorage(optsGetter generic.RESTOptionsGetter) *VolumeAttachmentStorage {
5053
UpdateStrategy: volumeattachment.Strategy,
5154
DeleteStrategy: volumeattachment.Strategy,
5255
ReturnDeletedObject: true,
56+
57+
TableConvertor: printerstorage.TableConvertor{TablePrinter: printers.NewTablePrinter().With(printersinternal.AddHandlers)},
5358
}
5459
options := &generic.StoreOptions{RESTOptions: optsGetter}
5560
if err := store.CompleteWithOptions(options); err != nil {

staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/BUILD

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,11 @@ go_test(
3636
name = "go_default_test",
3737
srcs = ["tableconvertor_test.go"],
3838
embed = [":go_default_library"],
39+
deps = [
40+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
41+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
42+
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
43+
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
44+
"//staging/src/k8s.io/client-go/util/jsonpath:go_default_library",
45+
],
3946
)

staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,13 @@ type convertor struct {
7676
}
7777

7878
func (c *convertor) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) {
79-
table := &metav1beta1.Table{
80-
ColumnDefinitions: c.headers,
79+
table := &metav1beta1.Table{}
80+
opt, ok := tableOptions.(*metav1beta1.TableOptions)
81+
noHeaders := ok && opt != nil && opt.NoHeaders
82+
if !noHeaders {
83+
table.ColumnDefinitions = c.headers
8184
}
85+
8286
if m, err := meta.ListAccessor(obj); err == nil {
8387
table.ResourceVersion = m.GetResourceVersion()
8488
table.SelfLink = m.GetSelfLink()

staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,17 @@ limitations under the License.
1717
package tableconvertor
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"reflect"
2223
"testing"
2324
"time"
25+
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
28+
"k8s.io/apimachinery/pkg/runtime"
29+
"k8s.io/apimachinery/pkg/util/diff"
30+
"k8s.io/client-go/util/jsonpath"
2431
)
2532

2633
func Test_cellForJSONValue(t *testing.T) {
@@ -66,3 +73,149 @@ func Test_cellForJSONValue(t *testing.T) {
6673
})
6774
}
6875
}
76+
77+
func Test_convertor_ConvertToTable(t *testing.T) {
78+
type fields struct {
79+
headers []metav1beta1.TableColumnDefinition
80+
additionalColumns []*jsonpath.JSONPath
81+
}
82+
type args struct {
83+
ctx context.Context
84+
obj runtime.Object
85+
tableOptions runtime.Object
86+
}
87+
tests := []struct {
88+
name string
89+
fields fields
90+
args args
91+
want *metav1beta1.Table
92+
wantErr bool
93+
}{
94+
{
95+
name: "Return table for object",
96+
fields: fields{
97+
headers: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
98+
},
99+
args: args{
100+
obj: &metav1beta1.PartialObjectMetadata{
101+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
102+
},
103+
tableOptions: nil,
104+
},
105+
want: &metav1beta1.Table{
106+
ColumnDefinitions: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
107+
Rows: []metav1beta1.TableRow{
108+
{
109+
Cells: []interface{}{"blah"},
110+
Object: runtime.RawExtension{
111+
Object: &metav1beta1.PartialObjectMetadata{
112+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
113+
},
114+
},
115+
},
116+
},
117+
},
118+
},
119+
{
120+
name: "Return table for list",
121+
fields: fields{
122+
headers: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
123+
},
124+
args: args{
125+
obj: &metav1beta1.PartialObjectMetadataList{
126+
Items: []*metav1beta1.PartialObjectMetadata{
127+
{ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))}},
128+
{ObjectMeta: metav1.ObjectMeta{Name: "blah-2", CreationTimestamp: metav1.NewTime(time.Unix(2, 0))}},
129+
},
130+
},
131+
tableOptions: nil,
132+
},
133+
want: &metav1beta1.Table{
134+
ColumnDefinitions: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
135+
Rows: []metav1beta1.TableRow{
136+
{
137+
Cells: []interface{}{"blah"},
138+
Object: runtime.RawExtension{
139+
Object: &metav1beta1.PartialObjectMetadata{
140+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
141+
},
142+
},
143+
},
144+
{
145+
Cells: []interface{}{"blah-2"},
146+
Object: runtime.RawExtension{
147+
Object: &metav1beta1.PartialObjectMetadata{
148+
ObjectMeta: metav1.ObjectMeta{Name: "blah-2", CreationTimestamp: metav1.NewTime(time.Unix(2, 0))},
149+
},
150+
},
151+
},
152+
},
153+
},
154+
},
155+
{
156+
name: "Accept TableOptions",
157+
fields: fields{
158+
headers: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
159+
},
160+
args: args{
161+
obj: &metav1beta1.PartialObjectMetadata{
162+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
163+
},
164+
tableOptions: &metav1beta1.TableOptions{},
165+
},
166+
want: &metav1beta1.Table{
167+
ColumnDefinitions: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
168+
Rows: []metav1beta1.TableRow{
169+
{
170+
Cells: []interface{}{"blah"},
171+
Object: runtime.RawExtension{
172+
Object: &metav1beta1.PartialObjectMetadata{
173+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
174+
},
175+
},
176+
},
177+
},
178+
},
179+
},
180+
{
181+
name: "Omit headers from TableOptions",
182+
fields: fields{
183+
headers: []metav1beta1.TableColumnDefinition{{Name: "name", Type: "string"}},
184+
},
185+
args: args{
186+
obj: &metav1beta1.PartialObjectMetadata{
187+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
188+
},
189+
tableOptions: &metav1beta1.TableOptions{NoHeaders: true},
190+
},
191+
want: &metav1beta1.Table{
192+
Rows: []metav1beta1.TableRow{
193+
{
194+
Cells: []interface{}{"blah"},
195+
Object: runtime.RawExtension{
196+
Object: &metav1beta1.PartialObjectMetadata{
197+
ObjectMeta: metav1.ObjectMeta{Name: "blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0))},
198+
},
199+
},
200+
},
201+
},
202+
},
203+
},
204+
}
205+
for _, tt := range tests {
206+
t.Run(tt.name, func(t *testing.T) {
207+
c := &convertor{
208+
headers: tt.fields.headers,
209+
additionalColumns: tt.fields.additionalColumns,
210+
}
211+
got, err := c.ConvertToTable(tt.args.ctx, tt.args.obj, tt.args.tableOptions)
212+
if (err != nil) != tt.wantErr {
213+
t.Errorf("convertor.ConvertToTable() error = %v, wantErr %v", err, tt.wantErr)
214+
return
215+
}
216+
if !reflect.DeepEqual(got, tt.want) {
217+
t.Errorf("convertor.ConvertToTable() = %s", diff.ObjectReflectDiff(tt.want, got))
218+
}
219+
})
220+
}
221+
}

0 commit comments

Comments
 (0)