mirror of
https://github.com/kubernetes/sample-controller.git
synced 2026-04-12 00:00:26 +08:00
Merge pull request #65904 from deads2k/api-02-trackscheme
Automatic merge from submit-queue (batch tested with PRs 65946, 65904, 65913, 65906, 65920). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. track schemes by name for error reporting Getting an error message about a type not being in the scheme is hard to fix if you don't know which scheme is failing. This adds a name to the scheme which can be set during creation or can be set based on the calling stack. If you use the old constructor a name is generated for you based on the stack. Something like "k8s.io/client-go/dynamic/scheme.go:28" for instance. Also moves a typer to its point of use. This was debt from previous refactors which I noticed going through. @kubernetes/sig-api-machinery-misc @sttts ```release-note NONE ``` Kubernetes-commit: 8e2fdb32bc84103b15310a221a375470bf567bdc
This commit is contained in:
+2
-2
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Defines conversions between generic types and structs to map query strings
|
||||
// Package runtime defines conversions between generic types and structs to map query strings
|
||||
// to struct objects.
|
||||
package runtime
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
)
|
||||
|
||||
// DefaultFieldSelectorConversion auto-accepts metav1 values for name and namespace.
|
||||
// DefaultMetaV1FieldSelectorConversion auto-accepts metav1 values for name and namespace.
|
||||
// A cluster scoped resource specifying namespace empty works fine and specifying a particular
|
||||
// namespace will return no results, as expected.
|
||||
func DefaultMetaV1FieldSelectorConversion(label, value string) (string, string, error) {
|
||||
|
||||
-1
@@ -41,5 +41,4 @@ limitations under the License.
|
||||
//
|
||||
// As a bonus, a few common types useful from all api objects and versions
|
||||
// are provided in types.go.
|
||||
|
||||
package runtime
|
||||
|
||||
+14
-14
@@ -31,7 +31,7 @@ type encodable struct {
|
||||
|
||||
func (e encodable) GetObjectKind() schema.ObjectKind { return e.obj.GetObjectKind() }
|
||||
func (e encodable) DeepCopyObject() Object {
|
||||
var out encodable = e
|
||||
out := e
|
||||
out.obj = e.obj.DeepCopyObject()
|
||||
copy(out.versions, e.versions)
|
||||
return out
|
||||
@@ -46,14 +46,14 @@ func NewEncodable(e Encoder, obj Object, versions ...schema.GroupVersion) Object
|
||||
return encodable{e, obj, versions}
|
||||
}
|
||||
|
||||
func (re encodable) UnmarshalJSON(in []byte) error {
|
||||
func (e encodable) UnmarshalJSON(in []byte) error {
|
||||
return errors.New("runtime.encodable cannot be unmarshalled from JSON")
|
||||
}
|
||||
|
||||
// Marshal may get called on pointers or values, so implement MarshalJSON on value.
|
||||
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
|
||||
func (re encodable) MarshalJSON() ([]byte, error) {
|
||||
return Encode(re.E, re.obj)
|
||||
func (e encodable) MarshalJSON() ([]byte, error) {
|
||||
return Encode(e.E, e.obj)
|
||||
}
|
||||
|
||||
// NewEncodableList creates an object that will be encoded with the provided codec on demand.
|
||||
@@ -70,28 +70,28 @@ func NewEncodableList(e Encoder, objects []Object, versions ...schema.GroupVersi
|
||||
return out
|
||||
}
|
||||
|
||||
func (re *Unknown) UnmarshalJSON(in []byte) error {
|
||||
if re == nil {
|
||||
func (e *Unknown) UnmarshalJSON(in []byte) error {
|
||||
if e == nil {
|
||||
return errors.New("runtime.Unknown: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
re.TypeMeta = TypeMeta{}
|
||||
re.Raw = append(re.Raw[0:0], in...)
|
||||
re.ContentEncoding = ""
|
||||
re.ContentType = ContentTypeJSON
|
||||
e.TypeMeta = TypeMeta{}
|
||||
e.Raw = append(e.Raw[0:0], in...)
|
||||
e.ContentEncoding = ""
|
||||
e.ContentType = ContentTypeJSON
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal may get called on pointers or values, so implement MarshalJSON on value.
|
||||
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
|
||||
func (re Unknown) MarshalJSON() ([]byte, error) {
|
||||
func (e Unknown) MarshalJSON() ([]byte, error) {
|
||||
// If ContentType is unset, we assume this is JSON.
|
||||
if re.ContentType != "" && re.ContentType != ContentTypeJSON {
|
||||
if e.ContentType != "" && e.ContentType != ContentTypeJSON {
|
||||
return nil, errors.New("runtime.Unknown: MarshalJSON on non-json data")
|
||||
}
|
||||
if re.Raw == nil {
|
||||
if e.Raw == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return re.Raw, nil
|
||||
return e.Raw, nil
|
||||
}
|
||||
|
||||
func Convert_runtime_Object_To_runtime_RawExtension(in *Object, out *RawExtension, s conversion.Scope) error {
|
||||
|
||||
+18
-17
@@ -24,46 +24,47 @@ import (
|
||||
)
|
||||
|
||||
type notRegisteredErr struct {
|
||||
gvk schema.GroupVersionKind
|
||||
target GroupVersioner
|
||||
t reflect.Type
|
||||
schemeName string
|
||||
gvk schema.GroupVersionKind
|
||||
target GroupVersioner
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
func NewNotRegisteredErrForKind(gvk schema.GroupVersionKind) error {
|
||||
return ¬RegisteredErr{gvk: gvk}
|
||||
func NewNotRegisteredErrForKind(schemeName string, gvk schema.GroupVersionKind) error {
|
||||
return ¬RegisteredErr{schemeName: schemeName, gvk: gvk}
|
||||
}
|
||||
|
||||
func NewNotRegisteredErrForType(t reflect.Type) error {
|
||||
return ¬RegisteredErr{t: t}
|
||||
func NewNotRegisteredErrForType(schemeName string, t reflect.Type) error {
|
||||
return ¬RegisteredErr{schemeName: schemeName, t: t}
|
||||
}
|
||||
|
||||
func NewNotRegisteredErrForTarget(t reflect.Type, target GroupVersioner) error {
|
||||
return ¬RegisteredErr{t: t, target: target}
|
||||
func NewNotRegisteredErrForTarget(schemeName string, t reflect.Type, target GroupVersioner) error {
|
||||
return ¬RegisteredErr{schemeName: schemeName, t: t, target: target}
|
||||
}
|
||||
|
||||
func NewNotRegisteredGVKErrForTarget(gvk schema.GroupVersionKind, target GroupVersioner) error {
|
||||
return ¬RegisteredErr{gvk: gvk, target: target}
|
||||
func NewNotRegisteredGVKErrForTarget(schemeName string, gvk schema.GroupVersionKind, target GroupVersioner) error {
|
||||
return ¬RegisteredErr{schemeName: schemeName, gvk: gvk, target: target}
|
||||
}
|
||||
|
||||
func (k *notRegisteredErr) Error() string {
|
||||
if k.t != nil && k.target != nil {
|
||||
return fmt.Sprintf("%v is not suitable for converting to %q", k.t, k.target)
|
||||
return fmt.Sprintf("%v is not suitable for converting to %q in scheme %q", k.t, k.target, k.schemeName)
|
||||
}
|
||||
nullGVK := schema.GroupVersionKind{}
|
||||
if k.gvk != nullGVK && k.target != nil {
|
||||
return fmt.Sprintf("%q is not suitable for converting to %q", k.gvk.GroupVersion(), k.target)
|
||||
return fmt.Sprintf("%q is not suitable for converting to %q in scheme %q", k.gvk.GroupVersion(), k.target, k.schemeName)
|
||||
}
|
||||
if k.t != nil {
|
||||
return fmt.Sprintf("no kind is registered for the type %v", k.t)
|
||||
return fmt.Sprintf("no kind is registered for the type %v in scheme %q", k.t, k.schemeName)
|
||||
}
|
||||
if len(k.gvk.Kind) == 0 {
|
||||
return fmt.Sprintf("no version %q has been registered", k.gvk.GroupVersion())
|
||||
return fmt.Sprintf("no version %q has been registered in scheme %q", k.gvk.GroupVersion(), k.schemeName)
|
||||
}
|
||||
if k.gvk.Version == APIVersionInternal {
|
||||
return fmt.Sprintf("no kind %q is registered for the internal version of group %q", k.gvk.Kind, k.gvk.Group)
|
||||
return fmt.Sprintf("no kind %q is registered for the internal version of group %q in scheme %q", k.gvk.Kind, k.gvk.Group, k.schemeName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("no kind %q is registered for version %q", k.gvk.Kind, k.gvk.GroupVersion())
|
||||
return fmt.Sprintf("no kind %q is registered for version %q in scheme %q", k.gvk.Kind, k.gvk.GroupVersion(), k.schemeName)
|
||||
}
|
||||
|
||||
// IsNotRegisteredError returns true if the error indicates the provided
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal may get called on pointers or values, so implement MarshalJSON on value.
|
||||
// MarshalJSON may get called on pointers or values, so implement MarshalJSON on value.
|
||||
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
|
||||
func (re RawExtension) MarshalJSON() ([]byte, error) {
|
||||
if re.Raw == nil {
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ func Field(v reflect.Value, fieldName string, dest interface{}) error {
|
||||
return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), destValue.Type())
|
||||
}
|
||||
|
||||
// fieldPtr puts the address of fieldName, which must be a member of v,
|
||||
// FieldPtr puts the address of fieldName, which must be a member of v,
|
||||
// into dest, which must be an address of a variable to which this field's
|
||||
// address can be assigned.
|
||||
func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
|
||||
|
||||
+3
-3
@@ -39,14 +39,14 @@ type GroupVersioner interface {
|
||||
KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (target schema.GroupVersionKind, ok bool)
|
||||
}
|
||||
|
||||
// Encoders write objects to a serialized form
|
||||
// Encoder writes objects to a serialized form
|
||||
type Encoder interface {
|
||||
// Encode writes an object to a stream. Implementations may return errors if the versions are
|
||||
// incompatible, or if no conversion is defined.
|
||||
Encode(obj Object, w io.Writer) error
|
||||
}
|
||||
|
||||
// Decoders attempt to load an object from data.
|
||||
// Decoder attempts to load an object from data.
|
||||
type Decoder interface {
|
||||
// Decode attempts to deserialize the provided data using either the innate typing of the scheme or the
|
||||
// default kind, group, and version provided. It returns a decoded object as well as the kind, group, and
|
||||
@@ -224,7 +224,7 @@ type SelfLinker interface {
|
||||
Namespace(obj Object) (string, error)
|
||||
}
|
||||
|
||||
// All API types registered with Scheme must support the Object interface. Since objects in a scheme are
|
||||
// Object interface must be supported by all API types registered with Scheme. Since objects in a scheme are
|
||||
// expected to be serialized to the wire, the interface an Object must provide to the Scheme allows
|
||||
// serializers to set the kind, version, and group the object is represented as. An Object may choose
|
||||
// to return a no-op ObjectKindAccessor in cases where it is not expected to be serialized.
|
||||
|
||||
+2
-3
@@ -85,11 +85,10 @@ func ParseGroupKind(gk string) GroupKind {
|
||||
// ParseGroupResource turns "resource.group" string into a GroupResource struct. Empty strings are allowed
|
||||
// for each field.
|
||||
func ParseGroupResource(gr string) GroupResource {
|
||||
if i := strings.Index(gr, "."); i == -1 {
|
||||
return GroupResource{Resource: gr}
|
||||
} else {
|
||||
if i := strings.Index(gr, "."); i >= 0 {
|
||||
return GroupResource{Group: gr[i+1:], Resource: gr[:i]}
|
||||
}
|
||||
return GroupResource{Resource: gr}
|
||||
}
|
||||
|
||||
// GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion
|
||||
|
||||
+25
-17
@@ -20,11 +20,12 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/naming"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
@@ -78,9 +79,13 @@ type Scheme struct {
|
||||
|
||||
// observedVersions keeps track of the order we've seen versions during type registration
|
||||
observedVersions []schema.GroupVersion
|
||||
|
||||
// schemeName is the name of this scheme. If you don't specify a name, the stack of the NewScheme caller will be used.
|
||||
// This is useful for error reporting to indicate the origin of the scheme.
|
||||
schemeName string
|
||||
}
|
||||
|
||||
// Function to convert a field selector to internal representation.
|
||||
// FieldLabelConversionFunc converts a field selector to internal representation.
|
||||
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
||||
|
||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
||||
@@ -93,21 +98,16 @@ func NewScheme() *Scheme {
|
||||
fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{},
|
||||
defaulterFuncs: map[reflect.Type]func(interface{}){},
|
||||
versionPriority: map[string][]string{},
|
||||
schemeName: naming.GetNameFromCallsite(internalPackages...),
|
||||
}
|
||||
s.converter = conversion.NewConverter(s.nameFunc)
|
||||
|
||||
s.AddConversionFuncs(DefaultEmbeddedConversions()...)
|
||||
utilruntime.Must(s.AddConversionFuncs(DefaultEmbeddedConversions()...))
|
||||
|
||||
// Enable map[string][]string conversions by default
|
||||
if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
utilruntime.Must(s.AddConversionFuncs(DefaultStringConversions...))
|
||||
utilruntime.Must(s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields))
|
||||
utilruntime.Must(s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields))
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error
|
||||
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return nil, false, NewNotRegisteredErrForType(t)
|
||||
return nil, false, NewNotRegisteredErrForType(s.schemeName, t)
|
||||
}
|
||||
_, unversionedType := s.unversionedTypes[t]
|
||||
|
||||
@@ -293,7 +293,7 @@ func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {
|
||||
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
||||
return reflect.New(t).Interface().(Object), nil
|
||||
}
|
||||
return nil, NewNotRegisteredErrForKind(kind)
|
||||
return nil, NewNotRegisteredErrForKind(s.schemeName, kind)
|
||||
}
|
||||
|
||||
// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
|
||||
@@ -393,7 +393,7 @@ func (s *Scheme) RegisterInputDefaults(in interface{}, fn conversion.FieldMappin
|
||||
return s.converter.RegisterInputDefaults(in, fn, defaultFlags)
|
||||
}
|
||||
|
||||
// AddTypeDefaultingFuncs registers a function that is passed a pointer to an
|
||||
// AddTypeDefaultingFunc registers a function that is passed a pointer to an
|
||||
// object and can default fields on the object. These functions will be invoked
|
||||
// when Default() is called. The function will never be called unless the
|
||||
// defaulted object matches srcType. If this function is invoked twice with the
|
||||
@@ -541,7 +541,7 @@ func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (
|
||||
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, NewNotRegisteredErrForType(t)
|
||||
return nil, NewNotRegisteredErrForType(s.schemeName, t)
|
||||
}
|
||||
|
||||
gvk, ok := target.KindForGroupVersionKinds(kinds)
|
||||
@@ -554,7 +554,7 @@ func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (
|
||||
}
|
||||
return copyAndSetTargetKind(copy, in, unversionedKind)
|
||||
}
|
||||
return nil, NewNotRegisteredErrForTarget(t, target)
|
||||
return nil, NewNotRegisteredErrForTarget(s.schemeName, t, target)
|
||||
}
|
||||
|
||||
// target wants to use the existing type, set kind and return (no conversion necessary)
|
||||
@@ -764,3 +764,11 @@ func (s *Scheme) addObservedVersion(version schema.GroupVersion) {
|
||||
|
||||
s.observedVersions = append(s.observedVersions, version)
|
||||
}
|
||||
|
||||
func (s *Scheme) Name() string {
|
||||
return s.schemeName
|
||||
}
|
||||
|
||||
// internalPackages are packages that ignored when creating a default reflector name. These packages are in the common
|
||||
// call chains to NewReflector, so they'd be low entropy names for reflectors
|
||||
var internalPackages = []string{"k8s.io/apimachinery/pkg/runtime/scheme.go"}
|
||||
|
||||
+1
-1
@@ -273,7 +273,7 @@ func (jsonFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser {
|
||||
return framer.NewJSONFramedReader(r)
|
||||
}
|
||||
|
||||
// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
|
||||
// YAMLFramer is the default JSON framing behavior, with newlines delimiting individual objects.
|
||||
var YAMLFramer = yamlFramer{}
|
||||
|
||||
type yamlFramer struct{}
|
||||
|
||||
+8
-14
@@ -24,18 +24,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// NewCodecForScheme is a convenience method for callers that are using a scheme.
|
||||
func NewCodecForScheme(
|
||||
// TODO: I should be a scheme interface?
|
||||
scheme *runtime.Scheme,
|
||||
encoder runtime.Encoder,
|
||||
decoder runtime.Decoder,
|
||||
encodeVersion runtime.GroupVersioner,
|
||||
decodeVersion runtime.GroupVersioner,
|
||||
) runtime.Codec {
|
||||
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, nil, encodeVersion, decodeVersion)
|
||||
}
|
||||
|
||||
// NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme.
|
||||
func NewDefaultingCodecForScheme(
|
||||
// TODO: I should be a scheme interface?
|
||||
@@ -45,7 +33,7 @@ func NewDefaultingCodecForScheme(
|
||||
encodeVersion runtime.GroupVersioner,
|
||||
decodeVersion runtime.GroupVersioner,
|
||||
) runtime.Codec {
|
||||
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion)
|
||||
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion, scheme.Name())
|
||||
}
|
||||
|
||||
// NewCodec takes objects in their internal versions and converts them to external versions before
|
||||
@@ -60,6 +48,7 @@ func NewCodec(
|
||||
defaulter runtime.ObjectDefaulter,
|
||||
encodeVersion runtime.GroupVersioner,
|
||||
decodeVersion runtime.GroupVersioner,
|
||||
originalSchemeName string,
|
||||
) runtime.Codec {
|
||||
internal := &codec{
|
||||
encoder: encoder,
|
||||
@@ -71,6 +60,8 @@ func NewCodec(
|
||||
|
||||
encodeVersion: encodeVersion,
|
||||
decodeVersion: decodeVersion,
|
||||
|
||||
originalSchemeName: originalSchemeName,
|
||||
}
|
||||
return internal
|
||||
}
|
||||
@@ -85,6 +76,9 @@ type codec struct {
|
||||
|
||||
encodeVersion runtime.GroupVersioner
|
||||
decodeVersion runtime.GroupVersioner
|
||||
|
||||
// originalSchemeName is optional, but when filled in it holds the name of the scheme from which this codec originates
|
||||
originalSchemeName string
|
||||
}
|
||||
|
||||
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
|
||||
@@ -182,7 +176,7 @@ func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
||||
}
|
||||
targetGVK, ok := c.encodeVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{objGVK})
|
||||
if !ok {
|
||||
return runtime.NewNotRegisteredGVKErrForTarget(objGVK, c.encodeVersion)
|
||||
return runtime.NewNotRegisteredGVKErrForTarget(c.originalSchemeName, objGVK, c.encodeVersion)
|
||||
}
|
||||
if targetGVK == objGVK {
|
||||
return c.encoder.Encode(obj, w)
|
||||
|
||||
Reference in New Issue
Block a user