Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

controllerrestmapper: implement RESTMappings #323

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion pkg/restmapper/caching.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/klog/v2"
Expand All @@ -17,6 +18,7 @@ import (
// cache is our cache of schema information.
type cache struct {
mutex sync.Mutex
groups map[string]metav1.APIGroup
groupVersions map[schema.GroupVersion]*cachedGroupVersion
}

Expand All @@ -27,6 +29,34 @@ func newCache() *cache {
}
}

// findGroupInfo returns the APIGroup for the specified group, querying discovery if not cached.
// If not found, returns APIGroup{}, false, nil
func (c *cache) findGroupInfo(ctx context.Context, discovery discovery.DiscoveryInterface, groupName string) (metav1.APIGroup, bool, error) {
log := log.FromContext(ctx)

c.mutex.Lock()
defer c.mutex.Unlock()

if c.groups == nil {
log.Info("discovering server groups")
serverGroups, err := discovery.ServerGroups()
if err != nil {
klog.Infof("unexpected error from ServerGroups: %v", err)
return metav1.APIGroup{}, false, fmt.Errorf("error from ServerGroups: %w", err)
}

groups := make(map[string]metav1.APIGroup)
for i := range serverGroups.Groups {
group := &serverGroups.Groups[i]
groups[group.Name] = *group
}
c.groups = groups
}

group, found := c.groups[groupName]
return group, found, nil
}

// cachedGroupVersion caches (all) the resource information for a particular groupversion.
type cachedGroupVersion struct {
gv schema.GroupVersion
Expand Down Expand Up @@ -88,7 +118,7 @@ func (c *cachedGroupVersion) fetch(ctx context.Context, discovery discovery.Disc
if meta.IsNoMatchError(err) || apierrors.IsNotFound(err) {
return nil, nil
} else {
klog.Infof("unexpected error from ServerResourcesForGroupVersion(%v): %w", c.gv, err)
klog.Infof("unexpected error from ServerResourcesForGroupVersion(%v): %v", c.gv, err)
return nil, fmt.Errorf("error from ServerResourcesForGroupVersion(%v): %w", c.gv, err)
}
}
Expand Down
56 changes: 50 additions & 6 deletions pkg/restmapper/controllerrestmapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@ var _ meta.RESTMapper = &ControllerRESTMapper{}

// KindFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
func (m *ControllerRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return schema.GroupVersionKind{}, fmt.Errorf("ControllerRESTMaper does not support KindFor operation")
return schema.GroupVersionKind{}, fmt.Errorf("ControllerRESTMapper does not support KindFor operation")
}

// KindsFor takes a partial resource and returns the list of potential kinds in priority order
func (m *ControllerRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
return nil, fmt.Errorf("ControllerRESTMaper does not support KindsFor operation")
return nil, fmt.Errorf("ControllerRESTMapper does not support KindsFor operation")
}

// ResourceFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
func (m *ControllerRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return schema.GroupVersionResource{}, fmt.Errorf("ControllerRESTMaper does not support ResourceFor operation")
return schema.GroupVersionResource{}, fmt.Errorf("ControllerRESTMapper does not support ResourceFor operation")
}

// ResourcesFor takes a partial resource and returns the list of potential resource in priority order
func (m *ControllerRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return nil, fmt.Errorf("ControllerRESTMaper does not support ResourcesFor operation")
return nil, fmt.Errorf("ControllerRESTMapper does not support ResourcesFor operation")
}

// RESTMapping identifies a preferred resource mapping for the provided group kind.
Expand All @@ -75,9 +75,53 @@ func (m *ControllerRESTMapper) RESTMapping(gk schema.GroupKind, versions ...stri
// version search is provided. Otherwise identifies a preferred resource mapping for
// the provided version(s).
func (m *ControllerRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
return nil, fmt.Errorf("ControllerRESTMaper does not support RESTMappings operation")
ctx := context.TODO()

if len(versions) != 0 {
return nil, fmt.Errorf("ControllerRESTMapper does not support RESTMappings operation with specified versions")
}

group, found, err := m.cache.findGroupInfo(ctx, m.uncached, gk.Group)
if err != nil {
return nil, err
}
if !found {
return nil, &meta.NoResourceMatchError{PartialResource: schema.GroupVersionResource{Group: gk.Group, Resource: gk.Kind}}
}

var mappings []*meta.RESTMapping

if group.PreferredVersion.Version != "" {
gv := schema.GroupVersion{Group: gk.Group, Version: group.PreferredVersion.Version}
mapping, err := m.cache.findRESTMapping(ctx, m.uncached, gv, gk.Kind)
if err != nil {
return nil, err
}
if mapping != nil {
mappings = append(mappings, mapping)
}
}

for i := range group.Versions {
gv := schema.GroupVersion{Group: gk.Group, Version: group.Versions[i].Version}
if gv.Version == group.PreferredVersion.Version {
continue
}
mapping, err := m.cache.findRESTMapping(ctx, m.uncached, gv, gk.Kind)
if err != nil {
return nil, err
}
if mapping != nil {
mappings = append(mappings, mapping)
}
}

if len(mappings) == 0 {
return nil, &meta.NoResourceMatchError{PartialResource: schema.GroupVersionResource{Group: gk.Group, Resource: gk.Kind}}
}
return mappings, nil
}

func (m *ControllerRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return "", fmt.Errorf("ControllerRESTMaper does not support ResourceSingularizer operation")
return "", fmt.Errorf("ControllerRESTMapper does not support ResourceSingularizer operation")
}