2017-02-07 21:33:23 +00:00
/ *
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"
2017-04-07 10:49:53 +00:00
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/pkg/api/meta"
"k8s.io/client-go/pkg/api/unversioned"
"k8s.io/client-go/pkg/apimachinery"
"k8s.io/client-go/pkg/apimachinery/registered"
"k8s.io/client-go/pkg/runtime"
"k8s.io/client-go/pkg/util/sets"
2017-02-07 21:33:23 +00:00
)
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
}
return nil
}