mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 02:34:03 +00:00
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:
commit
5fe3bd449f
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user