mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Automatic merge from submit-queue (batch tested with PRs 60302, 57921, 59042, 60126, 59561). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Remove pkg/client/unversioned **What this PR does / why we need it**: This is removing unused package, and moves the used bits into appropriate placeholders. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #25442 **Special notes for your reviewer**: **Release note**: ```release-note NONE ``` /assign @deads2k /assign @sttts
		
			
				
	
	
		
			452 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
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.
 | 
						|
*/
 | 
						|
 | 
						|
// this file contains factories with no other dependencies
 | 
						|
 | 
						|
package util
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/golang/glog"
 | 
						|
 | 
						|
	"k8s.io/api/core/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/api/meta"
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/labels"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime"
 | 
						|
	"k8s.io/client-go/discovery"
 | 
						|
	"k8s.io/client-go/dynamic"
 | 
						|
	restclient "k8s.io/client-go/rest"
 | 
						|
	scaleclient "k8s.io/client-go/scale"
 | 
						|
	"k8s.io/kubernetes/pkg/api/legacyscheme"
 | 
						|
	"k8s.io/kubernetes/pkg/apis/apps"
 | 
						|
	"k8s.io/kubernetes/pkg/apis/batch"
 | 
						|
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
						|
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
						|
	"k8s.io/kubernetes/pkg/controller"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/categories"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
 | 
						|
	openapivalidation "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/resource"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/validation"
 | 
						|
	"k8s.io/kubernetes/pkg/printers"
 | 
						|
	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
 | 
						|
)
 | 
						|
 | 
						|
type ring1Factory struct {
 | 
						|
	clientAccessFactory ClientAccessFactory
 | 
						|
 | 
						|
	// openAPIGetter loads and caches openapi specs
 | 
						|
	openAPIGetter openAPIGetter
 | 
						|
}
 | 
						|
 | 
						|
type openAPIGetter struct {
 | 
						|
	once   sync.Once
 | 
						|
	getter openapi.Getter
 | 
						|
}
 | 
						|
 | 
						|
func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMappingFactory {
 | 
						|
	f := &ring1Factory{
 | 
						|
		clientAccessFactory: clientAccessFactory,
 | 
						|
	}
 | 
						|
	return f
 | 
						|
}
 | 
						|
 | 
						|
// objectLoader attempts to perform discovery against the server, and will fall back to
 | 
						|
// the built in mapper if necessary. It supports unstructured objects either way, since
 | 
						|
// the underlying Scheme supports Unstructured. The mapper will return converters that can
 | 
						|
// convert versioned types to unstructured and back.
 | 
						|
func (f *ring1Factory) objectLoader() (meta.RESTMapper, runtime.ObjectTyper, error) {
 | 
						|
	discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
 | 
						|
	if err != nil {
 | 
						|
		glog.V(3).Infof("Unable to get a discovery client to find server resources, falling back to hardcoded types: %v", err)
 | 
						|
		return legacyscheme.Registry.RESTMapper(), legacyscheme.Scheme, nil
 | 
						|
	}
 | 
						|
 | 
						|
	groupResources, err := discovery.GetAPIGroupResources(discoveryClient)
 | 
						|
	if err != nil && !discoveryClient.Fresh() {
 | 
						|
		discoveryClient.Invalidate()
 | 
						|
		groupResources, err = discovery.GetAPIGroupResources(discoveryClient)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		glog.V(3).Infof("Unable to retrieve API resources, falling back to hardcoded types: %v", err)
 | 
						|
		return legacyscheme.Registry.RESTMapper(), legacyscheme.Scheme, nil
 | 
						|
	}
 | 
						|
 | 
						|
	// allow conversion between typed and unstructured objects
 | 
						|
	interfaces := meta.InterfacesForUnstructuredConversion(legacyscheme.Registry.InterfacesFor)
 | 
						|
	mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.VersionInterfacesFunc(interfaces))
 | 
						|
	// TODO: should this also indicate it recognizes typed objects?
 | 
						|
	typer := discovery.NewUnstructuredObjectTyper(groupResources, legacyscheme.Scheme)
 | 
						|
	expander := NewShortcutExpander(mapper, discoveryClient)
 | 
						|
	return expander, typer, err
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
 | 
						|
	return meta.NewLazyObjectLoader(f.objectLoader)
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) CategoryExpander() categories.CategoryExpander {
 | 
						|
	legacyExpander := categories.LegacyCategoryExpander
 | 
						|
 | 
						|
	discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
 | 
						|
	if err == nil {
 | 
						|
		// fallback is the legacy expander wrapped with discovery based filtering
 | 
						|
		fallbackExpander, err := categories.NewDiscoveryFilteredExpander(legacyExpander, discoveryClient)
 | 
						|
		CheckErr(err)
 | 
						|
 | 
						|
		// by default use the expander that discovers based on "categories" field from the API
 | 
						|
		discoveryCategoryExpander, err := categories.NewDiscoveryCategoryExpander(fallbackExpander, discoveryClient)
 | 
						|
		CheckErr(err)
 | 
						|
 | 
						|
		return discoveryCategoryExpander
 | 
						|
	}
 | 
						|
 | 
						|
	return legacyExpander
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
 | 
						|
	cfg, err := f.clientAccessFactory.ClientConfig()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if err := setKubernetesDefaults(cfg); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	gvk := mapping.GroupVersionKind
 | 
						|
	switch gvk.Group {
 | 
						|
	case api.GroupName:
 | 
						|
		cfg.APIPath = "/api"
 | 
						|
	default:
 | 
						|
		cfg.APIPath = "/apis"
 | 
						|
	}
 | 
						|
	gv := gvk.GroupVersion()
 | 
						|
	cfg.GroupVersion = &gv
 | 
						|
	return restclient.RESTClientFor(cfg)
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
 | 
						|
	cfg, err := f.clientAccessFactory.BareClientConfig()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if err := restclient.SetKubernetesDefaults(cfg); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	cfg.APIPath = "/apis"
 | 
						|
	if mapping.GroupVersionKind.Group == api.GroupName {
 | 
						|
		cfg.APIPath = "/api"
 | 
						|
	}
 | 
						|
	gv := mapping.GroupVersionKind.GroupVersion()
 | 
						|
	cfg.ContentConfig = dynamic.ContentConfig()
 | 
						|
	cfg.GroupVersion = &gv
 | 
						|
	return restclient.RESTClientFor(cfg)
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (printers.Describer, error) {
 | 
						|
	clientset, err := f.clientAccessFactory.ClientSet()
 | 
						|
	if err != nil {
 | 
						|
		// if we can't make a client for this group/version, go generic if possible
 | 
						|
		if genericDescriber, genericErr := genericDescriber(f.clientAccessFactory, mapping); genericErr == nil {
 | 
						|
			return genericDescriber, nil
 | 
						|
		}
 | 
						|
		// otherwise return the original error
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// try to get a describer
 | 
						|
	if describer, ok := printersinternal.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok {
 | 
						|
		return describer, nil
 | 
						|
	}
 | 
						|
	// if this is a kind we don't have a describer for yet, go generic if possible
 | 
						|
	if genericDescriber, genericErr := genericDescriber(f.clientAccessFactory, mapping); genericErr == nil {
 | 
						|
		return genericDescriber, nil
 | 
						|
	}
 | 
						|
	// otherwise return an unregistered error
 | 
						|
	return nil, fmt.Errorf("no description has been implemented for %s", mapping.GroupVersionKind.String())
 | 
						|
}
 | 
						|
 | 
						|
// helper function to make a generic describer, or return an error
 | 
						|
func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (printers.Describer, error) {
 | 
						|
	clientConfig, err := clientAccessFactory.ClientConfig()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	clientConfigCopy := *clientConfig
 | 
						|
	clientConfigCopy.APIPath = dynamic.LegacyAPIPathResolverFunc(mapping.GroupVersionKind)
 | 
						|
	gv := mapping.GroupVersionKind.GroupVersion()
 | 
						|
	clientConfigCopy.GroupVersion = &gv
 | 
						|
 | 
						|
	// used to fetch the resource
 | 
						|
	dynamicClient, err := dynamic.NewClient(&clientConfigCopy)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// used to get events for the resource
 | 
						|
	clientSet, err := clientAccessFactory.ClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	eventsClient := clientSet.Core()
 | 
						|
 | 
						|
	return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
 | 
						|
	clientset, err := f.clientAccessFactory.ClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	opts, ok := options.(*api.PodLogOptions)
 | 
						|
	if !ok {
 | 
						|
		return nil, errors.New("provided options object is not a PodLogOptions")
 | 
						|
	}
 | 
						|
 | 
						|
	var selector labels.Selector
 | 
						|
	var namespace string
 | 
						|
	switch t := object.(type) {
 | 
						|
	case *api.Pod:
 | 
						|
		return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
 | 
						|
 | 
						|
	case *api.ReplicationController:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector = labels.SelectorFromSet(t.Spec.Selector)
 | 
						|
 | 
						|
	case *extensions.ReplicaSet:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	case *extensions.Deployment:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	case *batch.Job:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	case *apps.StatefulSet:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("cannot get the logs from %T", object)
 | 
						|
	}
 | 
						|
 | 
						|
	sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
 | 
						|
	pod, numPods, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if numPods > 1 {
 | 
						|
		fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
 | 
						|
	}
 | 
						|
	return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
 | 
						|
	clientset, err := f.clientAccessFactory.ClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// create scales getter
 | 
						|
	// TODO(p0lyn0mial): put scalesGetter to a factory
 | 
						|
	discoClient, err := f.clientAccessFactory.DiscoveryClient()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	restClient, err := f.clientAccessFactory.RESTClient()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	mapper, _ := f.Object()
 | 
						|
	resolver := scaleclient.NewDiscoveryScaleKindResolver(discoClient)
 | 
						|
	scalesGetter := scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver)
 | 
						|
	gvk := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource)
 | 
						|
 | 
						|
	return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), clientset.Batch(), scalesGetter, gvk.GroupResource()), nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
 | 
						|
	clientset, clientsetErr := f.clientAccessFactory.ClientSet()
 | 
						|
	reaper, reaperErr := kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), clientset)
 | 
						|
 | 
						|
	if kubectl.IsNoSuchReaperError(reaperErr) {
 | 
						|
		return nil, reaperErr
 | 
						|
	}
 | 
						|
	if clientsetErr != nil {
 | 
						|
		return nil, clientsetErr
 | 
						|
	}
 | 
						|
	return reaper, reaperErr
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
 | 
						|
	external, err := f.clientAccessFactory.KubernetesClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), external)
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) Rollbacker(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
 | 
						|
	external, err := f.clientAccessFactory.KubernetesClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), external)
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
 | 
						|
	clientset, err := f.clientAccessFactory.KubernetesClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) ApproximatePodTemplateForObject(object runtime.Object) (*api.PodTemplateSpec, error) {
 | 
						|
	switch t := object.(type) {
 | 
						|
	case *api.Pod:
 | 
						|
		return &api.PodTemplateSpec{
 | 
						|
			ObjectMeta: t.ObjectMeta,
 | 
						|
			Spec:       t.Spec,
 | 
						|
		}, nil
 | 
						|
	case *api.ReplicationController:
 | 
						|
		return t.Spec.Template, nil
 | 
						|
	case *extensions.ReplicaSet:
 | 
						|
		return &t.Spec.Template, nil
 | 
						|
	case *extensions.DaemonSet:
 | 
						|
		return &t.Spec.Template, nil
 | 
						|
	case *extensions.Deployment:
 | 
						|
		return &t.Spec.Template, nil
 | 
						|
	case *batch.Job:
 | 
						|
		return &t.Spec.Template, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return nil, fmt.Errorf("unable to extract pod template from type %v", reflect.TypeOf(object))
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout time.Duration) (*api.Pod, error) {
 | 
						|
	clientset, err := f.clientAccessFactory.ClientSet()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	var selector labels.Selector
 | 
						|
	var namespace string
 | 
						|
	switch t := object.(type) {
 | 
						|
	case *extensions.ReplicaSet:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector = labels.SelectorFromSet(t.Spec.Selector.MatchLabels)
 | 
						|
 | 
						|
	case *api.ReplicationController:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector = labels.SelectorFromSet(t.Spec.Selector)
 | 
						|
 | 
						|
	case *apps.StatefulSet:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	case *extensions.Deployment:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	case *batch.Job:
 | 
						|
		namespace = t.Namespace
 | 
						|
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid label selector: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
	case *api.Service:
 | 
						|
		namespace = t.Namespace
 | 
						|
		if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
 | 
						|
			return nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
 | 
						|
		}
 | 
						|
		selector = labels.SelectorFromSet(t.Spec.Selector)
 | 
						|
 | 
						|
	case *api.Pod:
 | 
						|
		return t, nil
 | 
						|
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("cannot attach to %T: not implemented", object)
 | 
						|
	}
 | 
						|
 | 
						|
	sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
 | 
						|
	pod, _, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy)
 | 
						|
	return pod, err
 | 
						|
}
 | 
						|
 | 
						|
func (f *ring1Factory) Validator(validate bool) (validation.Schema, error) {
 | 
						|
	if !validate {
 | 
						|
		return validation.NullSchema{}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	resources, err := f.OpenAPISchema()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return validation.ConjunctiveSchema{
 | 
						|
		openapivalidation.NewSchemaValidation(resources),
 | 
						|
		validation.NoDoubleKeySchema{},
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// OpenAPISchema returns metadata and structural information about Kubernetes object definitions.
 | 
						|
func (f *ring1Factory) OpenAPISchema() (openapi.Resources, error) {
 | 
						|
	discovery, err := f.clientAccessFactory.DiscoveryClient()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Lazily initialize the OpenAPIGetter once
 | 
						|
	f.openAPIGetter.once.Do(func() {
 | 
						|
		// Create the caching OpenAPIGetter
 | 
						|
		f.openAPIGetter.getter = openapi.NewOpenAPIGetter(discovery)
 | 
						|
	})
 | 
						|
 | 
						|
	// Delegate to the OpenAPIGetter
 | 
						|
	return f.openAPIGetter.getter.Get()
 | 
						|
}
 |