Implement Encoder.Identifier() method

This commit is contained in:
wojtekt 2019-08-14 16:23:03 +02:00
parent 36300c8c9f
commit cd4215ad8b
13 changed files with 178 additions and 8 deletions

View File

@ -44,6 +44,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
], ],
) )

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
"k8s.io/klog"
) )
// NestedFieldCopy returns a deep copy of the value of a nested field. // NestedFieldCopy returns a deep copy of the value of a nested field.
@ -329,6 +330,8 @@ var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
type unstructuredJSONScheme struct{} type unstructuredJSONScheme struct{}
const unstructuredJSONSchemeIdentifier runtime.Identifier = "unstructuredJSON"
func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
var err error var err error
if obj != nil { if obj != nil {
@ -373,6 +376,11 @@ func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
} }
} }
// Identifier implements runtime.Encoder interface.
func (unstructuredJSONScheme) Identifier() runtime.Identifier {
return unstructuredJSONSchemeIdentifier
}
func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) { func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
type detector struct { type detector struct {
Items gojson.RawMessage Items gojson.RawMessage
@ -461,16 +469,28 @@ func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList
} }
type jsonFallbackEncoder struct { type jsonFallbackEncoder struct {
encoder runtime.Encoder encoder runtime.Encoder
identifier runtime.Identifier
} }
func NewJSONFallbackEncoder(encoder runtime.Encoder) runtime.Encoder { func NewJSONFallbackEncoder(encoder runtime.Encoder) runtime.Encoder {
result := map[string]string{
"name": "fallback",
"base": string(encoder.Identifier()),
}
identifier, err := gojson.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for jsonFallbackEncoder: %v", err)
}
return &jsonFallbackEncoder{ return &jsonFallbackEncoder{
encoder: encoder, encoder: encoder,
identifier: runtime.Identifier(identifier),
} }
} }
func (c *jsonFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error { func (c *jsonFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
// There is no need to handle runtime.CacheableObject, as we only
// fallback to other encoders here.
err := c.encoder.Encode(obj, w) err := c.encoder.Encode(obj, w)
if runtime.IsNotRegisteredError(err) { if runtime.IsNotRegisteredError(err) {
switch obj.(type) { switch obj.(type) {
@ -480,3 +500,8 @@ func (c *jsonFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
} }
return err return err
} }
// Identifier implements runtime.Encoder interface.
func (c *jsonFallbackEncoder) Identifier() runtime.Identifier {
return c.identifier
}

View File

@ -104,10 +104,17 @@ type NoopEncoder struct {
var _ Serializer = NoopEncoder{} var _ Serializer = NoopEncoder{}
const noopEncoderIdentifier Identifier = "noop"
func (n NoopEncoder) Encode(obj Object, w io.Writer) error { func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder)) return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
} }
// Identifier implements runtime.Encoder interface.
func (n NoopEncoder) Identifier() Identifier {
return noopEncoderIdentifier
}
// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding. // NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
type NoopDecoder struct { type NoopDecoder struct {
Encoder Encoder
@ -197,10 +204,30 @@ func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (u
type base64Serializer struct { type base64Serializer struct {
Encoder Encoder
Decoder Decoder
identifier Identifier
} }
func NewBase64Serializer(e Encoder, d Decoder) Serializer { func NewBase64Serializer(e Encoder, d Decoder) Serializer {
return &base64Serializer{e, d} return &base64Serializer{
Encoder: e,
Decoder: d,
identifier: identifier(e),
}
}
func identifier(e Encoder) Identifier {
result := map[string]string{
"name": "base64",
}
if e != nil {
result["encoder"] = string(e.Identifier())
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for base64Serializer: %v", err)
}
return Identifier(identifier)
} }
func (s base64Serializer) Encode(obj Object, stream io.Writer) error { func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
@ -210,6 +237,11 @@ func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
return err return err
} }
// Identifier implements runtime.Encoder interface.
func (s base64Serializer) Identifier() Identifier {
return s.identifier
}
func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) { func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
out := make([]byte, base64.StdEncoding.DecodedLen(len(data))) out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
n, err := base64.StdEncoding.Decode(out, data) n, err := base64.StdEncoding.Decode(out, data)

View File

@ -36,6 +36,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library", "//vendor/github.com/json-iterator/go:go_default_library",
"//vendor/github.com/modern-go/reflect2:go_default_library", "//vendor/github.com/modern-go/reflect2:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
"//vendor/sigs.k8s.io/yaml:go_default_library", "//vendor/sigs.k8s.io/yaml:go_default_library",
], ],
) )

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer" "k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
"k8s.io/apimachinery/pkg/util/framer" "k8s.io/apimachinery/pkg/util/framer"
utilyaml "k8s.io/apimachinery/pkg/util/yaml" utilyaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/klog"
) )
// NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer // NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer
@ -53,13 +54,28 @@ func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer ru
// and are immutable. // and are immutable.
func NewSerializerWithOptions(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options SerializerOptions) *Serializer { func NewSerializerWithOptions(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options SerializerOptions) *Serializer {
return &Serializer{ return &Serializer{
meta: meta, meta: meta,
creater: creater, creater: creater,
typer: typer, typer: typer,
options: options, options: options,
identifier: identifier(options),
} }
} }
// identifier computes Identifier of Encoder based on the given options.
func identifier(options SerializerOptions) runtime.Identifier {
result := map[string]string{
"name": "json",
"yaml": strconv.FormatBool(options.Yaml),
"pretty": strconv.FormatBool(options.Pretty),
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for json Serializer: %v", err)
}
return runtime.Identifier(identifier)
}
// SerializerOptions holds the options which are used to configure a JSON/YAML serializer. // SerializerOptions holds the options which are used to configure a JSON/YAML serializer.
// example: // example:
// (1) To configure a JSON serializer, set `Yaml` to `false`. // (1) To configure a JSON serializer, set `Yaml` to `false`.
@ -85,6 +101,8 @@ type Serializer struct {
options SerializerOptions options SerializerOptions
creater runtime.ObjectCreater creater runtime.ObjectCreater
typer runtime.ObjectTyper typer runtime.ObjectTyper
identifier runtime.Identifier
} }
// Serializer implements Serializer // Serializer implements Serializer
@ -311,6 +329,11 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
return encoder.Encode(obj) return encoder.Encode(obj)
} }
// Identifier implements runtime.Encoder interface.
func (s *Serializer) Identifier() runtime.Identifier {
return s.identifier
}
// RecognizesData implements the RecognizingDecoder interface. // RecognizesData implements the RecognizingDecoder interface.
func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error) { func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error) {
if s.options.Yaml { if s.options.Yaml {

View File

@ -86,6 +86,8 @@ type Serializer struct {
var _ runtime.Serializer = &Serializer{} var _ runtime.Serializer = &Serializer{}
var _ recognizer.RecognizingDecoder = &Serializer{} var _ recognizer.RecognizingDecoder = &Serializer{}
const serializerIdentifier runtime.Identifier = "protobuf"
// Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default // Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default
// gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, // gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown,
// the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will // the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will
@ -245,6 +247,11 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
} }
} }
// Identifier implements runtime.Encoder interface.
func (s *Serializer) Identifier() runtime.Identifier {
return serializerIdentifier
}
// RecognizesData implements the RecognizingDecoder interface. // RecognizesData implements the RecognizingDecoder interface.
func (s *Serializer) RecognizesData(peek io.Reader) (bool, bool, error) { func (s *Serializer) RecognizesData(peek io.Reader) (bool, bool, error) {
prefix := make([]byte, 4) prefix := make([]byte, 4)
@ -321,6 +328,8 @@ type RawSerializer struct {
var _ runtime.Serializer = &RawSerializer{} var _ runtime.Serializer = &RawSerializer{}
const rawSerializerIdentifier runtime.Identifier = "raw-protobuf"
// Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default // Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default
// gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, // gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown,
// the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will // the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will
@ -460,6 +469,11 @@ func (s *RawSerializer) Encode(obj runtime.Object, w io.Writer) error {
} }
} }
// Identifier implements runtime.Encoder interface.
func (s *RawSerializer) Identifier() runtime.Identifier {
return rawSerializerIdentifier
}
var LengthDelimitedFramer = lengthDelimitedFramer{} var LengthDelimitedFramer = lengthDelimitedFramer{}
type lengthDelimitedFramer struct{} type lengthDelimitedFramer struct{}

View File

@ -31,6 +31,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//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:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
], ],
) )

View File

@ -17,12 +17,14 @@ limitations under the License.
package versioning package versioning
import ( import (
"encoding/json"
"io" "io"
"reflect" "reflect"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog"
) )
// NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme. // NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme.
@ -62,6 +64,8 @@ func NewCodec(
encodeVersion: encodeVersion, encodeVersion: encodeVersion,
decodeVersion: decodeVersion, decodeVersion: decodeVersion,
identifier: identifier(encodeVersion, encoder),
originalSchemeName: originalSchemeName, originalSchemeName: originalSchemeName,
} }
return internal return internal
@ -78,10 +82,30 @@ type codec struct {
encodeVersion runtime.GroupVersioner encodeVersion runtime.GroupVersioner
decodeVersion runtime.GroupVersioner decodeVersion runtime.GroupVersioner
identifier runtime.Identifier
// originalSchemeName is optional, but when filled in it holds the name of the scheme from which this codec originates // originalSchemeName is optional, but when filled in it holds the name of the scheme from which this codec originates
originalSchemeName string originalSchemeName string
} }
// identifier computes Identifier of Encoder based on codec parameters.
func identifier(encodeGV runtime.GroupVersioner, encoder runtime.Encoder) runtime.Identifier {
result := map[string]string{
"name": "versioning",
}
if encodeGV != nil {
result["encodeGV"] = encodeGV.Identifier()
}
if encoder != nil {
result["encoder"] = string(encoder.Identifier())
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for codec: %v", err)
}
return runtime.Identifier(identifier)
}
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is // Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an // successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
// into that matches the serialized version. // into that matches the serialized version.
@ -230,3 +254,8 @@ func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object // Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
return c.encoder.Encode(out, w) return c.encoder.Encode(out, w)
} }
// Identifier implements runtime.Encoder interface.
func (c *codec) Identifier() runtime.Identifier {
return c.identifier
}

View File

@ -46,6 +46,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
], ],
) )

View File

@ -18,10 +18,12 @@ package discovery
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog"
) )
const APIGroupPrefix = "/apis" const APIGroupPrefix = "/apis"
@ -36,6 +38,29 @@ func keepUnversioned(group string) bool {
type stripVersionEncoder struct { type stripVersionEncoder struct {
encoder runtime.Encoder encoder runtime.Encoder
serializer runtime.Serializer serializer runtime.Serializer
identifier runtime.Identifier
}
func newStripVersionEncoder(e runtime.Encoder, s runtime.Serializer) runtime.Encoder {
return stripVersionEncoder{
encoder: e,
serializer: s,
identifier: identifier(e),
}
}
func identifier(e runtime.Encoder) runtime.Identifier {
result := map[string]string{
"name": "stripVersion",
}
if e != nil {
result["encoder"] = string(e.Identifier())
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for stripVersionEncoder: %v", err)
}
return runtime.Identifier(identifier)
} }
func (c stripVersionEncoder) Encode(obj runtime.Object, w io.Writer) error { func (c stripVersionEncoder) Encode(obj runtime.Object, w io.Writer) error {
@ -54,6 +79,11 @@ func (c stripVersionEncoder) Encode(obj runtime.Object, w io.Writer) error {
return c.serializer.Encode(roundTrippedObj, w) return c.serializer.Encode(roundTrippedObj, w)
} }
// Identifier implements runtime.Encoder interface.
func (c stripVersionEncoder) Identifier() runtime.Identifier {
return c.identifier
}
// stripVersionNegotiatedSerializer will return stripVersionEncoder when // stripVersionNegotiatedSerializer will return stripVersionEncoder when
// EncoderForVersion is called. See comments for stripVersionEncoder. // EncoderForVersion is called. See comments for stripVersionEncoder.
type stripVersionNegotiatedSerializer struct { type stripVersionNegotiatedSerializer struct {
@ -69,5 +99,5 @@ func (n stripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Enco
panic(fmt.Sprintf("Unable to extract serializer from %#v", encoder)) panic(fmt.Sprintf("Unable to extract serializer from %#v", encoder))
} }
versioned := n.NegotiatedSerializer.EncoderForVersion(encoder, gv) versioned := n.NegotiatedSerializer.EncoderForVersion(encoder, gv)
return stripVersionEncoder{versioned, serializer} return newStripVersionEncoder(versioned, serializer)
} }

View File

@ -287,6 +287,10 @@ func (e *fakeEncoder) Encode(obj runtime.Object, w io.Writer) error {
return err return err
} }
func (e *fakeEncoder) Identifier() runtime.Identifier {
return runtime.Identifier("fake")
}
func gzipContent(data []byte, level int) []byte { func gzipContent(data []byte, level int) []byte {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
gw, err := gzip.NewWriterLevel(buf, level) gw, err := gzip.NewWriterLevel(buf, level)

View File

@ -58,6 +58,11 @@ func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error {
return unstructured.UnstructuredJSONScheme.Encode(obj, w) return unstructured.UnstructuredJSONScheme.Encode(obj, w)
} }
// Identifier implements runtime.Encoder interface.
func (dynamicCodec) Identifier() runtime.Identifier {
return unstructured.UnstructuredJSONScheme.Identifier()
}
// UnstructuredPlusDefaultContentConfig returns a rest.ContentConfig for dynamic types. It includes enough codecs to act as a "normal" // UnstructuredPlusDefaultContentConfig returns a rest.ContentConfig for dynamic types. It includes enough codecs to act as a "normal"
// serializer for the rest.client with options, status and the like. // serializer for the rest.client with options, status and the like.
func UnstructuredPlusDefaultContentConfig() rest.ContentConfig { func UnstructuredPlusDefaultContentConfig() rest.ContentConfig {

View File

@ -190,6 +190,10 @@ func (c *fakeCodec) Encode(obj runtime.Object, stream io.Writer) error {
return nil return nil
} }
func (c *fakeCodec) Identifier() runtime.Identifier {
return runtime.Identifier("fake")
}
type fakeRoundTripper struct{} type fakeRoundTripper struct{}
func (r *fakeRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { func (r *fakeRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {