Merge pull request #41788 from sttts/sttts-scheme-registration-idem-potent

Automatic merge from submit-queue (batch tested with PRs 41234, 42186, 41615, 42028, 41788)

apimachinery: handle duplicated and conflicting type registration

Double registrations were leading to duplications in  `KnownKinds()`. Conflicting registrations with same gvk, but different types were not detected.
This commit is contained in:
Kubernetes Submit Queue 2017-02-28 00:34:11 -08:00 committed by GitHub
commit 9690771227
16 changed files with 97 additions and 46 deletions

View File

@ -18,6 +18,7 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/conversion", "//vendor:k8s.io/apimachinery/pkg/conversion",
"//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/types", "//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/client-go/pkg/api/v1", "//vendor:k8s.io/client-go/pkg/api/v1",
"//vendor:k8s.io/client-go/tools/record", "//vendor:k8s.io/client-go/tools/record",

View File

@ -22,6 +22,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
clientv1 "k8s.io/client-go/pkg/api/v1" clientv1 "k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
@ -48,10 +49,11 @@ func NewFederatedEventSink(clientset fedclientset.Interface) *FederatedEventSink
var scheme = runtime.NewScheme() var scheme = runtime.NewScheme()
func init() { func init() {
scheme.AddKnownTypes(clientv1.SchemeGroupVersion, // register client-go's and kube's Event type under two different GroupVersions
&clientv1.Event{}, // TODO: switch to client-go client for events
&kubev1.Event{}, scheme.AddKnownTypes(clientv1.SchemeGroupVersion, &clientv1.Event{})
) scheme.AddKnownTypes(schema.GroupVersion{Group: "fake-kube-" + kubev1.SchemeGroupVersion.Group, Version: kubev1.SchemeGroupVersion.Version}, &kubev1.Event{})
if err := scheme.AddConversionFuncs( if err := scheme.AddConversionFuncs(
metav1.Convert_unversioned_Time_To_unversioned_Time, metav1.Convert_unversioned_Time_To_unversioned_Time,
); err != nil { ); err != nil {

View File

@ -174,7 +174,8 @@ func TestCommonKindsRegistered(t *testing.T) {
t.Error(err) t.Error(err)
} }
defaults := gv.WithKind("") defaults := gv.WithKind("")
if _, got, err := api.Codecs.LegacyCodec().Decode([]byte(`{"kind":"`+kind+`"}`), &defaults, nil); err != nil || gvk != *got { var got *schema.GroupVersionKind
if obj, got, err = api.Codecs.LegacyCodec().Decode([]byte(`{"kind":"`+kind+`"}`), &defaults, obj); err != nil || gvk != *got {
t.Errorf("expected %v: %v %v", gvk, got, err) t.Errorf("expected %v: %v %v", gvk, got, err)
} }
data, err := runtime.Encode(api.Codecs.LegacyCodec(*gv), obj) data, err := runtime.Encode(api.Codecs.LegacyCodec(*gv), obj)

View File

@ -20,7 +20,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api/v1"
) )
// GroupName is the group name use in this package // GroupName is the group name use in this package
@ -39,10 +38,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion, scheme.AddKnownTypes(SchemeGroupVersion,
&HorizontalPodAutoscaler{}, &HorizontalPodAutoscaler{},
&HorizontalPodAutoscalerList{}, &HorizontalPodAutoscalerList{},
&v1.ListOptions{},
&v1.DeleteOptions{},
&metav1.GetOptions{},
&metav1.ExportOptions{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion) metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil

View File

@ -17,7 +17,6 @@ limitations under the License.
package rbac package rbac
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -55,6 +54,5 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ClusterRoleBindingList{}, &ClusterRoleBindingList{},
&ClusterRoleList{}, &ClusterRoleList{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil
} }

View File

@ -52,6 +52,10 @@ func NewMetadataCodecFactory() serializer.CodecFactory {
if kind.Version == runtime.APIVersionInternal { if kind.Version == runtime.APIVersionInternal {
continue continue
} }
if kind == api.Unversioned.WithKind("Status") {
// this is added below as unversioned
continue
}
metaOnlyObject := gvkToMetadataOnlyObject(kind) metaOnlyObject := gvkToMetadataOnlyObject(kind)
scheme.AddKnownTypeWithName(kind, metaOnlyObject) scheme.AddKnownTypeWithName(kind, metaOnlyObject)
} }

View File

@ -163,22 +163,13 @@ func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Objec
// the struct becomes the "kind" field when encoding. Version may not be empty - use the // the struct becomes the "kind" field when encoding. Version may not be empty - use the
// APIVersionInternal constant if you have a type that does not have a formal version. // APIVersionInternal constant if you have a type that does not have a formal version.
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) { func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
if len(gv.Version) == 0 {
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
}
for _, obj := range types { for _, obj := range types {
t := reflect.TypeOf(obj) t := reflect.TypeOf(obj)
if t.Kind() != reflect.Ptr { if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.") panic("All types must be pointers to structs.")
} }
t = t.Elem() t = t.Elem()
if t.Kind() != reflect.Struct { s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
panic("All types must be pointers to structs.")
}
gvk := gv.WithKind(t.Name())
s.gvkToType[gvk] = t
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
} }
} }
@ -199,7 +190,17 @@ func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
panic("All types must be pointers to structs.") panic("All types must be pointers to structs.")
} }
if oldT, found := s.gvkToType[gvk]; found && oldT != t {
panic(fmt.Sprintf("Double registration of different types for %v: old=%v.%v, new=%v.%v", gvk, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name()))
}
s.gvkToType[gvk] = t s.gvkToType[gvk] = t
for _, existingGvk := range s.typeToGVK[t] {
if existingGvk == gvk {
return
}
}
s.typeToGVK[t] = append(s.typeToGVK[t], gvk) s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
} }

View File

@ -548,6 +548,79 @@ func TestKnownTypes(t *testing.T) {
} }
} }
func TestAddKnownTypesIdemPotent(t *testing.T) {
s := runtime.NewScheme()
gv := schema.GroupVersion{Group: "foo", Version: "v1"}
s.AddKnownTypes(gv, &InternalSimple{})
s.AddKnownTypes(gv, &InternalSimple{})
if len(s.KnownTypes(gv)) != 1 {
t.Errorf("expected only one %v type after double registration", gv)
}
if len(s.AllKnownTypes()) != 1 {
t.Errorf("expected only one type after double registration")
}
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &InternalSimple{})
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &InternalSimple{})
if len(s.KnownTypes(gv)) != 1 {
t.Errorf("expected only one %v type after double registration with custom name", gv)
}
if len(s.AllKnownTypes()) != 1 {
t.Errorf("expected only one type after double registration with custom name")
}
s.AddUnversionedTypes(gv, &InternalSimple{})
if len(s.KnownTypes(gv)) != 1 {
t.Errorf("expected only one %v type after double registration with custom name", gv)
}
if len(s.AllKnownTypes()) != 1 {
t.Errorf("expected only one type after double registration with custom name")
}
kinds, _, err := s.ObjectKinds(&InternalSimple{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(kinds) != 1 {
t.Errorf("expected only one kind for InternalSimple after double registration", gv)
}
}
func TestConflictingAddKnownTypes(t *testing.T) {
s := runtime.NewScheme()
gv := schema.GroupVersion{Group: "foo", Version: "v1"}
panicked := make(chan bool)
go func() {
defer func() {
if recover() != nil {
panicked <- true
}
}()
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &InternalSimple{})
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &ExternalSimple{})
panicked <- false
}()
if !<-panicked {
t.Errorf("Expected AddKnownTypesWithName to panic with conflicting type registrations")
}
go func() {
defer func() {
if recover() != nil {
panicked <- true
}
}()
s.AddUnversionedTypes(gv, &InternalSimple{})
s.AddUnversionedTypes(gv, &InternalSimple{})
panicked <- false
}()
if !<-panicked {
t.Errorf("Expected AddUnversionedTypes to panic with conflicting type registrations")
}
}
func TestConvertToVersionBasic(t *testing.T) { func TestConvertToVersionBasic(t *testing.T) {
s := GetTestScheme() s := GetTestScheme()
tt := &TestType1{A: "I'm not a pointer object"} tt := &TestType1{A: "I'm not a pointer object"}

View File

@ -17,7 +17,6 @@ limitations under the License.
package apiserver package apiserver
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -47,6 +46,5 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion, scheme.AddKnownTypes(SchemeGroupVersion,
&AdmissionConfiguration{}, &AdmissionConfiguration{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
package example package example
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -48,6 +47,5 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion, scheme.AddKnownTypes(SchemeGroupVersion,
&Pod{}, &Pod{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil
} }

View File

@ -77,7 +77,6 @@ func testScheme(t *testing.T) (*runtime.Scheme, serializer.CodecFactory) {
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
scheme.Log(t) scheme.Log(t)
scheme.AddKnownTypes(schema.GroupVersion{Version: runtime.APIVersionInternal}, &storagetesting.TestResource{}) scheme.AddKnownTypes(schema.GroupVersion{Version: runtime.APIVersionInternal}, &storagetesting.TestResource{})
scheme.AddKnownTypes(schema.GroupVersion{Version: runtime.APIVersionInternal}, &storagetesting.TestResource{})
example.AddToScheme(scheme) example.AddToScheme(scheme)
examplev1.AddToScheme(scheme) examplev1.AddToScheme(scheme)
if err := scheme.AddConversionFuncs( if err := scheme.AddConversionFuncs(

View File

@ -20,7 +20,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/pkg/api/v1"
) )
// GroupName is the group name use in this package // GroupName is the group name use in this package
@ -39,10 +38,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion, scheme.AddKnownTypes(SchemeGroupVersion,
&HorizontalPodAutoscaler{}, &HorizontalPodAutoscaler{},
&HorizontalPodAutoscalerList{}, &HorizontalPodAutoscalerList{},
&v1.ListOptions{},
&v1.DeleteOptions{},
&metav1.GetOptions{},
&metav1.ExportOptions{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion) metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil

View File

@ -17,7 +17,6 @@ limitations under the License.
package rbac package rbac
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -55,6 +54,5 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ClusterRoleBindingList{}, &ClusterRoleBindingList{},
&ClusterRoleList{}, &ClusterRoleList{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil
} }

View File

@ -17,12 +17,8 @@ limitations under the License.
package apiregistration package apiregistration
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
// we register here until we separate our scheme, which requires updates to client gen
kapi "k8s.io/client-go/pkg/api"
) )
const GroupName = "apiregistration.k8s.io" const GroupName = "apiregistration.k8s.io"
@ -50,11 +46,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion, scheme.AddKnownTypes(SchemeGroupVersion,
&APIService{}, &APIService{},
&APIServiceList{}, &APIServiceList{},
&kapi.ListOptions{},
&metav1.DeleteOptions{},
&metav1.GetOptions{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
package wardle package wardle
import ( import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -48,6 +47,5 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&Flunder{}, &Flunder{},
&FlunderList{}, &FlunderList{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil
} }

1
vendor/BUILD vendored
View File

@ -15836,7 +15836,6 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/conversion", "//vendor:k8s.io/apimachinery/pkg/conversion",
"//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/client-go/pkg/api",
], ],
) )