Merge pull request #30565 from lavalamp/improve-registered
Automatic merge from submit-queue Centralize install code Trying to figure out a way to do this that makes the changes as painless to roll out as possible. This is going to be a multi-step process...
This commit is contained in:
		| @@ -15,6 +15,12 @@ limitations under the License. | |||||||
| */ | */ | ||||||
|  |  | ||||||
| // Package testapi provides a helper for retrieving the KUBE_TEST_API environment variable. | // Package testapi provides a helper for retrieving the KUBE_TEST_API environment variable. | ||||||
|  | // | ||||||
|  | // TODO(lavalamp): this package is a huge disaster at the moment. I intend to | ||||||
|  | // refactor. All code currently using this package should change: | ||||||
|  | // 1. Declare your own registered.APIGroupRegistrationManager in your own test code. | ||||||
|  | // 2. Import the relevant install packages. | ||||||
|  | // 3. Register the types you need, from the announced.APIGroupAnnouncementManager. | ||||||
| package testapi | package testapi | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|   | |||||||
							
								
								
									
										107
									
								
								pkg/apimachinery/announced/announced.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								pkg/apimachinery/announced/announced.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | |||||||
|  | /* | ||||||
|  | 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 announced contains tools for announcing API group factories. This is | ||||||
|  | // distinct from registration (in the 'registered' package) in that it's safe | ||||||
|  | // to announce every possible group linked in, but only groups requested at | ||||||
|  | // runtime should be registered. This package contains both a registry, and | ||||||
|  | // factory code (which was formerly copy-pasta in every install package). | ||||||
|  | package announced | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/apimachinery/registered" | ||||||
|  | 	"k8s.io/kubernetes/pkg/runtime" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	DefaultGroupFactoryRegistry = make(APIGroupFactoryRegistry) | ||||||
|  |  | ||||||
|  | 	// These functions will announce your group or version. | ||||||
|  | 	AnnounceGroupVersion = DefaultGroupFactoryRegistry.AnnounceGroupVersion | ||||||
|  | 	AnnounceGroup        = DefaultGroupFactoryRegistry.AnnounceGroup | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // APIGroupFactoryRegistry allows for groups and versions to announce themselves, | ||||||
|  | // which simply makes them available and doesn't take other actions. Later, | ||||||
|  | // users of the registry can select which groups and versions they'd actually | ||||||
|  | // like to register with an APIRegistrationManager. | ||||||
|  | // | ||||||
|  | // (Right now APIRegistrationManager has separate 'registration' and 'enabled' | ||||||
|  | // concepts-- APIGroupFactory is going to take over the former function; | ||||||
|  | // they will overlap untill the refactoring is finished.) | ||||||
|  | // | ||||||
|  | // The key is the group name. After initialization, this should be treated as | ||||||
|  | // read-only. It is implemented as a map from group name to group factory, and | ||||||
|  | // it is safe to use this knowledge to manually pick out groups to register | ||||||
|  | // (e.g., for testing). | ||||||
|  | type APIGroupFactoryRegistry map[string]*GroupMetaFactory | ||||||
|  |  | ||||||
|  | func (gar APIGroupFactoryRegistry) group(groupName string) *GroupMetaFactory { | ||||||
|  | 	gmf, ok := gar[groupName] | ||||||
|  | 	if !ok { | ||||||
|  | 		gmf = &GroupMetaFactory{VersionArgs: map[string]*GroupVersionFactoryArgs{}} | ||||||
|  | 		gar[groupName] = gmf | ||||||
|  | 	} | ||||||
|  | 	return gmf | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AnnounceGroupVersion adds the particular arguments for this group version to the group factory. | ||||||
|  | func (gar APIGroupFactoryRegistry) AnnounceGroupVersion(gvf *GroupVersionFactoryArgs) error { | ||||||
|  | 	gmf := gar.group(gvf.GroupName) | ||||||
|  | 	if _, ok := gmf.VersionArgs[gvf.VersionName]; ok { | ||||||
|  | 		return fmt.Errorf("version %q in group %q has already been announced", gvf.VersionName, gvf.GroupName) | ||||||
|  | 	} | ||||||
|  | 	gmf.VersionArgs[gvf.VersionName] = gvf | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AnnounceGroup adds the group-wide arguments to the group factory. | ||||||
|  | func (gar APIGroupFactoryRegistry) AnnounceGroup(args *GroupMetaFactoryArgs) error { | ||||||
|  | 	gmf := gar.group(args.GroupName) | ||||||
|  | 	if gmf.GroupArgs != nil { | ||||||
|  | 		return fmt.Errorf("group %q has already been announced", args.GroupName) | ||||||
|  | 	} | ||||||
|  | 	gmf.GroupArgs = args | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RegisterAndEnableAll throws every factory at the specified API registration | ||||||
|  | // manager, and lets it decide which to register. (If you want to do this a la | ||||||
|  | // cart, you may look through gar itself-- it's just a map.) | ||||||
|  | func (gar APIGroupFactoryRegistry) RegisterAndEnableAll(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error { | ||||||
|  | 	for groupName, gmf := range gar { | ||||||
|  | 		if err := gmf.Register(m); err != nil { | ||||||
|  | 			return fmt.Errorf("error registering %v: %v", groupName, err) | ||||||
|  | 		} | ||||||
|  | 		if err := gmf.Enable(m, scheme); err != nil { | ||||||
|  | 			return fmt.Errorf("error enabling %v: %v", groupName, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AnnouncePreconstructedFactory announces a factory which you've manually assembled. | ||||||
|  | // You may call this instead of calling AnnounceGroup and AnnounceGroupVersion. | ||||||
|  | func (gar APIGroupFactoryRegistry) AnnouncePreconstructedFactory(gmf *GroupMetaFactory) error { | ||||||
|  | 	name := gmf.GroupArgs.GroupName | ||||||
|  | 	if _, exists := gar[name]; exists { | ||||||
|  | 		return fmt.Errorf("the group %q has already been announced.", name) | ||||||
|  | 	} | ||||||
|  | 	gar[name] = gmf | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								pkg/apimachinery/announced/announced_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								pkg/apimachinery/announced/announced_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | /* | ||||||
|  | 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 announced | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/util/sets" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestFactoryRegistry(t *testing.T) { | ||||||
|  | 	regA := make(APIGroupFactoryRegistry) | ||||||
|  | 	regB := make(APIGroupFactoryRegistry) | ||||||
|  |  | ||||||
|  | 	if err := regA.AnnounceGroup(&GroupMetaFactoryArgs{ | ||||||
|  | 		GroupName:              "foo", | ||||||
|  | 		VersionPreferenceOrder: []string{"v2", "v1"}, | ||||||
|  | 		ImportPrefix:           "pkg/apis/foo", | ||||||
|  | 		RootScopedKinds:        sets.NewString("namespaces"), | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := regA.AnnounceGroupVersion(&GroupVersionFactoryArgs{ | ||||||
|  | 		GroupName:   "foo", | ||||||
|  | 		VersionName: "v1", | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := regA.AnnounceGroupVersion(&GroupVersionFactoryArgs{ | ||||||
|  | 		GroupName:   "foo", | ||||||
|  | 		VersionName: "v2", | ||||||
|  | 	}); err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := regB.AnnouncePreconstructedFactory(NewGroupMetaFactory( | ||||||
|  | 		&GroupMetaFactoryArgs{ | ||||||
|  | 			GroupName:              "foo", | ||||||
|  | 			VersionPreferenceOrder: []string{"v2", "v1"}, | ||||||
|  | 			ImportPrefix:           "pkg/apis/foo", | ||||||
|  | 			RootScopedKinds:        sets.NewString("namespaces"), | ||||||
|  | 		}, | ||||||
|  | 		VersionToSchemeFunc{"v1": nil, "v2": nil}, | ||||||
|  | 	)); err != nil { | ||||||
|  | 		t.Fatalf("Unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !reflect.DeepEqual(regA, regB) { | ||||||
|  | 		t.Errorf("Expected both ways of registering to be equivalent, but they were not.\n\n%#v\n\n%#v\n", regA, regB) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										249
									
								
								pkg/apimachinery/announced/group_factory.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								pkg/apimachinery/announced/group_factory.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | |||||||
|  | /* | ||||||
|  | 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 announced | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/golang/glog" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/api" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/meta" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apimachinery" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apimachinery/registered" | ||||||
|  | 	"k8s.io/kubernetes/pkg/runtime" | ||||||
|  | 	"k8s.io/kubernetes/pkg/util/sets" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SchemeFunc func(*runtime.Scheme) error | ||||||
|  | type VersionToSchemeFunc map[string]SchemeFunc | ||||||
|  |  | ||||||
|  | // GroupVersionFactoryArgs contains all the per-version parts of a GroupMetaFactory. | ||||||
|  | type GroupVersionFactoryArgs struct { | ||||||
|  | 	GroupName   string | ||||||
|  | 	VersionName string | ||||||
|  |  | ||||||
|  | 	AddToScheme SchemeFunc | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GroupMetaFactoryArgs contains the group-level args of a GroupMetaFactory. | ||||||
|  | type GroupMetaFactoryArgs struct { | ||||||
|  | 	GroupName              string | ||||||
|  | 	VersionPreferenceOrder []string | ||||||
|  | 	ImportPrefix           string | ||||||
|  |  | ||||||
|  | 	RootScopedKinds sets.String // nil is allowed | ||||||
|  | 	IgnoredKinds    sets.String // nil is allowed | ||||||
|  |  | ||||||
|  | 	// May be nil if there are no internal objects. | ||||||
|  | 	AddInternalObjectsToScheme SchemeFunc | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewGroupMetaFactory builds the args for you. This is for if you're | ||||||
|  | // constructing a factory all at once and not using the registry. | ||||||
|  | func NewGroupMetaFactory(groupArgs *GroupMetaFactoryArgs, versions VersionToSchemeFunc) *GroupMetaFactory { | ||||||
|  | 	gmf := &GroupMetaFactory{ | ||||||
|  | 		GroupArgs:   groupArgs, | ||||||
|  | 		VersionArgs: map[string]*GroupVersionFactoryArgs{}, | ||||||
|  | 	} | ||||||
|  | 	for v, f := range versions { | ||||||
|  | 		gmf.VersionArgs[v] = &GroupVersionFactoryArgs{ | ||||||
|  | 			GroupName:   groupArgs.GroupName, | ||||||
|  | 			VersionName: v, | ||||||
|  | 			AddToScheme: f, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return gmf | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Announce adds this Group factory to the global factory registry. It should | ||||||
|  | // only be called if you constructed the GroupMetaFactory yourself via | ||||||
|  | // NewGroupMetadFactory. | ||||||
|  | // Note that this will panic on an error, since it's expected that you'll be | ||||||
|  | // calling this at initialization time and any error is a result of a | ||||||
|  | // programmer importing the wrong set of packages. If this assumption doesn't | ||||||
|  | // work for you, just call DefaultGroupFactoryRegistry.AnnouncePreconstructedFactory | ||||||
|  | // yourself. | ||||||
|  | func (gmf *GroupMetaFactory) Announce() *GroupMetaFactory { | ||||||
|  | 	if err := DefaultGroupFactoryRegistry.AnnouncePreconstructedFactory(gmf); err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	return gmf | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GroupMetaFactory has the logic for actually assembling and registering a group. | ||||||
|  | // | ||||||
|  | // There are two ways of obtaining one of these. | ||||||
|  | // 1. You can announce your group and versions separately, and then let the | ||||||
|  | //    GroupFactoryRegistry assemble this object for you. (This allows group and | ||||||
|  | //    versions to be imported separately, without referencing each other, to | ||||||
|  | //    keep import trees small.) | ||||||
|  | // 2. You can call NewGroupMetaFactory(), which is mostly a drop-in replacement | ||||||
|  | //    for the old, bad way of doing things. You can then call .Announce() to | ||||||
|  | //    announce your constructed factory to any code that would like to do | ||||||
|  | //    things the new, better way. | ||||||
|  | // | ||||||
|  | // Note that GroupMetaFactory actually does construct GroupMeta objects, but | ||||||
|  | // currently it does so in a way that's very entangled with an | ||||||
|  | // APIRegistrationManager. It's a TODO item to cleanly separate that interface. | ||||||
|  | type GroupMetaFactory struct { | ||||||
|  | 	GroupArgs *GroupMetaFactoryArgs | ||||||
|  | 	// map of version name to version factory | ||||||
|  | 	VersionArgs map[string]*GroupVersionFactoryArgs | ||||||
|  |  | ||||||
|  | 	// assembled by Register() | ||||||
|  | 	prioritizedVersionList []unversioned.GroupVersion | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Register constructs the finalized prioritized version list and sanity checks | ||||||
|  | // the announced group & versions. Then it calls register. | ||||||
|  | func (gmf *GroupMetaFactory) Register(m *registered.APIRegistrationManager) error { | ||||||
|  | 	if gmf.GroupArgs == nil { | ||||||
|  | 		return fmt.Errorf("partially announced groups are not allowed, only got versions: %#v", gmf.VersionArgs) | ||||||
|  | 	} | ||||||
|  | 	if len(gmf.VersionArgs) == 0 { | ||||||
|  | 		return fmt.Errorf("group %v announced but no versions announced", gmf.GroupArgs.GroupName) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pvSet := sets.NewString(gmf.GroupArgs.VersionPreferenceOrder...) | ||||||
|  | 	if pvSet.Len() != len(gmf.GroupArgs.VersionPreferenceOrder) { | ||||||
|  | 		return fmt.Errorf("preference order for group %v has duplicates: %v", gmf.GroupArgs.GroupName, gmf.GroupArgs.VersionPreferenceOrder) | ||||||
|  | 	} | ||||||
|  | 	prioritizedVersions := []unversioned.GroupVersion{} | ||||||
|  | 	for _, v := range gmf.GroupArgs.VersionPreferenceOrder { | ||||||
|  | 		prioritizedVersions = append( | ||||||
|  | 			prioritizedVersions, | ||||||
|  | 			unversioned.GroupVersion{ | ||||||
|  | 				Group:   gmf.GroupArgs.GroupName, | ||||||
|  | 				Version: v, | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Go through versions that weren't explicitly prioritized. | ||||||
|  | 	unprioritizedVersions := []unversioned.GroupVersion{} | ||||||
|  | 	for _, v := range gmf.VersionArgs { | ||||||
|  | 		if v.GroupName != gmf.GroupArgs.GroupName { | ||||||
|  | 			return fmt.Errorf("found %v/%v in group %v?", v.GroupName, v.VersionName, gmf.GroupArgs.GroupName) | ||||||
|  | 		} | ||||||
|  | 		if pvSet.Has(v.VersionName) { | ||||||
|  | 			pvSet.Delete(v.VersionName) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		unprioritizedVersions = append(unprioritizedVersions, unversioned.GroupVersion{Group: v.GroupName, Version: v.VersionName}) | ||||||
|  | 	} | ||||||
|  | 	if len(unprioritizedVersions) > 1 { | ||||||
|  | 		glog.Warningf("group %v has multiple unprioritized versions: %#v. They will have an arbitrary preference order!", gmf.GroupArgs.GroupName, unprioritizedVersions) | ||||||
|  | 	} | ||||||
|  | 	if pvSet.Len() != 0 { | ||||||
|  | 		return fmt.Errorf("group %v has versions in the priority list that were never announced: %s", gmf.GroupArgs.GroupName, pvSet) | ||||||
|  | 	} | ||||||
|  | 	prioritizedVersions = append(prioritizedVersions, unprioritizedVersions...) | ||||||
|  | 	m.RegisterVersions(prioritizedVersions) | ||||||
|  | 	gmf.prioritizedVersionList = prioritizedVersions | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (gmf *GroupMetaFactory) newRESTMapper(scheme *runtime.Scheme, externalVersions []unversioned.GroupVersion, groupMeta *apimachinery.GroupMeta) meta.RESTMapper { | ||||||
|  | 	// the list of kinds that are scoped at the root of the api hierarchy | ||||||
|  | 	// if a kind is not enumerated here, it is assumed to have a namespace scope | ||||||
|  | 	rootScoped := sets.NewString() | ||||||
|  | 	if gmf.GroupArgs.RootScopedKinds != nil { | ||||||
|  | 		rootScoped = gmf.GroupArgs.RootScopedKinds | ||||||
|  | 	} | ||||||
|  | 	ignoredKinds := sets.NewString() | ||||||
|  | 	if gmf.GroupArgs.IgnoredKinds != nil { | ||||||
|  | 		ignoredKinds = gmf.GroupArgs.IgnoredKinds | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return api.NewDefaultRESTMapperFromScheme( | ||||||
|  | 		externalVersions, | ||||||
|  | 		groupMeta.InterfacesFor, | ||||||
|  | 		gmf.GroupArgs.ImportPrefix, | ||||||
|  | 		ignoredKinds, | ||||||
|  | 		rootScoped, | ||||||
|  | 		scheme, | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Enable enables group versions that are allowed, adds methods to the scheme, etc. | ||||||
|  | func (gmf *GroupMetaFactory) Enable(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error { | ||||||
|  | 	externalVersions := []unversioned.GroupVersion{} | ||||||
|  | 	for _, v := range gmf.prioritizedVersionList { | ||||||
|  | 		if !m.IsAllowedVersion(v) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		externalVersions = append(externalVersions, v) | ||||||
|  | 		if err := m.EnableVersions(v); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		gmf.VersionArgs[v.Version].AddToScheme(scheme) | ||||||
|  | 	} | ||||||
|  | 	if len(externalVersions) == 0 { | ||||||
|  | 		glog.V(4).Infof("No version is registered for group %v", gmf.GroupArgs.GroupName) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if gmf.GroupArgs.AddInternalObjectsToScheme != nil { | ||||||
|  | 		gmf.GroupArgs.AddInternalObjectsToScheme(scheme) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	preferredExternalVersion := externalVersions[0] | ||||||
|  | 	accessor := meta.NewAccessor() | ||||||
|  |  | ||||||
|  | 	groupMeta := &apimachinery.GroupMeta{ | ||||||
|  | 		GroupVersion:  preferredExternalVersion, | ||||||
|  | 		GroupVersions: externalVersions, | ||||||
|  | 		SelfLinker:    runtime.SelfLinker(accessor), | ||||||
|  | 	} | ||||||
|  | 	for _, v := range externalVersions { | ||||||
|  | 		gvf := gmf.VersionArgs[v.Version] | ||||||
|  | 		if err := groupMeta.AddVersionInterfaces( | ||||||
|  | 			unversioned.GroupVersion{Group: gvf.GroupName, Version: gvf.VersionName}, | ||||||
|  | 			&meta.VersionInterfaces{ | ||||||
|  | 				ObjectConvertor:  scheme, | ||||||
|  | 				MetadataAccessor: accessor, | ||||||
|  | 			}, | ||||||
|  | 		); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	groupMeta.InterfacesFor = groupMeta.DefaultInterfacesFor | ||||||
|  | 	groupMeta.RESTMapper = gmf.newRESTMapper(scheme, externalVersions, groupMeta) | ||||||
|  |  | ||||||
|  | 	if err := m.RegisterGroup(*groupMeta); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RegisterAndEnable is provided only to allow this code to get added in multiple steps. | ||||||
|  | // It's really bad that this is called in init() methods, but supporting this | ||||||
|  | // temporarily lets us do the change incrementally. | ||||||
|  | func (gmf *GroupMetaFactory) RegisterAndEnable() error { | ||||||
|  | 	if err := gmf.Register(registered.DefaultAPIRegistrationManager); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := gmf.Enable(registered.DefaultAPIRegistrationManager, api.Scheme); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: find a sane way to register the rest mappers. | ||||||
|  | 	api.RegisterRESTMapper(registered.GroupOrDie(gmf.GroupArgs.GroupName).RESTMapper) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -35,7 +35,14 @@ var ( | |||||||
| 	DefaultAPIRegistrationManager = NewOrDie(os.Getenv("KUBE_API_VERSIONS")) | 	DefaultAPIRegistrationManager = NewOrDie(os.Getenv("KUBE_API_VERSIONS")) | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // APIRegistrationManager | // APIRegistrationManager provides the concept of what API groups are enabled. | ||||||
|  | // | ||||||
|  | // TODO: currently, it also provides a "registered" concept. But it's wrong to | ||||||
|  | // have both concepts in the same object. Therefore the "announced" package is | ||||||
|  | // going to take over the registered concept. After all the install packages | ||||||
|  | // are switched to using the announce package instead of this package, then we | ||||||
|  | // can combine the registered/enabled concepts in this object. Simplifying this | ||||||
|  | // isn't easy right now because there are so many callers of this package. | ||||||
| type APIRegistrationManager struct { | type APIRegistrationManager struct { | ||||||
| 	// registeredGroupVersions stores all API group versions for which RegisterGroup is called. | 	// registeredGroupVersions stores all API group versions for which RegisterGroup is called. | ||||||
| 	registeredVersions map[unversioned.GroupVersion]struct{} | 	registeredVersions map[unversioned.GroupVersion]struct{} | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ limitations under the License. | |||||||
| package apimachinery | package apimachinery | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/meta" | 	"k8s.io/kubernetes/pkg/api/meta" | ||||||
| 	"k8s.io/kubernetes/pkg/api/unversioned" | 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||||
| 	"k8s.io/kubernetes/pkg/runtime" | 	"k8s.io/kubernetes/pkg/runtime" | ||||||
| @@ -47,6 +49,45 @@ type GroupMeta struct { | |||||||
| 	RESTMapper meta.RESTMapper | 	RESTMapper meta.RESTMapper | ||||||
|  |  | ||||||
| 	// InterfacesFor returns the default Codec and ResourceVersioner for a given version | 	// InterfacesFor returns the default Codec and ResourceVersioner for a given version | ||||||
| 	// or an error if the version is not known. | 	// string, or an error if the version is not known. | ||||||
|  | 	// TODO: make this stop being a func pointer and always use the default | ||||||
|  | 	// function provided below once every place that populates this field has been changed. | ||||||
| 	InterfacesFor func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) | 	InterfacesFor func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) | ||||||
|  |  | ||||||
|  | 	// InterfacesByVersion stores the per-version interfaces. | ||||||
|  | 	InterfacesByVersion map[unversioned.GroupVersion]*meta.VersionInterfaces | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DefaultInterfacesFor returns the default Codec and ResourceVersioner for a given version | ||||||
|  | // string, or an error if the version is not known. | ||||||
|  | // TODO: Remove the "Default" prefix. | ||||||
|  | func (gm *GroupMeta) DefaultInterfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { | ||||||
|  | 	if v, ok := gm.InterfacesByVersion[version]; ok { | ||||||
|  | 		return v, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, gm.GroupVersions) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddVersionInterfaces adds the given version to the group. Only call during | ||||||
|  | // init, after that GroupMeta objects should be immutable. Not thread safe. | ||||||
|  | // (If you use this, be sure to set .InterfacesFor = .DefaultInterfacesFor) | ||||||
|  | // TODO: remove the "Interfaces" suffix and make this also maintain the | ||||||
|  | // .GroupVersions member. | ||||||
|  | func (gm *GroupMeta) AddVersionInterfaces(version unversioned.GroupVersion, interfaces *meta.VersionInterfaces) error { | ||||||
|  | 	if e, a := gm.GroupVersion.Group, version.Group; a != e { | ||||||
|  | 		return fmt.Errorf("got a version in group %v, but am in group %v", a, e) | ||||||
|  | 	} | ||||||
|  | 	if gm.InterfacesByVersion == nil { | ||||||
|  | 		gm.InterfacesByVersion = make(map[unversioned.GroupVersion]*meta.VersionInterfaces) | ||||||
|  | 	} | ||||||
|  | 	gm.InterfacesByVersion[version] = interfaces | ||||||
|  |  | ||||||
|  | 	// TODO: refactor to make the below error not possible, this function | ||||||
|  | 	// should *set* GroupVersions rather than depend on it. | ||||||
|  | 	for _, v := range gm.GroupVersions { | ||||||
|  | 		if v == version { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return fmt.Errorf("added a version interface without the corresponding version %v being in the list %#v", version, gm.GroupVersions) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								pkg/apimachinery/types_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								pkg/apimachinery/types_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | /* | ||||||
|  | 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 apimachinery | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestAdd(t *testing.T) { | ||||||
|  | 	gm := GroupMeta{ | ||||||
|  | 		GroupVersion: unversioned.GroupVersion{ | ||||||
|  | 			Group:   "test", | ||||||
|  | 			Version: "v1", | ||||||
|  | 		}, | ||||||
|  | 		GroupVersions: []unversioned.GroupVersion{{Group: "test", Version: "v1"}}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gm.AddVersionInterfaces(unversioned.GroupVersion{Group: "test", Version: "v1"}, nil) | ||||||
|  | 	if e, a := 1, len(gm.InterfacesByVersion); e != a { | ||||||
|  | 		t.Errorf("expected %v, got %v", e, a) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// GroupVersions is unchanged | ||||||
|  | 	if e, a := 1, len(gm.GroupVersions); e != a { | ||||||
|  | 		t.Errorf("expected %v, got %v", e, a) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -19,128 +19,25 @@ limitations under the License. | |||||||
| package install | package install | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"k8s.io/kubernetes/pkg/apimachinery/announced" | ||||||
|  |  | ||||||
| 	"github.com/golang/glog" |  | ||||||
|  |  | ||||||
| 	"k8s.io/kubernetes/pkg/api" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/meta" |  | ||||||
| 	"k8s.io/kubernetes/pkg/api/unversioned" |  | ||||||
| 	"k8s.io/kubernetes/pkg/apimachinery" |  | ||||||
| 	"k8s.io/kubernetes/pkg/apimachinery/registered" |  | ||||||
| 	"k8s.io/kubernetes/pkg/apis/batch" | 	"k8s.io/kubernetes/pkg/apis/batch" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/batch/v1" | 	"k8s.io/kubernetes/pkg/apis/batch/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/batch/v2alpha1" | 	"k8s.io/kubernetes/pkg/apis/batch/v2alpha1" | ||||||
| 	"k8s.io/kubernetes/pkg/runtime" |  | ||||||
| 	"k8s.io/kubernetes/pkg/util/sets" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const importPrefix = "k8s.io/kubernetes/pkg/apis/batch" |  | ||||||
|  |  | ||||||
| var accessor = meta.NewAccessor() |  | ||||||
|  |  | ||||||
| // availableVersions lists all known external versions for this group from most preferred to least preferred |  | ||||||
| var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion, v2alpha1.SchemeGroupVersion} |  | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	registered.RegisterVersions(availableVersions) | 	if err := announced.NewGroupMetaFactory( | ||||||
| 	externalVersions := []unversioned.GroupVersion{} | 		&announced.GroupMetaFactoryArgs{ | ||||||
| 	for _, v := range availableVersions { | 			GroupName:                  "batch", | ||||||
| 		if registered.IsAllowedVersion(v) { | 			VersionPreferenceOrder:     []string{"v1", "v2alpha1"}, | ||||||
| 			externalVersions = append(externalVersions, v) | 			ImportPrefix:               "k8s.io/kubernetes/pkg/apis/batch", | ||||||
| 		} | 			AddInternalObjectsToScheme: batch.AddToScheme, | ||||||
| 	} | 		}, | ||||||
| 	if len(externalVersions) == 0 { | 		announced.VersionToSchemeFunc{ | ||||||
| 		glog.V(4).Infof("No version is registered for group %v", batch.GroupName) | 			"v1":       v1.AddToScheme, | ||||||
| 		return | 			"v2alpha1": v2alpha1.AddToScheme, | ||||||
| 	} | 		}, | ||||||
|  | 	).Announce().RegisterAndEnable(); err != nil { | ||||||
| 	if err := registered.EnableVersions(externalVersions...); err != nil { |  | ||||||
| 		glog.V(4).Infof("%v", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if err := enableVersions(externalVersions); err != nil { |  | ||||||
| 		glog.V(4).Infof("%v", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TODO: enableVersions should be centralized rather than spread in each API |  | ||||||
| // group. |  | ||||||
| // We can combine registered.RegisterVersions, registered.EnableVersions and |  | ||||||
| // registered.RegisterGroup once we have moved enableVersions there. |  | ||||||
| func enableVersions(externalVersions []unversioned.GroupVersion) error { |  | ||||||
| 	addVersionsToScheme(externalVersions...) |  | ||||||
| 	preferredExternalVersion := externalVersions[0] |  | ||||||
|  |  | ||||||
| 	groupMeta := apimachinery.GroupMeta{ |  | ||||||
| 		GroupVersion:  preferredExternalVersion, |  | ||||||
| 		GroupVersions: externalVersions, |  | ||||||
| 		RESTMapper:    newRESTMapper(externalVersions), |  | ||||||
| 		SelfLinker:    runtime.SelfLinker(accessor), |  | ||||||
| 		InterfacesFor: interfacesFor, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := registered.RegisterGroup(groupMeta); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	api.RegisterRESTMapper(groupMeta.RESTMapper) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { |  | ||||||
| 	// the list of kinds that are scoped at the root of the api hierarchy |  | ||||||
| 	// if a kind is not enumerated here, it is assumed to have a namespace scope |  | ||||||
| 	rootScoped := sets.NewString() |  | ||||||
|  |  | ||||||
| 	ignoredKinds := sets.NewString() |  | ||||||
|  |  | ||||||
| 	return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // interfacesFor returns the default Codec and ResourceVersioner for a given version |  | ||||||
| // string, or an error if the version is not known. |  | ||||||
| func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { |  | ||||||
| 	switch version { |  | ||||||
| 	case v1.SchemeGroupVersion: |  | ||||||
| 		return &meta.VersionInterfaces{ |  | ||||||
| 			ObjectConvertor:  api.Scheme, |  | ||||||
| 			MetadataAccessor: accessor, |  | ||||||
| 		}, nil |  | ||||||
| 	case v2alpha1.SchemeGroupVersion: |  | ||||||
| 		return &meta.VersionInterfaces{ |  | ||||||
| 			ObjectConvertor:  api.Scheme, |  | ||||||
| 			MetadataAccessor: accessor, |  | ||||||
| 		}, nil |  | ||||||
| 	default: |  | ||||||
| 		g, _ := registered.Group(batch.GroupName) |  | ||||||
| 		return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { |  | ||||||
| 	// add the internal version to Scheme |  | ||||||
| 	if err := batch.AddToScheme(api.Scheme); err != nil { |  | ||||||
| 		// Programmer error, detect immediately |  | ||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
| 	// add the enabled external versions to Scheme |  | ||||||
| 	for _, v := range externalVersions { |  | ||||||
| 		if !registered.IsEnabledVersion(v) { |  | ||||||
| 			glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		switch v { |  | ||||||
| 		case v1.SchemeGroupVersion: |  | ||||||
| 			if err := v1.AddToScheme(api.Scheme); err != nil { |  | ||||||
| 				// Programmer error, detect immediately |  | ||||||
| 				panic(err) |  | ||||||
| 			} |  | ||||||
| 		case v2alpha1.SchemeGroupVersion: |  | ||||||
| 			if err := v2alpha1.AddToScheme(api.Scheme); err != nil { |  | ||||||
| 				// Programmer error, detect immediately |  | ||||||
| 				panic(err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue