Implement support for CacheableObject

This commit is contained in:
wojtekt 2019-08-15 22:02:33 +02:00
parent 1dd43724ce
commit 970f103e2c
16 changed files with 193 additions and 7 deletions

View File

@ -21,6 +21,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/testing:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",

View File

@ -352,7 +352,14 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind,
return obj, &gvk, nil
}
func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
func (s unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (unstructuredJSONScheme) doEncode(obj runtime.Object, w io.Writer) error {
switch t := obj.(type) {
case *Unstructured:
return json.NewEncoder(w).Encode(t.Object)

View File

@ -21,6 +21,8 @@ import (
"sync"
"testing"
runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
"github.com/stretchr/testify/assert"
)
@ -159,3 +161,7 @@ func TestNestedFieldCopy(t *testing.T) {
assert.Nil(t, err)
assert.Nil(t, res)
}
func TestCacheableObject(t *testing.T) {
runtimetesting.CacheableObjectTest(t, UnstructuredJSONScheme)
}

View File

@ -107,6 +107,8 @@ var _ Serializer = NoopEncoder{}
const noopEncoderIdentifier Identifier = "noop"
func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
// There is no need to handle runtime.CacheableObject, as we don't
// process the obj at all.
return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
}
@ -231,6 +233,13 @@ func identifier(e Encoder) Identifier {
}
func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
if co, ok := obj.(CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, stream)
}
return s.doEncode(obj, stream)
}
func (s base64Serializer) doEncode(obj Object, stream io.Writer) error {
e := base64.NewEncoder(base64.StdEncoding, stream)
err := s.Encoder.Encode(obj, e)
e.Close()

View File

@ -14,12 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
package runtime_test
import (
"io"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
)
func gv(group, version string) schema.GroupVersion {
@ -69,7 +72,7 @@ func TestCoercingMultiGroupVersioner(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
v := NewCoercingMultiGroupVersioner(tc.target, tc.preferredKinds...)
v := runtime.NewCoercingMultiGroupVersioner(tc.target, tc.preferredKinds...)
kind, ok := v.KindForGroupVersionKinds(tc.kinds)
if !ok {
t.Error("got no kind")
@ -83,3 +86,19 @@ func TestCoercingMultiGroupVersioner(t *testing.T) {
})
}
}
type mockEncoder struct{}
func (m *mockEncoder) Encode(obj runtime.Object, w io.Writer) error {
_, err := w.Write([]byte("mock-result"))
return err
}
func (m *mockEncoder) Identifier() runtime.Identifier {
return runtime.Identifier("mock-identifier")
}
func TestCacheableObject(t *testing.T) {
serializer := runtime.NewBase64Serializer(&mockEncoder{}, nil)
runtimetesting.CacheableObjectTest(t, serializer)
}

View File

@ -16,6 +16,7 @@ go_test(
deps = [
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/testing:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
],
)

View File

@ -304,6 +304,13 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
// Encode serializes the provided object to the given writer.
func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (s *Serializer) doEncode(obj runtime.Object, w io.Writer) error {
if s.options.Yaml {
json, err := caseSensitiveJsonIterator.Marshal(obj)
if err != nil {

View File

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
"k8s.io/apimachinery/pkg/util/diff"
)
@ -456,6 +457,15 @@ func TestDecode(t *testing.T) {
}
}
func TestCacheableObject(t *testing.T) {
gvk := schema.GroupVersionKind{Group: "group", Version: "version", Kind: "MockCacheableObject"}
creater := &mockCreater{obj: &runtimetesting.MockCacheableObject{}}
typer := &mockTyper{gvk: &gvk}
serializer := json.NewSerializer(json.DefaultMetaFactory, creater, typer, false)
runtimetesting.CacheableObjectTest(t, serializer)
}
type mockCreater struct {
apiVersion string
kind string

View File

@ -1,9 +1,6 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
@ -35,3 +32,14 @@ filegroup(
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["protobuf_test.go"],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/testing:go_default_library",
],
)

View File

@ -178,6 +178,13 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
// Encode serializes the provided object to the given writer.
func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (s *Serializer) doEncode(obj runtime.Object, w io.Writer) error {
prefixSize := uint64(len(s.prefix))
var unk runtime.Unknown
@ -428,6 +435,13 @@ func unmarshalToObject(typer runtime.ObjectTyper, creater runtime.ObjectCreater,
// Encode serializes the provided object to the given writer. Overrides is ignored.
func (s *RawSerializer) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (s *RawSerializer) doEncode(obj runtime.Object, w io.Writer) error {
switch t := obj.(type) {
case bufferedReverseMarshaller:
// this path performs a single allocation during write but requires the caller to implement

View File

@ -0,0 +1,68 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protobuf
import (
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
)
func TestCacheableObject(t *testing.T) {
gvk := schema.GroupVersionKind{Group: "group", Version: "version", Kind: "MockCacheableObject"}
creater := &mockCreater{obj: &runtimetesting.MockCacheableObject{}}
typer := &mockTyper{gvk: &gvk}
encoders := []runtime.Encoder{
NewSerializer(creater, typer),
NewRawSerializer(creater, typer),
}
for _, encoder := range encoders {
runtimetesting.CacheableObjectTest(t, encoder)
}
}
type mockCreater struct {
apiVersion string
kind string
err error
obj runtime.Object
}
func (c *mockCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind
return c.obj, c.err
}
type mockTyper struct {
gvk *schema.GroupVersionKind
err error
}
func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
if t.gvk == nil {
return nil, false, t.err
}
return []schema.GroupVersionKind{*t.gvk}, false, t.err
}
func (t *mockTyper) Recognizes(_ schema.GroupVersionKind) bool {
return false
}

View File

@ -17,6 +17,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/testing:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],

View File

@ -196,6 +196,13 @@ func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into ru
// Encode ensures the provided object is output in the appropriate group and version, invoking
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(c.Identifier(), c.doEncode, w)
}
return c.doEncode(obj, w)
}
func (c *codec) doEncode(obj runtime.Object, w io.Writer) error {
switch obj := obj.(type) {
case *runtime.Unknown:
return c.encoder.Encode(obj, w)

View File

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
"k8s.io/apimachinery/pkg/util/diff"
)
@ -376,6 +377,10 @@ func (s *mockSerializer) Encode(obj runtime.Object, w io.Writer) error {
return s.err
}
func (s *mockSerializer) Identifier() runtime.Identifier {
return runtime.Identifier("mock")
}
type mockCreater struct {
err error
obj runtime.Object
@ -424,3 +429,17 @@ func TestDirectCodecEncode(t *testing.T) {
t.Errorf("expected group to be %v, got %v", e, a)
}
}
func TestCacheableObject(t *testing.T) {
gvk1 := schema.GroupVersionKind{Group: "group", Version: "version1", Kind: "MockCacheableObject"}
gvk2 := schema.GroupVersionKind{Group: "group", Version: "version2", Kind: "MockCacheableObject"}
encoder := NewCodec(
&mockSerializer{}, &mockSerializer{},
&mockConvertor{}, nil,
&mockTyper{gvks: []schema.GroupVersionKind{gvk1, gvk2}}, nil,
gvk1.GroupVersion(), gvk2.GroupVersion(),
"TestCacheableObject")
runtimetesting.CacheableObjectTest(t, encoder)
}

View File

@ -64,6 +64,13 @@ func identifier(e runtime.Encoder) runtime.Identifier {
}
func (c stripVersionEncoder) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(c.Identifier(), c.doEncode, w)
}
return c.doEncode(obj, w)
}
func (c stripVersionEncoder) doEncode(obj runtime.Object, w io.Writer) error {
buf := bytes.NewBuffer([]byte{})
err := c.encoder.Encode(obj, buf)
if err != nil {

View File

@ -55,6 +55,8 @@ func (dynamicCodec) Decode(data []byte, gvk *schema.GroupVersionKind, obj runtim
}
func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error {
// There is no need to handle runtime.CacheableObject, as we only
// fallback to other encoders here.
return unstructured.UnstructuredJSONScheme.Encode(obj, w)
}