From 970f103e2c079da98743db35e38fd411a64e2e04 Mon Sep 17 00:00:00 2001 From: wojtekt Date: Thu, 15 Aug 2019 22:02:33 +0200 Subject: [PATCH] Implement support for CacheableObject --- .../pkg/apis/meta/v1/unstructured/BUILD | 1 + .../pkg/apis/meta/v1/unstructured/helpers.go | 9 ++- .../apis/meta/v1/unstructured/helpers_test.go | 6 ++ .../k8s.io/apimachinery/pkg/runtime/codec.go | 9 +++ .../apimachinery/pkg/runtime/codec_test.go | 23 ++++++- .../pkg/runtime/serializer/json/BUILD | 1 + .../pkg/runtime/serializer/json/json.go | 7 ++ .../pkg/runtime/serializer/json/json_test.go | 10 +++ .../pkg/runtime/serializer/protobuf/BUILD | 16 +++-- .../runtime/serializer/protobuf/protobuf.go | 14 ++++ .../serializer/protobuf/protobuf_test.go | 68 +++++++++++++++++++ .../pkg/runtime/serializer/versioning/BUILD | 1 + .../serializer/versioning/versioning.go | 7 ++ .../serializer/versioning/versioning_test.go | 19 ++++++ .../apiserver/pkg/endpoints/discovery/util.go | 7 ++ .../k8s.io/cli-runtime/pkg/resource/scheme.go | 2 + 16 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf_test.go diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/BUILD b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/BUILD index ceeb848d2ba..47046794675 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/BUILD +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/BUILD @@ -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", diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go index 12c078025ea..9d37c17104b 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go @@ -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) diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers_test.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers_test.go index b2978b6f94d..948992aa943 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers_test.go @@ -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) +} diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/codec.go b/staging/src/k8s.io/apimachinery/pkg/runtime/codec.go index de0068ab1ad..0bccf9dd95b 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/codec.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/codec.go @@ -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() diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/codec_test.go b/staging/src/k8s.io/apimachinery/pkg/runtime/codec_test.go index ee0939a5706..9425db212a9 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/codec_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/codec_test.go @@ -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) +} diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/BUILD b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/BUILD index 5dfd2b27b91..e8304fc51b5 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/BUILD +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/BUILD @@ -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", ], ) diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go index 1871c9ac2ef..2fff77611cb 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go @@ -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 { diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json_test.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json_test.go index 3984cf143f7..f75f792c161 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json_test.go @@ -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 diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/BUILD b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/BUILD index 6008b387dce..06fc1ed5575 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/BUILD +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/BUILD @@ -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", + ], +) diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go index 3d408a09355..fe89120f6b5 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go @@ -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 diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf_test.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf_test.go new file mode 100644 index 00000000000..001b0f64786 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf_test.go @@ -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 +} diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/BUILD b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/BUILD index 3406307ebc8..9c206b42be4 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/BUILD +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/BUILD @@ -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", ], diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go index f7dd7983323..4e098d4a544 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go @@ -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) diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning_test.go b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning_test.go index d4ef5827806..7d4e98f4e92 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning_test.go @@ -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) +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/util.go b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/util.go index fa63b19d21e..2411a780d1c 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/util.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/util.go @@ -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 { diff --git a/staging/src/k8s.io/cli-runtime/pkg/resource/scheme.go b/staging/src/k8s.io/cli-runtime/pkg/resource/scheme.go index 94939a1c641..0a47d159628 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/resource/scheme.go +++ b/staging/src/k8s.io/cli-runtime/pkg/resource/scheme.go @@ -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) }