mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			508 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			508 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
| // SPDX-License-Identifier: AGPL-3.0-only
 | |
| // Provenance-includes-location: https://github.com/kubernetes/kubernetes/blob/master/cmd/kube-apiserver/app/aggregator.go
 | |
| // Provenance-includes-license: Apache-2.0
 | |
| // Provenance-includes-copyright: The Kubernetes Authors.
 | |
| // Provenance-includes-location: https://github.com/kubernetes/kubernetes/blob/master/cmd/kube-apiserver/app/server.go
 | |
| // Provenance-includes-license: Apache-2.0
 | |
| // Provenance-includes-copyright: The Kubernetes Authors.
 | |
| 
 | |
| package aggregator
 | |
| 
 | |
| import (
 | |
| 	"crypto/tls"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/spf13/pflag"
 | |
| 
 | |
| 	servicev0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1"
 | |
| 	serviceclientset "github.com/grafana/grafana/pkg/generated/clientset/versioned"
 | |
| 	informersv0alpha1 "github.com/grafana/grafana/pkg/generated/informers/externalversions"
 | |
| 	"github.com/grafana/grafana/pkg/registry/apis/service"
 | |
| 	grafanaAPIServer "github.com/grafana/grafana/pkg/services/grafana-apiserver"
 | |
| 	filestorage "github.com/grafana/grafana/pkg/services/grafana-apiserver/storage/file"
 | |
| 
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apimachinery/pkg/runtime"
 | |
| 	"k8s.io/apimachinery/pkg/runtime/schema"
 | |
| 	"k8s.io/apimachinery/pkg/runtime/serializer"
 | |
| 	utilnet "k8s.io/apimachinery/pkg/util/net"
 | |
| 	"k8s.io/apimachinery/pkg/util/sets"
 | |
| 	openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
 | |
| 	genericfeatures "k8s.io/apiserver/pkg/features"
 | |
| 	genericapiserver "k8s.io/apiserver/pkg/server"
 | |
| 	"k8s.io/apiserver/pkg/server/healthz"
 | |
| 	"k8s.io/apiserver/pkg/server/options"
 | |
| 	"k8s.io/apiserver/pkg/server/resourceconfig"
 | |
| 	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | |
| 	"k8s.io/client-go/informers"
 | |
| 	"k8s.io/client-go/kubernetes/fake"
 | |
| 	"k8s.io/client-go/tools/cache"
 | |
| 	"k8s.io/klog/v2"
 | |
| 	v1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
 | |
| 	v1helper "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper"
 | |
| 	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
 | |
| 	aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
 | |
| 	aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
 | |
| 	apiregistrationclientset "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
 | |
| 	apiregistrationclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1"
 | |
| 	apiregistrationInformers "k8s.io/kube-aggregator/pkg/client/informers/externalversions/apiregistration/v1"
 | |
| 	"k8s.io/kube-aggregator/pkg/controllers/autoregister"
 | |
| 	aggregatoropenapi "k8s.io/kube-aggregator/pkg/generated/openapi"
 | |
| 	"k8s.io/kube-openapi/pkg/common"
 | |
| )
 | |
| 
 | |
| type ExtraOptions struct {
 | |
| 	ProxyClientCertFile string
 | |
| 	ProxyClientKeyFile  string
 | |
| }
 | |
| 
 | |
| // AggregatorServerOptions contains the state for the aggregator apiserver
 | |
| type AggregatorServerOptions struct {
 | |
| 	Builders           []grafanaAPIServer.APIGroupBuilder
 | |
| 	RecommendedOptions *options.RecommendedOptions
 | |
| 	ExtraOptions       *ExtraOptions
 | |
| 	AlternateDNS       []string
 | |
| 
 | |
| 	sharedInformerFactory informersv0alpha1.SharedInformerFactory
 | |
| 
 | |
| 	StdOut io.Writer
 | |
| 	StdErr io.Writer
 | |
| }
 | |
| 
 | |
| func NewAggregatorServerOptions(out, errOut io.Writer) *AggregatorServerOptions {
 | |
| 	return &AggregatorServerOptions{
 | |
| 		StdOut:       out,
 | |
| 		StdErr:       errOut,
 | |
| 		ExtraOptions: &ExtraOptions{},
 | |
| 		Builders: []grafanaAPIServer.APIGroupBuilder{
 | |
| 			service.NewServiceAPIBuilder(),
 | |
| 		},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (o *AggregatorServerOptions) LoadAPIGroupBuilders() error {
 | |
| 	// Install schemas
 | |
| 	for _, b := range o.Builders {
 | |
| 		if err := b.InstallSchema(aggregatorscheme.Scheme); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (o *AggregatorServerOptions) Config(codecs serializer.CodecFactory) (*genericapiserver.RecommendedConfig, error) {
 | |
| 	if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(
 | |
| 		"localhost", o.AlternateDNS, []net.IP{net.IPv4(127, 0, 0, 1)},
 | |
| 	); err != nil {
 | |
| 		return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	o.RecommendedOptions.Authentication.RemoteKubeConfigFileOptional = true
 | |
| 	o.RecommendedOptions.Authorization.RemoteKubeConfigFileOptional = true
 | |
| 
 | |
| 	o.RecommendedOptions.Admission = nil
 | |
| 
 | |
| 	if o.RecommendedOptions.CoreAPI.CoreAPIKubeconfigPath == "" {
 | |
| 		o.RecommendedOptions.CoreAPI = nil
 | |
| 	}
 | |
| 
 | |
| 	serverConfig := genericapiserver.NewRecommendedConfig(codecs)
 | |
| 
 | |
| 	if o.RecommendedOptions.CoreAPI == nil {
 | |
| 		if err := o.ModifiedApplyTo(serverConfig); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	} else {
 | |
| 		if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return serverConfig, nil
 | |
| }
 | |
| 
 | |
| // A copy of ApplyTo in recommended.go, but for >= 0.28, server pkg in apiserver does a bit extra causing
 | |
| // a panic when CoreAPI is set to nil
 | |
| func (o *AggregatorServerOptions) ModifiedApplyTo(config *genericapiserver.RecommendedConfig) error {
 | |
| 	if err := o.RecommendedOptions.Etcd.ApplyTo(&config.Config); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := o.RecommendedOptions.EgressSelector.ApplyTo(&config.Config); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := o.RecommendedOptions.Traces.ApplyTo(config.Config.EgressSelector, &config.Config); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := o.RecommendedOptions.SecureServing.ApplyTo(&config.Config.SecureServing, &config.Config.LoopbackClientConfig); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := o.RecommendedOptions.Authentication.ApplyTo(&config.Config.Authentication, config.SecureServing, config.OpenAPIConfig); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := o.RecommendedOptions.Authorization.ApplyTo(&config.Config.Authorization); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := o.RecommendedOptions.Audit.ApplyTo(&config.Config); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// TODO: determine whether we need flow control (API priority and fairness)
 | |
| 	//if err := o.RecommendedOptions.Features.ApplyTo(&config.Config); err != nil {
 | |
| 	//	return err
 | |
| 	//}
 | |
| 
 | |
| 	if err := o.RecommendedOptions.CoreAPI.ApplyTo(config); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	_, err := o.RecommendedOptions.ExtraAdmissionInitializers(config)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (o *AggregatorServerOptions) getMergedOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
 | |
| 	// Add OpenAPI specs for each group+version
 | |
| 	prerequisiteAPIs := grafanaAPIServer.GetOpenAPIDefinitions(o.Builders)(ref)
 | |
| 	aggregatorAPIs := aggregatoropenapi.GetOpenAPIDefinitions(ref)
 | |
| 
 | |
| 	for k, v := range prerequisiteAPIs {
 | |
| 		aggregatorAPIs[k] = v
 | |
| 	}
 | |
| 
 | |
| 	return aggregatorAPIs
 | |
| }
 | |
| 
 | |
| func (o *AggregatorServerOptions) AddFlags(fs *pflag.FlagSet) {
 | |
| 	if o == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	o.RecommendedOptions.AddFlags(fs)
 | |
| 
 | |
| 	fs.StringVar(&o.ExtraOptions.ProxyClientCertFile, "proxy-client-cert-file", o.ExtraOptions.ProxyClientCertFile,
 | |
| 		"path to proxy client cert file")
 | |
| 
 | |
| 	fs.StringVar(&o.ExtraOptions.ProxyClientKeyFile, "proxy-client-key-file", o.ExtraOptions.ProxyClientKeyFile,
 | |
| 		"path to proxy client cert file")
 | |
| }
 | |
| 
 | |
| func (o *AggregatorServerOptions) CreateAggregatorConfig() (*aggregatorapiserver.Config, error) {
 | |
| 	sharedConfig, err := o.Config(aggregatorscheme.Codecs)
 | |
| 	if err != nil {
 | |
| 		klog.Errorf("Error translating server options to config: %s", err)
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	commandOptions := *o.RecommendedOptions
 | |
| 
 | |
| 	// make a shallow copy to let us twiddle a few things
 | |
| 	// most of the config actually remains the same.  We only need to mess with a couple items related to the particulars of the aggregator
 | |
| 	genericConfig := sharedConfig.Config
 | |
| 
 | |
| 	genericConfig.PostStartHooks = map[string]genericapiserver.PostStartHookConfigEntry{}
 | |
| 	genericConfig.RESTOptionsGetter = nil
 | |
| 	// prevent generic API server from installing the OpenAPI handler. Aggregator server
 | |
| 	// has its own customized OpenAPI handler.
 | |
| 	genericConfig.SkipOpenAPIInstallation = true
 | |
| 	mergedResourceConfig, err := resourceconfig.MergeAPIResourceConfigs(aggregatorapiserver.DefaultAPIResourceConfigSource(), nil, aggregatorscheme.Scheme)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	genericConfig.MergedResourceConfig = mergedResourceConfig
 | |
| 
 | |
| 	namer := openapinamer.NewDefinitionNamer(aggregatorscheme.Scheme)
 | |
| 	genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(o.getMergedOpenAPIDefinitions, namer)
 | |
| 	genericConfig.OpenAPIV3Config.Info.Title = "Kubernetes"
 | |
| 	genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(o.getMergedOpenAPIDefinitions, namer)
 | |
| 	genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
 | |
| 
 | |
| 	if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StorageVersionAPI) &&
 | |
| 		utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerIdentity) {
 | |
| 		// Add StorageVersionPrecondition handler to aggregator-apiserver.
 | |
| 		// The handler will block write requests to built-in resources until the
 | |
| 		// target resources' storage versions are up-to-date.
 | |
| 		genericConfig.BuildHandlerChainFunc = genericapiserver.BuildHandlerChainWithStorageVersionPrecondition
 | |
| 	}
 | |
| 
 | |
| 	// copy the etcd options so we don't mutate originals.
 | |
| 	// we assume that the etcd options have been completed already.  avoid messing with anything outside
 | |
| 	// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
 | |
| 	etcdOptions := *commandOptions.Etcd
 | |
| 	etcdOptions.StorageConfig.Codec = aggregatorscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion,
 | |
| 		v1beta1.SchemeGroupVersion,
 | |
| 		servicev0alpha1.SchemeGroupVersion)
 | |
| 	etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1.SchemeGroupVersion,
 | |
| 		schema.GroupKind{Group: v1beta1.GroupName},
 | |
| 		schema.GroupKind{Group: servicev0alpha1.GROUP})
 | |
| 	// etcdOptions.StorageConfig.Transport.ServerList = []string{"127.0.0.1:2379"}
 | |
| 	etcdOptions.SkipHealthEndpoints = true // avoid double wiring of health checks
 | |
| 	if err := etcdOptions.ApplyTo(&genericConfig); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	genericConfig.RESTOptionsGetter = filestorage.NewRESTOptionsGetter("/tmp/grafana.aggregator", etcdOptions.StorageConfig)
 | |
| 
 | |
| 	versionedInformers := informers.NewSharedInformerFactory(fake.NewSimpleClientset(), 10*time.Minute)
 | |
| 
 | |
| 	serviceClient, err := serviceclientset.NewForConfig(genericConfig.LoopbackClientConfig)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	o.sharedInformerFactory = informersv0alpha1.NewSharedInformerFactory(
 | |
| 		serviceClient,
 | |
| 		5*time.Minute, // this is effectively used as a refresh interval right now.  Might want to do something nicer later on.
 | |
| 	)
 | |
| 	serviceResolver := NewExternalNameResolver(o.sharedInformerFactory.Service().V0alpha1().ExternalNames().Lister())
 | |
| 
 | |
| 	genericConfig.DisabledPostStartHooks = genericConfig.DisabledPostStartHooks.Insert("apiservice-status-available-controller")
 | |
| 	genericConfig.DisabledPostStartHooks = genericConfig.DisabledPostStartHooks.Insert("start-kube-aggregator-informers")
 | |
| 
 | |
| 	aggregatorConfig := &aggregatorapiserver.Config{
 | |
| 		GenericConfig: &genericapiserver.RecommendedConfig{
 | |
| 			Config:                genericConfig,
 | |
| 			SharedInformerFactory: versionedInformers,
 | |
| 			ClientConfig:          genericConfig.LoopbackClientConfig,
 | |
| 		},
 | |
| 		ExtraConfig: aggregatorapiserver.ExtraConfig{
 | |
| 			ProxyClientCertFile: o.ExtraOptions.ProxyClientCertFile,
 | |
| 			ProxyClientKeyFile:  o.ExtraOptions.ProxyClientKeyFile,
 | |
| 			// NOTE: while ProxyTransport can be skipped in the configuration, it allows honoring
 | |
| 			// DISABLE_HTTP2, HTTPS_PROXY and NO_PROXY env vars as needed
 | |
| 			ProxyTransport: createProxyTransport(),
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	aggregatorConfig.ExtraConfig.ServiceResolver = serviceResolver
 | |
| 
 | |
| 	// we need to clear the poststarthooks so we don't add them multiple times to all the servers (that fails)
 | |
| 	aggregatorConfig.GenericConfig.PostStartHooks = map[string]genericapiserver.PostStartHookConfigEntry{}
 | |
| 
 | |
| 	return aggregatorConfig, nil
 | |
| }
 | |
| 
 | |
| func (o *AggregatorServerOptions) CreateAggregatorServer(aggregatorConfig *aggregatorapiserver.Config, delegateAPIServer genericapiserver.DelegationTarget) (*aggregatorapiserver.APIAggregator, error) {
 | |
| 	completedConfig := aggregatorConfig.Complete()
 | |
| 	aggregatorServer, err := completedConfig.NewWithDelegate(delegateAPIServer)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// create controllers for auto-registration
 | |
| 	apiRegistrationClient, err := apiregistrationclient.NewForConfig(aggregatorConfig.GenericConfig.LoopbackClientConfig)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	autoRegistrationController := autoregister.NewAutoRegisterController(aggregatorServer.APIRegistrationInformers.Apiregistration().V1().APIServices(), apiRegistrationClient)
 | |
| 	apiServices := apiServicesToRegister(delegateAPIServer, autoRegistrationController)
 | |
| 
 | |
| 	// Imbue all builtin group-priorities onto the aggregated discovery
 | |
| 	if aggregatorConfig.GenericConfig.AggregatedDiscoveryGroupManager != nil {
 | |
| 		for gv, entry := range apiVersionPriorities {
 | |
| 			aggregatorConfig.GenericConfig.AggregatedDiscoveryGroupManager.SetGroupVersionPriority(metav1.GroupVersion(gv), int(entry.group), int(entry.version))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err = aggregatorServer.GenericAPIServer.AddPostStartHook("kube-apiserver-autoregistration", func(context genericapiserver.PostStartHookContext) error {
 | |
| 		go func() {
 | |
| 			autoRegistrationController.Run(5, context.StopCh)
 | |
| 		}()
 | |
| 		return nil
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	err = aggregatorServer.GenericAPIServer.AddBootSequenceHealthChecks(
 | |
| 		makeAPIServiceAvailableHealthCheck(
 | |
| 			"autoregister-completion",
 | |
| 			apiServices,
 | |
| 			aggregatorServer.APIRegistrationInformers.Apiregistration().V1().APIServices(),
 | |
| 		),
 | |
| 	)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	apiregistrationClient, err := apiregistrationclientset.NewForConfig(completedConfig.GenericConfig.LoopbackClientConfig)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	availableController, err := NewAvailableConditionController(
 | |
| 		aggregatorServer.APIRegistrationInformers.Apiregistration().V1().APIServices(),
 | |
| 		o.sharedInformerFactory.Service().V0alpha1().ExternalNames(),
 | |
| 		apiregistrationClient.ApiregistrationV1(),
 | |
| 		nil,
 | |
| 		(func() ([]byte, []byte))(nil),
 | |
| 		completedConfig.ExtraConfig.ServiceResolver,
 | |
| 	)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	aggregatorServer.GenericAPIServer.AddPostStartHookOrDie("apiservice-status-override-available-controller", func(context genericapiserver.PostStartHookContext) error {
 | |
| 		// if we end up blocking for long periods of time, we may need to increase workers.
 | |
| 		go availableController.Run(5, context.StopCh)
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	aggregatorServer.GenericAPIServer.AddPostStartHookOrDie("start-grafana-aggregator-informers", func(context genericapiserver.PostStartHookContext) error {
 | |
| 		o.sharedInformerFactory.Start(context.StopCh)
 | |
| 		aggregatorServer.APIRegistrationInformers.Start(context.StopCh)
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	// Install the API Group+version
 | |
| 	for _, b := range o.Builders {
 | |
| 		g, err := b.GetAPIGroupInfo(aggregatorscheme.Scheme, aggregatorscheme.Codecs, aggregatorConfig.GenericConfig.RESTOptionsGetter)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if g == nil || len(g.PrioritizedVersions) < 1 {
 | |
| 			continue
 | |
| 		}
 | |
| 		err = aggregatorServer.GenericAPIServer.InstallAPIGroup(g)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return aggregatorServer, nil
 | |
| }
 | |
| 
 | |
| func makeAPIService(gv schema.GroupVersion) *v1.APIService {
 | |
| 	apiServicePriority, ok := apiVersionPriorities[gv]
 | |
| 	if !ok {
 | |
| 		// if we aren't found, then we shouldn't register ourselves because it could result in a CRD group version
 | |
| 		// being permanently stuck in the APIServices list.
 | |
| 		klog.Infof("Skipping APIService creation for %v", gv)
 | |
| 		return nil
 | |
| 	}
 | |
| 	return &v1.APIService{
 | |
| 		ObjectMeta: metav1.ObjectMeta{Name: gv.Version + "." + gv.Group},
 | |
| 		Spec: v1.APIServiceSpec{
 | |
| 			Group:                gv.Group,
 | |
| 			Version:              gv.Version,
 | |
| 			GroupPriorityMinimum: apiServicePriority.group,
 | |
| 			VersionPriority:      apiServicePriority.version,
 | |
| 		},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // makeAPIServiceAvailableHealthCheck returns a healthz check that returns healthy
 | |
| // once all of the specified services have been observed to be available at least once.
 | |
| func makeAPIServiceAvailableHealthCheck(name string, apiServices []*v1.APIService, apiServiceInformer apiregistrationInformers.APIServiceInformer) healthz.HealthChecker {
 | |
| 	// Track the auto-registered API services that have not been observed to be available yet
 | |
| 	pendingServiceNamesLock := &sync.RWMutex{}
 | |
| 	pendingServiceNames := sets.NewString()
 | |
| 	for _, service := range apiServices {
 | |
| 		pendingServiceNames.Insert(service.Name)
 | |
| 	}
 | |
| 
 | |
| 	// When an APIService in the list is seen as available, remove it from the pending list
 | |
| 	handleAPIServiceChange := func(service *v1.APIService) {
 | |
| 		pendingServiceNamesLock.Lock()
 | |
| 		defer pendingServiceNamesLock.Unlock()
 | |
| 		if !pendingServiceNames.Has(service.Name) {
 | |
| 			return
 | |
| 		}
 | |
| 		if v1helper.IsAPIServiceConditionTrue(service, v1.Available) {
 | |
| 			pendingServiceNames.Delete(service.Name)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Watch add/update events for APIServices
 | |
| 	_, _ = apiServiceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | |
| 		AddFunc:    func(obj interface{}) { handleAPIServiceChange(obj.(*v1.APIService)) },
 | |
| 		UpdateFunc: func(old, new interface{}) { handleAPIServiceChange(new.(*v1.APIService)) },
 | |
| 	})
 | |
| 
 | |
| 	// Don't return healthy until the pending list is empty
 | |
| 	return healthz.NamedCheck(name, func(r *http.Request) error {
 | |
| 		pendingServiceNamesLock.RLock()
 | |
| 		defer pendingServiceNamesLock.RUnlock()
 | |
| 		if pendingServiceNames.Len() > 0 {
 | |
| 			return fmt.Errorf("missing APIService: %v", pendingServiceNames.List())
 | |
| 		}
 | |
| 		return nil
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // priority defines group priority that is used in discovery. This controls
 | |
| // group position in the kubectl output.
 | |
| type priority struct {
 | |
| 	// group indicates the order of the group relative to other groups.
 | |
| 	group int32
 | |
| 	// version indicates the relative order of the version inside of its group.
 | |
| 	version int32
 | |
| }
 | |
| 
 | |
| // The proper way to resolve this letting the aggregator know the desired group and version-within-group order of the underlying servers
 | |
| // is to refactor the genericapiserver.DelegationTarget to include a list of priorities based on which APIs were installed.
 | |
| // This requires the APIGroupInfo struct to evolve and include the concept of priorities and to avoid mistakes, the core storage map there needs to be updated.
 | |
| // That ripples out every bit as far as you'd expect, so for 1.7 we'll include the list here instead of being built up during storage.
 | |
| var apiVersionPriorities = map[schema.GroupVersion]priority{
 | |
| 	{Group: "", Version: "v1"}: {group: 18000, version: 1},
 | |
| 	// to my knowledge, nothing below here collides
 | |
| 	{Group: "admissionregistration.k8s.io", Version: "v1"}:       {group: 16700, version: 15},
 | |
| 	{Group: "admissionregistration.k8s.io", Version: "v1beta1"}:  {group: 16700, version: 12},
 | |
| 	{Group: "admissionregistration.k8s.io", Version: "v1alpha1"}: {group: 16700, version: 9},
 | |
| 	// Append a new group to the end of the list if unsure.
 | |
| 	// You can use min(existing group)-100 as the initial value for a group.
 | |
| 	// Version can be set to 9 (to have space around) for a new group.
 | |
| }
 | |
| 
 | |
| func apiServicesToRegister(delegateAPIServer genericapiserver.DelegationTarget, registration autoregister.AutoAPIServiceRegistration) []*v1.APIService {
 | |
| 	apiServices := []*v1.APIService{}
 | |
| 
 | |
| 	for _, curr := range delegateAPIServer.ListedPaths() {
 | |
| 		if curr == "/api/v1" {
 | |
| 			apiService := makeAPIService(schema.GroupVersion{Group: "", Version: "v1"})
 | |
| 			registration.AddAPIServiceToSyncOnStart(apiService)
 | |
| 			apiServices = append(apiServices, apiService)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if !strings.HasPrefix(curr, "/apis/") {
 | |
| 			continue
 | |
| 		}
 | |
| 		// this comes back in a list that looks like /apis/rbac.authorization.k8s.io/v1alpha1
 | |
| 		tokens := strings.Split(curr, "/")
 | |
| 		if len(tokens) != 4 {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		apiService := makeAPIService(schema.GroupVersion{Group: tokens[2], Version: tokens[3]})
 | |
| 		if apiService == nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		registration.AddAPIServiceToSyncOnStart(apiService)
 | |
| 		apiServices = append(apiServices, apiService)
 | |
| 	}
 | |
| 
 | |
| 	return apiServices
 | |
| }
 | |
| 
 | |
| // NOTE: below function imported from https://github.com/kubernetes/kubernetes/blob/master/cmd/kube-apiserver/app/server.go#L197
 | |
| // createProxyTransport creates the dialer infrastructure to connect to the api servers.
 | |
| func createProxyTransport() *http.Transport {
 | |
| 	// NOTE: We don't set proxyDialerFn but the below SetTransportDefaults will
 | |
| 	// See https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/util/net/http.go#L109
 | |
| 	var proxyDialerFn utilnet.DialFunc
 | |
| 	// Proxying to services is IP-based... don't expect to be able to verify the hostname
 | |
| 	proxyTLSClientConfig := &tls.Config{InsecureSkipVerify: true}
 | |
| 	proxyTransport := utilnet.SetTransportDefaults(&http.Transport{
 | |
| 		DialContext:     proxyDialerFn,
 | |
| 		TLSClientConfig: proxyTLSClientConfig,
 | |
| 	})
 | |
| 	return proxyTransport
 | |
| }
 |