add validateListType to pkg/api/meta/schema_test.go
This commit is contained in:
		| @@ -39,10 +39,12 @@ func init() { | |||||||
| 	if _, found := testapi.Groups[testgroup.SchemeGroupVersion.Group]; found { | 	if _, found := testapi.Groups[testgroup.SchemeGroupVersion.Group]; found { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	externalGroupVersion := registered.GroupOrDie(testgroup.SchemeGroupVersion.Group).GroupVersion | ||||||
| 	testapi.Groups[testgroup.SchemeGroupVersion.Group] = testapi.NewTestGroup( | 	testapi.Groups[testgroup.SchemeGroupVersion.Group] = testapi.NewTestGroup( | ||||||
| 		registered.GroupOrDie(testgroup.SchemeGroupVersion.Group).GroupVersion, | 		externalGroupVersion, | ||||||
| 		testgroup.SchemeGroupVersion, | 		testgroup.SchemeGroupVersion, | ||||||
| 		api.Scheme.KnownTypes(testgroup.SchemeGroupVersion)) | 		api.Scheme.KnownTypes(testgroup.SchemeGroupVersion), | ||||||
|  | 		api.Scheme.KnownTypes(externalGroupVersion)) | ||||||
| 	testHelper = testapi.Groups[testgroup.SchemeGroupVersion.Group] | 	testHelper = testapi.Groups[testgroup.SchemeGroupVersion.Group] | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -134,8 +134,9 @@ specific actions that create, update, delete, or get. | |||||||
| 2. **Lists** are collections of **resources** of one (usually) or more | 2. **Lists** are collections of **resources** of one (usually) or more | ||||||
| (occasionally) kinds. | (occasionally) kinds. | ||||||
|  |  | ||||||
|    Lists have a limited set of common metadata. All lists use the "items" field |    The name of a list kind must end with "List". Lists have a limited set of | ||||||
| to contain the array of objects they return. | common metadata. All lists use the required "items" field to contain the array | ||||||
|  | of objects they return. Any kind that has the "items" field must be a list kind. | ||||||
|  |  | ||||||
|    Most objects defined in the system should have an endpoint that returns the |    Most objects defined in the system should have an endpoint that returns the | ||||||
| full set of resources, as well as zero or more endpoints that return subsets of | full set of resources, as well as zero or more endpoints that return subsets of | ||||||
|   | |||||||
							
								
								
									
										121
									
								
								pkg/api/meta/scheme_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								pkg/api/meta/scheme_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2016 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 meta_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/testapi" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||||
|  | 	"k8s.io/kubernetes/pkg/util/sets" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // These types do not follow the list convention as documented in | ||||||
|  | // docs/devel/api-convention.md | ||||||
|  | var listTypeExceptions = sets.NewString("APIGroupList", "APIResourceList") | ||||||
|  |  | ||||||
|  | func validateListType(target reflect.Type) error { | ||||||
|  | 	// exceptions | ||||||
|  | 	if listTypeExceptions.Has(target.Name()) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	hasListSuffix := strings.HasSuffix(target.Name(), "List") | ||||||
|  | 	hasMetadata := false | ||||||
|  | 	hasItems := false | ||||||
|  | 	for i := 0; i < target.NumField(); i++ { | ||||||
|  | 		field := target.Field(i) | ||||||
|  | 		tag := field.Tag.Get("json") | ||||||
|  | 		switch { | ||||||
|  | 		case strings.HasPrefix(tag, "metadata"): | ||||||
|  | 			hasMetadata = true | ||||||
|  | 		case tag == "items": | ||||||
|  | 			hasItems = true | ||||||
|  | 			if field.Type.Kind() != reflect.Slice { | ||||||
|  | 				return fmt.Errorf("Expected items to be slice, got %s", field.Type.Kind()) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if hasListSuffix && !hasMetadata { | ||||||
|  | 		return fmt.Errorf("Expected type %s to contain \"metadata\"", target.Name()) | ||||||
|  | 	} | ||||||
|  | 	if hasListSuffix && !hasItems { | ||||||
|  | 		return fmt.Errorf("Expected type %s to contain \"items\"", target.Name()) | ||||||
|  | 	} | ||||||
|  | 	// if a type contains field Items with JSON tag "items", its name should end with List. | ||||||
|  | 	if !hasListSuffix && hasItems { | ||||||
|  | 		return fmt.Errorf("Type %s has Items, its name is expected to end with \"List\"", target.Name()) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestListTypes verifies that no external type violates the api convention of | ||||||
|  | // list types. | ||||||
|  | func TestListTypes(t *testing.T) { | ||||||
|  | 	for groupKey, group := range testapi.Groups { | ||||||
|  | 		for kind, target := range group.ExternalTypes() { | ||||||
|  | 			t.Logf("working on %v in %v", kind, groupKey) | ||||||
|  | 			err := validateListType(target) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Error(err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WithoutMetaDataList struct { | ||||||
|  | 	unversioned.TypeMeta `json:",inline"` | ||||||
|  | 	unversioned.ListMeta | ||||||
|  | 	Items []interface{} `json:"items"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WithoutItemsList struct { | ||||||
|  | 	unversioned.TypeMeta `json:",inline"` | ||||||
|  | 	unversioned.ListMeta `json:"metadata,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WrongItemsJSONTagList struct { | ||||||
|  | 	unversioned.TypeMeta `json:",inline"` | ||||||
|  | 	unversioned.ListMeta `json:"metadata,omitempty"` | ||||||
|  | 	Items                []interface{} `json:"items,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // If a type has Items, its name should end with "List" | ||||||
|  | type ListWithWrongName struct { | ||||||
|  | 	unversioned.TypeMeta `json:",inline"` | ||||||
|  | 	unversioned.ListMeta `json:"metadata,omitempty"` | ||||||
|  | 	Items                []interface{} `json:"items"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestValidateListType verifies the validateListType function reports error on | ||||||
|  | // types that violate the api convention. | ||||||
|  | func TestValidateListType(t *testing.T) { | ||||||
|  | 	var testTypes = []interface{}{ | ||||||
|  | 		WithoutMetaDataList{}, | ||||||
|  | 		WithoutItemsList{}, | ||||||
|  | 		WrongItemsJSONTagList{}, | ||||||
|  | 		ListWithWrongName{}, | ||||||
|  | 	} | ||||||
|  | 	for _, testType := range testTypes { | ||||||
|  | 		err := validateListType(reflect.TypeOf(testType)) | ||||||
|  | 		if err == nil { | ||||||
|  | 			t.Errorf("Expected error") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -71,6 +71,7 @@ type TestGroup struct { | |||||||
| 	externalGroupVersion unversioned.GroupVersion | 	externalGroupVersion unversioned.GroupVersion | ||||||
| 	internalGroupVersion unversioned.GroupVersion | 	internalGroupVersion unversioned.GroupVersion | ||||||
| 	internalTypes        map[string]reflect.Type | 	internalTypes        map[string]reflect.Type | ||||||
|  | 	externalTypes        map[string]reflect.Type | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| @@ -112,22 +113,27 @@ func init() { | |||||||
| 				externalGroupVersion: groupVersion, | 				externalGroupVersion: groupVersion, | ||||||
| 				internalGroupVersion: internalGroupVersion, | 				internalGroupVersion: internalGroupVersion, | ||||||
| 				internalTypes:        api.Scheme.KnownTypes(internalGroupVersion), | 				internalTypes:        api.Scheme.KnownTypes(internalGroupVersion), | ||||||
|  | 				externalTypes:        api.Scheme.KnownTypes(groupVersion), | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if _, ok := Groups[api.GroupName]; !ok { | 	if _, ok := Groups[api.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: api.GroupName, Version: registered.GroupOrDie(api.GroupName).GroupVersion.Version} | ||||||
| 		Groups[api.GroupName] = TestGroup{ | 		Groups[api.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: api.GroupName, Version: registered.GroupOrDie(api.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: api.SchemeGroupVersion, | 			internalGroupVersion: api.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(api.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(api.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[extensions.GroupName]; !ok { | 	if _, ok := Groups[extensions.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version} | ||||||
| 		Groups[extensions.GroupName] = TestGroup{ | 		Groups[extensions.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: extensions.SchemeGroupVersion, | 			internalGroupVersion: extensions.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(extensions.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(extensions.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[autoscaling.GroupName]; !ok { | 	if _, ok := Groups[autoscaling.GroupName]; !ok { | ||||||
| @@ -138,10 +144,12 @@ func init() { | |||||||
| 			} | 			} | ||||||
| 			internalTypes[k] = t | 			internalTypes[k] = t | ||||||
| 		} | 		} | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version} | ||||||
| 		Groups[autoscaling.GroupName] = TestGroup{ | 		Groups[autoscaling.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: extensions.SchemeGroupVersion, | 			internalGroupVersion: extensions.SchemeGroupVersion, | ||||||
| 			internalTypes:        internalTypes, | 			internalTypes:        internalTypes, | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[autoscaling.GroupName+"IntraGroup"]; !ok { | 	if _, ok := Groups[autoscaling.GroupName+"IntraGroup"]; !ok { | ||||||
| @@ -152,52 +160,66 @@ func init() { | |||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version} | ||||||
| 		Groups[autoscaling.GroupName] = TestGroup{ | 		Groups[autoscaling.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: autoscaling.SchemeGroupVersion, | 			internalGroupVersion: autoscaling.SchemeGroupVersion, | ||||||
| 			internalTypes:        internalTypes, | 			internalTypes:        internalTypes, | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[batch.GroupName]; !ok { | 	if _, ok := Groups[batch.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: batch.GroupName, Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version} | ||||||
| 		Groups[batch.GroupName] = TestGroup{ | 		Groups[batch.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: batch.GroupName, Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: batch.SchemeGroupVersion, | 			internalGroupVersion: batch.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(batch.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(batch.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[apps.GroupName]; !ok { | 	if _, ok := Groups[apps.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: apps.GroupName, Version: registered.GroupOrDie(apps.GroupName).GroupVersion.Version} | ||||||
| 		Groups[apps.GroupName] = TestGroup{ | 		Groups[apps.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: apps.GroupName, Version: registered.GroupOrDie(apps.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: extensions.SchemeGroupVersion, | 			internalGroupVersion: extensions.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(extensions.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(extensions.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[policy.GroupName]; !ok { | 	if _, ok := Groups[policy.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: policy.GroupName, Version: registered.GroupOrDie(policy.GroupName).GroupVersion.Version} | ||||||
| 		Groups[policy.GroupName] = TestGroup{ | 		Groups[policy.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: policy.GroupName, Version: registered.GroupOrDie(policy.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: policy.SchemeGroupVersion, | 			internalGroupVersion: policy.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(policy.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(policy.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[federation.GroupName]; !ok { | 	if _, ok := Groups[federation.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: federation.GroupName, Version: registered.GroupOrDie(federation.GroupName).GroupVersion.Version} | ||||||
| 		Groups[federation.GroupName] = TestGroup{ | 		Groups[federation.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: federation.GroupName, Version: registered.GroupOrDie(federation.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: federation.SchemeGroupVersion, | 			internalGroupVersion: federation.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(federation.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(federation.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[rbac.GroupName]; !ok { | 	if _, ok := Groups[rbac.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: rbac.GroupName, Version: registered.GroupOrDie(rbac.GroupName).GroupVersion.Version} | ||||||
| 		Groups[rbac.GroupName] = TestGroup{ | 		Groups[rbac.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: rbac.GroupName, Version: registered.GroupOrDie(rbac.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: rbac.SchemeGroupVersion, | 			internalGroupVersion: rbac.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(rbac.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(rbac.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, ok := Groups[certificates.GroupName]; !ok { | 	if _, ok := Groups[certificates.GroupName]; !ok { | ||||||
|  | 		externalGroupVersion := unversioned.GroupVersion{Group: certificates.GroupName, Version: registered.GroupOrDie(certificates.GroupName).GroupVersion.Version} | ||||||
| 		Groups[certificates.GroupName] = TestGroup{ | 		Groups[certificates.GroupName] = TestGroup{ | ||||||
| 			externalGroupVersion: unversioned.GroupVersion{Group: certificates.GroupName, Version: registered.GroupOrDie(certificates.GroupName).GroupVersion.Version}, | 			externalGroupVersion: externalGroupVersion, | ||||||
| 			internalGroupVersion: certificates.SchemeGroupVersion, | 			internalGroupVersion: certificates.SchemeGroupVersion, | ||||||
| 			internalTypes:        api.Scheme.KnownTypes(certificates.SchemeGroupVersion), | 			internalTypes:        api.Scheme.KnownTypes(certificates.SchemeGroupVersion), | ||||||
|  | 			externalTypes:        api.Scheme.KnownTypes(externalGroupVersion), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -232,6 +254,11 @@ func (g TestGroup) InternalTypes() map[string]reflect.Type { | |||||||
| 	return g.internalTypes | 	return g.internalTypes | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ExternalTypes returns a map of external API types' kind names to their Go types. | ||||||
|  | func (g TestGroup) ExternalTypes() map[string]reflect.Type { | ||||||
|  | 	return g.externalTypes | ||||||
|  | } | ||||||
|  |  | ||||||
| // Codec returns the codec for the API version to test against, as set by the | // Codec returns the codec for the API version to test against, as set by the | ||||||
| // KUBE_TEST_API_TYPE env var. | // KUBE_TEST_API_TYPE env var. | ||||||
| func (g TestGroup) Codec() runtime.Codec { | func (g TestGroup) Codec() runtime.Codec { | ||||||
| @@ -387,6 +414,6 @@ func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) { | |||||||
| 	return nil, fmt.Errorf("unexpected kind: %v", kind) | 	return nil, fmt.Errorf("unexpected kind: %v", kind) | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewTestGroup(external, internal unversioned.GroupVersion, internalTypes map[string]reflect.Type) TestGroup { | func NewTestGroup(external, internal unversioned.GroupVersion, internalTypes map[string]reflect.Type, externalTypes map[string]reflect.Type) TestGroup { | ||||||
| 	return TestGroup{external, internal, internalTypes} | 	return TestGroup{external, internal, internalTypes, externalTypes} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Chao Xu
					Chao Xu