Skip to content

Commit

Permalink
update restmapper
Browse files Browse the repository at this point in the history
Signed-off-by: Liang Deng <[email protected]>
  • Loading branch information
YTGhost committed Oct 16, 2023
1 parent 71dbacd commit d7c1327
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 336 deletions.
4 changes: 2 additions & 2 deletions pkg/admiralctl/federalize/federalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,12 +258,12 @@ func (o *CommandFederalizeOption) Preflight(f util.Factory, args []string) error

o.resourceObj = obj
o.gvk = obj.GetObjectKind().GroupVersionKind()
o.mapper, err = restmapper.MapperProvider(o.controlPlaneRestConfig)
o.mapper, err = restmapper.GetRESTMapper(o.controlPlaneRestConfig)
if err != nil {
return fmt.Errorf("failed to create restmapper: %w", err)
}

o.gvr, err = restmapper.GetGroupVersionResource(o.mapper, o.gvk)
o.gvr, err = restmapper.ConvertGVKToGVR(o.mapper, o.gvk)
if err != nil {
return fmt.Errorf("failed to get gvr from %q: %w", o.gvk, err)
}
Expand Down
132 changes: 11 additions & 121 deletions pkg/admiralctl/util/restmapper/restmapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,142 +17,32 @@ limitations under the License.
package restmapper

import (
"sync"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
)

// GetGroupVersionResource is a helper to map GVK(schema.GroupVersionKind) to GVR(schema.GroupVersionResource).
func GetGroupVersionResource(restMapper meta.RESTMapper, gvk schema.GroupVersionKind) (schema.GroupVersionResource, error) {
restMapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
func GetRESTMapper(restConfig *rest.Config) (meta.RESTMapper, error) {
dc, err := discovery.NewDiscoveryClientForConfig(restConfig)
if err != nil {
return schema.GroupVersionResource{}, err
}
return restMapping.Resource, nil
}

// cachedRESTMapper caches the previous result to accelerate subsequent queries.
type cachedRESTMapper struct {
restMapper meta.RESTMapper
discoveryClient discovery.DiscoveryInterface
gvkToGVR sync.Map
// mu is used to provide thread-safe mapper reloading.
mu sync.RWMutex
}

func (g *cachedRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return g.getMapper().KindFor(resource)
}

func (g *cachedRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
return g.getMapper().KindsFor(resource)
}

func (g *cachedRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return g.getMapper().ResourceFor(input)
}

func (g *cachedRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return g.getMapper().ResourcesFor(input)
}

func (g *cachedRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
return g.getMapper().RESTMappings(gk, versions...)
}

func (g *cachedRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return g.getMapper().ResourceSingularizer(resource)
}

func (g *cachedRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
// in case of multi-versions or no versions, cachedRESTMapper don't know which is the preferred version,
// so just bypass the cache and consult the underlying mapper.
if len(versions) != 1 {
return g.getMapper().RESTMapping(gk, versions...)
}

gvk := gk.WithVersion(versions[0])
value, ok := g.gvkToGVR.Load(gvk)
if ok { // hit cache, just return
return value.(*meta.RESTMapping), nil
}

// consult underlying mapper and then update cache
restMapping, err := g.getMapper().RESTMapping(gk, versions...)
if meta.IsNoMatchError(err) {
// hit here means a resource might be missing from the current rest mapper,
// probably because a new resource(CRD) has been added, we have to reload
// resource and rebuild the rest mapper.

var groupResources []*restmapper.APIGroupResources
groupResources, err = restmapper.GetAPIGroupResources(g.discoveryClient)
if err != nil {
return nil, err
}

newMapper := restmapper.NewDiscoveryRESTMapper(groupResources)
restMapping, err = newMapper.RESTMapping(gk, versions...)
if err == nil {
// hit here means after reloading, the new rest mapper can recognize
// the resource, we have to replace the mapper and clear cache.
g.mu.Lock()
g.restMapper = newMapper
g.mu.Unlock()
g.gvkToGVR.Range(func(key, value any) bool {
g.gvkToGVR.Delete(key)
return true
})
}
panic(err.Error())
}

grl, err := restmapper.GetAPIGroupResources(dc)
if err != nil {
return restMapping, err
panic(err.Error())
}
g.gvkToGVR.Store(gvk, restMapping)

return restMapping, nil
return restmapper.NewDiscoveryRESTMapper(grl), nil
}

func (g *cachedRESTMapper) getMapper() meta.RESTMapper {
g.mu.RLock()
defer g.mu.RUnlock()
return g.restMapper
}

// NewCachedRESTMapper builds a cachedRESTMapper with a customized underlyingMapper.
// If underlyingMapper is nil, defaults to DiscoveryRESTMapper.
func NewCachedRESTMapper(cfg *rest.Config, underlyingMapper meta.RESTMapper) (meta.RESTMapper, error) {
cachedMapper := cachedRESTMapper{}

// short path, build with customized underlying mapper.
if underlyingMapper != nil {
cachedMapper.restMapper = underlyingMapper
return &cachedMapper, nil
}

client, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
return nil, err
}

// loading current resources for building a base rest mapper.
groupResources, err := restmapper.GetAPIGroupResources(client)
// ConvertGVKToGVR convert GVK to GVR
func ConvertGVKToGVR(restMapper meta.RESTMapper, gvk schema.GroupVersionKind) (schema.GroupVersionResource, error) {
restMapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, err
return schema.GroupVersionResource{}, err
}

cachedMapper.restMapper = restmapper.NewDiscoveryRESTMapper(groupResources)
cachedMapper.discoveryClient = client

return &cachedMapper, nil
}

// MapperProvider is a wrapper of cachedRESTMapper which is typically used
// to generate customized RESTMapper for controller-runtime framework.
func MapperProvider(c *rest.Config) (meta.RESTMapper, error) {
return NewCachedRESTMapper(c, nil)
return restMapping.Resource, nil
}
213 changes: 0 additions & 213 deletions pkg/admiralctl/util/restmapper/restmapper_test.go

This file was deleted.

0 comments on commit d7c1327

Please sign in to comment.