mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	add summarizing discovery controller and handlers
This commit is contained in:
		@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1.
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  version: v1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1.autoscaling
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: autoscaling
 | 
				
			||||||
 | 
					  version: v1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1.batch
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: batch
 | 
				
			||||||
 | 
					  version: v1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1alpha1.certificates.k8s.io
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: certificates.k8s.io
 | 
				
			||||||
 | 
					  version: v1alpha1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1alpha1.rbac.authorization.k8s.io
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: rbac.authorization.k8s.io
 | 
				
			||||||
 | 
					  version: v1alpha1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1beta1.apps
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: apps
 | 
				
			||||||
 | 
					  version: v1beta1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1beta1.authentication.k8s.io
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: authentication.k8s.io
 | 
				
			||||||
 | 
					  version: v1beta1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1beta1.authorization.k8s.io
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: authorization.k8s.io
 | 
				
			||||||
 | 
					  version: v1beta1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1beta1.extensions
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: extensions
 | 
				
			||||||
 | 
					  version: v1beta1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 150
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1beta1.policy
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: policy
 | 
				
			||||||
 | 
					  version: v1beta1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					apiVersion: apiregistration.k8s.io/v1alpha1
 | 
				
			||||||
 | 
					kind: APIService
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: v1beta1.storage.k8s.io
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  group: storage.k8s.io
 | 
				
			||||||
 | 
					  version: v1beta1
 | 
				
			||||||
 | 
					  service:
 | 
				
			||||||
 | 
					    namespace: default
 | 
				
			||||||
 | 
					    name: kubernetes
 | 
				
			||||||
 | 
					  insecureSkipTLSVerify: true
 | 
				
			||||||
 | 
					  priority: 100
 | 
				
			||||||
@@ -12,16 +12,57 @@ load(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
go_library(
 | 
					go_library(
 | 
				
			||||||
    name = "go_default_library",
 | 
					    name = "go_default_library",
 | 
				
			||||||
    srcs = ["apiserver.go"],
 | 
					    srcs = [
 | 
				
			||||||
 | 
					        "apiserver.go",
 | 
				
			||||||
 | 
					        "apiservice_controller.go",
 | 
				
			||||||
 | 
					        "handler_apis.go",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
    tags = ["automanaged"],
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//cmd/kubernetes-discovery/pkg/apis/apiregistration:go_default_library",
 | 
					        "//cmd/kubernetes-discovery/pkg/apis/apiregistration:go_default_library",
 | 
				
			||||||
        "//cmd/kubernetes-discovery/pkg/apis/apiregistration/v1alpha1:go_default_library",
 | 
					        "//cmd/kubernetes-discovery/pkg/apis/apiregistration/v1alpha1:go_default_library",
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/client/clientset_generated/internalclientset:go_default_library",
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/client/clientset_generated/release_1_5:go_default_library",
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/client/informers:go_default_library",
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/client/informers/apiregistration/internalversion:go_default_library",
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion:go_default_library",
 | 
				
			||||||
        "//cmd/kubernetes-discovery/pkg/registry/apiservice:go_default_library",
 | 
					        "//cmd/kubernetes-discovery/pkg/registry/apiservice:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/api:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/api/errors:go_default_library",
 | 
				
			||||||
        "//pkg/api/rest:go_default_library",
 | 
					        "//pkg/api/rest:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/apiserver:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/apiserver/filters:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/auth/handlers:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/client/cache:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/controller:go_default_library",
 | 
				
			||||||
        "//pkg/genericapiserver:go_default_library",
 | 
					        "//pkg/genericapiserver:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/genericapiserver/filters:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/labels:go_default_library",
 | 
				
			||||||
        "//pkg/registry/generic:go_default_library",
 | 
					        "//pkg/registry/generic:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/runtime:go_default_library",
 | 
				
			||||||
        "//pkg/runtime/schema:go_default_library",
 | 
					        "//pkg/runtime/schema:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/util/runtime:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/util/sets:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/util/wait:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/util/workqueue:go_default_library",
 | 
				
			||||||
        "//pkg/version:go_default_library",
 | 
					        "//pkg/version:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor:github.com/golang/glog",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go_test(
 | 
				
			||||||
 | 
					    name = "go_default_test",
 | 
				
			||||||
 | 
					    srcs = ["handler_apis_test.go"],
 | 
				
			||||||
 | 
					    library = "go_default_library",
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/apis/apiregistration:go_default_library",
 | 
				
			||||||
 | 
					        "//cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/api:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/client/cache:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/runtime:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/util/diff:go_default_library",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,17 +17,34 @@ limitations under the License.
 | 
				
			|||||||
package apiserver
 | 
					package apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/rest"
 | 
						"k8s.io/kubernetes/pkg/api/rest"
 | 
				
			||||||
 | 
						apiserverfilters "k8s.io/kubernetes/pkg/apiserver/filters"
 | 
				
			||||||
 | 
						authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/genericapiserver"
 | 
						"k8s.io/kubernetes/pkg/genericapiserver"
 | 
				
			||||||
 | 
						genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/registry/generic"
 | 
						"k8s.io/kubernetes/pkg/registry/generic"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime/schema"
 | 
						"k8s.io/kubernetes/pkg/runtime/schema"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/wait"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/version"
 | 
						"k8s.io/kubernetes/pkg/version"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration"
 | 
						"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration"
 | 
				
			||||||
	"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration/v1alpha1"
 | 
						"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration/v1alpha1"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/clientset_generated/internalclientset"
 | 
				
			||||||
 | 
						clientset "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/clientset_generated/release_1_5"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/informers"
 | 
				
			||||||
 | 
						listers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion"
 | 
				
			||||||
	apiservicestorage "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/registry/apiservice"
 | 
						apiservicestorage "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/registry/apiservice"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// legacyAPIServiceName is the fixed name of the only non-groupified API version
 | 
				
			||||||
 | 
					const legacyAPIServiceName = "v1."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO move to genericapiserver or something like that
 | 
					// TODO move to genericapiserver or something like that
 | 
				
			||||||
// RESTOptionsGetter is used to construct storage for a particular resource
 | 
					// RESTOptionsGetter is used to construct storage for a particular resource
 | 
				
			||||||
type RESTOptionsGetter interface {
 | 
					type RESTOptionsGetter interface {
 | 
				
			||||||
@@ -44,6 +61,14 @@ type Config struct {
 | 
				
			|||||||
// APIDiscoveryServer contains state for a Kubernetes cluster master/api server.
 | 
					// APIDiscoveryServer contains state for a Kubernetes cluster master/api server.
 | 
				
			||||||
type APIDiscoveryServer struct {
 | 
					type APIDiscoveryServer struct {
 | 
				
			||||||
	GenericAPIServer *genericapiserver.GenericAPIServer
 | 
						GenericAPIServer *genericapiserver.GenericAPIServer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// handledAPIServices tracks which APIServices have already been handled.  Once endpoints are added,
 | 
				
			||||||
 | 
						// the listers that are used keep bits in sync automatically.
 | 
				
			||||||
 | 
						handledAPIServices sets.String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// lister is used to add group handling for /apis/<group> discovery lookups based on
 | 
				
			||||||
 | 
						// controller state
 | 
				
			||||||
 | 
						lister listers.APIServiceLister
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type completedConfig struct {
 | 
					type completedConfig struct {
 | 
				
			||||||
@@ -67,26 +92,108 @@ func (c *Config) SkipComplete() completedConfig {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// New returns a new instance of APIDiscoveryServer from the given config.
 | 
					// New returns a new instance of APIDiscoveryServer from the given config.
 | 
				
			||||||
func (c completedConfig) New() (*APIDiscoveryServer, error) {
 | 
					func (c completedConfig) New() (*APIDiscoveryServer, error) {
 | 
				
			||||||
 | 
						informerFactory := informers.NewSharedInformerFactory(
 | 
				
			||||||
 | 
							internalclientset.NewForConfigOrDie(c.Config.GenericConfig.LoopbackClientConfig),
 | 
				
			||||||
 | 
							clientset.NewForConfigOrDie(c.Config.GenericConfig.LoopbackClientConfig),
 | 
				
			||||||
 | 
							5*time.Minute, // this is effectively used as a refresh interval right now.  Might want to do something nicer later on.
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// most API servers don't need to do this, but we need a custom handler chain to handle the special /apis handling here
 | 
				
			||||||
 | 
						c.Config.GenericConfig.BuildHandlerChainsFunc = (&handlerChainConfig{
 | 
				
			||||||
 | 
							informers: informerFactory,
 | 
				
			||||||
 | 
						}).handlerChain
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
 | 
						genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s := &APIDiscoveryServer{
 | 
						s := &APIDiscoveryServer{
 | 
				
			||||||
		GenericAPIServer: genericServer,
 | 
							GenericAPIServer:   genericServer,
 | 
				
			||||||
 | 
							handledAPIServices: sets.String{},
 | 
				
			||||||
 | 
							lister:             informerFactory.Apiregistration().InternalVersion().APIServices().Lister(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName)
 | 
						apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName)
 | 
				
			||||||
	apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion
 | 
						apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion
 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1alpha1storage := map[string]rest.Storage{}
 | 
						v1alpha1storage := map[string]rest.Storage{}
 | 
				
			||||||
	v1alpha1storage["apiservices"] = apiservicestorage.NewREST(c.RESTOptionsGetter.NewFor(apiregistration.Resource("apiservices")))
 | 
						v1alpha1storage["apiservices"] = apiservicestorage.NewREST(c.RESTOptionsGetter.NewFor(apiregistration.Resource("apiservices")))
 | 
				
			||||||
 | 
					 | 
				
			||||||
	apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage
 | 
						apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
 | 
						if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.GenericAPIServer.AddPostStartHook("start-informers", func(context genericapiserver.PostStartHookContext) error {
 | 
				
			||||||
 | 
							informerFactory.Start(wait.NeverStop)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						s.GenericAPIServer.AddPostStartHook("apiservice-registration-controller", func(context genericapiserver.PostStartHookContext) error {
 | 
				
			||||||
 | 
							apiserviceRegistrationController.Run(wait.NeverStop)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handlerChainConfig is the config used to build the custom handler chain for this api server
 | 
				
			||||||
 | 
					type handlerChainConfig struct {
 | 
				
			||||||
 | 
						informers informers.SharedInformerFactory
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handlerChain is a method to build the handler chain for this API server.  We need a custom handler chain so that we
 | 
				
			||||||
 | 
					// can have custom handling for `/apis`, since we're hosting discovery differently from anyone else and we're hosting
 | 
				
			||||||
 | 
					// the endpoints differently, since we're proxying all groups except for apiregistration.k8s.io.
 | 
				
			||||||
 | 
					func (h *handlerChainConfig) handlerChain(apiHandler http.Handler, c *genericapiserver.Config) (secure, insecure http.Handler) {
 | 
				
			||||||
 | 
						// add this as a filter so that we never collide with "already registered" failures on `/apis`
 | 
				
			||||||
 | 
						handler := WithAPIs(apiHandler, h.informers.Apiregistration().InternalVersion().APIServices())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handler = apiserverfilters.WithAuthorization(handler, c.RequestContextMapper, c.Authorizer)
 | 
				
			||||||
 | 
						handler = apiserverfilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
 | 
				
			||||||
 | 
						// audit to stdout to help with debugging as we get this started
 | 
				
			||||||
 | 
						handler = apiserverfilters.WithAudit(handler, c.RequestContextMapper, os.Stdout)
 | 
				
			||||||
 | 
						handler = authhandlers.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, authhandlers.Unauthorized(c.SupportsBasicAuth))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
 | 
				
			||||||
 | 
						handler = genericfilters.WithPanicRecovery(handler, c.RequestContextMapper)
 | 
				
			||||||
 | 
						handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc)
 | 
				
			||||||
 | 
						handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc)
 | 
				
			||||||
 | 
						handler = apiserverfilters.WithRequestInfo(handler, genericapiserver.NewRequestInfoResolver(c), c.RequestContextMapper)
 | 
				
			||||||
 | 
						handler = api.WithRequestContext(handler, c.RequestContextMapper)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return handler, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddAPIService adds an API service.  It is not thread-safe, so only call it on one thread at a time please.
 | 
				
			||||||
 | 
					// It's a slow moving API, so its ok to run the controller on a single thread
 | 
				
			||||||
 | 
					func (s *APIDiscoveryServer) AddAPIService(apiService *apiregistration.APIService) {
 | 
				
			||||||
 | 
						if s.handledAPIServices.Has(apiService.Name) {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// if we're dealing with the legacy group, we're done here
 | 
				
			||||||
 | 
						if apiService.Name == legacyAPIServiceName {
 | 
				
			||||||
 | 
							s.handledAPIServices.Insert(apiService.Name)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// it's time to register the group discovery endpoint
 | 
				
			||||||
 | 
						groupPath := "/apis/" + apiService.Spec.Group
 | 
				
			||||||
 | 
						groupDiscoveryHandler := &apiGroupHandler{
 | 
				
			||||||
 | 
							groupName: apiService.Spec.Group,
 | 
				
			||||||
 | 
							lister:    s.lister,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// discovery is protected
 | 
				
			||||||
 | 
						s.GenericAPIServer.HandlerContainer.SecretRoutes.Handle(groupPath, groupDiscoveryHandler)
 | 
				
			||||||
 | 
						s.GenericAPIServer.HandlerContainer.SecretRoutes.Handle(groupPath+"/", groupDiscoveryHandler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveAPIService removes the APIService from being handled.  Later on it will disable the proxy endpoint.
 | 
				
			||||||
 | 
					// Right now it does nothing because our handler has to properly 404 itself since muxes don't unregister
 | 
				
			||||||
 | 
					func (s *APIDiscoveryServer) RemoveAPIService(apiServiceName string) {
 | 
				
			||||||
 | 
						if !s.handledAPIServices.Has(apiServiceName) {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										162
									
								
								cmd/kubernetes-discovery/pkg/apiserver/apiservice_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								cmd/kubernetes-discovery/pkg/apiserver/apiservice_controller.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golang/glog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apierrors "k8s.io/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/client/cache"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
 | 
						utilruntime "k8s.io/kubernetes/pkg/util/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/wait"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/workqueue"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration"
 | 
				
			||||||
 | 
						informers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/informers/apiregistration/internalversion"
 | 
				
			||||||
 | 
						listers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type APIHandlerManager interface {
 | 
				
			||||||
 | 
						AddAPIService(apiServer *apiregistration.APIService)
 | 
				
			||||||
 | 
						RemoveAPIService(apiServerName string)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type APIServiceRegistrationController struct {
 | 
				
			||||||
 | 
						apiHandlerManager APIHandlerManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiServerLister listers.APIServiceLister
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// To allow injection for testing.
 | 
				
			||||||
 | 
						syncFn func(key string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						queue workqueue.RateLimitingInterface
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewAPIServiceRegistrationController(apiServerInformer informers.APIServiceInformer, apiHandlerManager APIHandlerManager) *APIServiceRegistrationController {
 | 
				
			||||||
 | 
						c := &APIServiceRegistrationController{
 | 
				
			||||||
 | 
							apiHandlerManager: apiHandlerManager,
 | 
				
			||||||
 | 
							apiServerLister:   apiServerInformer.Lister(),
 | 
				
			||||||
 | 
							queue:             workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "APIServiceRegistrationController"),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiServerInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
 | 
				
			||||||
 | 
							AddFunc:    c.addAPIService,
 | 
				
			||||||
 | 
							UpdateFunc: c.updateAPIService,
 | 
				
			||||||
 | 
							DeleteFunc: c.deleteAPIService,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.syncFn = c.sync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) sync(key string) error {
 | 
				
			||||||
 | 
						apiServer, err := c.apiServerLister.Get(key)
 | 
				
			||||||
 | 
						if apierrors.IsNotFound(err) {
 | 
				
			||||||
 | 
							c.apiHandlerManager.RemoveAPIService(key)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.apiHandlerManager.AddAPIService(apiServer)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) Run(stopCh <-chan struct{}) {
 | 
				
			||||||
 | 
						defer utilruntime.HandleCrash()
 | 
				
			||||||
 | 
						defer c.queue.ShutDown()
 | 
				
			||||||
 | 
						defer glog.Infof("Shutting down APIServiceRegistrationController")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						glog.Infof("Starting APIServiceRegistrationController")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// only start one worker thread since its a slow moving API and the discovery server adding bits
 | 
				
			||||||
 | 
						// aren't threadsafe
 | 
				
			||||||
 | 
						go wait.Until(c.runWorker, time.Second, stopCh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<-stopCh
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) runWorker() {
 | 
				
			||||||
 | 
						for c.processNextWorkItem() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// processNextWorkItem deals with one key off the queue.  It returns false when it's time to quit.
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) processNextWorkItem() bool {
 | 
				
			||||||
 | 
						key, quit := c.queue.Get()
 | 
				
			||||||
 | 
						if quit {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer c.queue.Done(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := c.syncFn(key.(string))
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							c.queue.Forget(key)
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						utilruntime.HandleError(fmt.Errorf("%v failed with : %v", key, err))
 | 
				
			||||||
 | 
						c.queue.AddRateLimited(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) enqueue(obj *apiregistration.APIService) {
 | 
				
			||||||
 | 
						key, err := controller.KeyFunc(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							glog.Errorf("Couldn't get key for object %#v: %v", obj, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.queue.Add(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) addAPIService(obj interface{}) {
 | 
				
			||||||
 | 
						castObj := obj.(*apiregistration.APIService)
 | 
				
			||||||
 | 
						glog.V(4).Infof("Adding %s", castObj.Name)
 | 
				
			||||||
 | 
						c.enqueue(castObj)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) updateAPIService(obj, _ interface{}) {
 | 
				
			||||||
 | 
						castObj := obj.(*apiregistration.APIService)
 | 
				
			||||||
 | 
						glog.V(4).Infof("Updating %s", castObj.Name)
 | 
				
			||||||
 | 
						c.enqueue(castObj)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *APIServiceRegistrationController) deleteAPIService(obj interface{}) {
 | 
				
			||||||
 | 
						castObj, ok := obj.(*apiregistration.APIService)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								glog.Errorf("Couldn't get object from tombstone %#v", obj)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							castObj, ok = tombstone.Obj.(*apiregistration.APIService)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								glog.Errorf("Tombstone contained object that is not expected %#v", obj)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						glog.V(4).Infof("Deleting %q", castObj.Name)
 | 
				
			||||||
 | 
						c.enqueue(castObj)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										177
									
								
								cmd/kubernetes-discovery/pkg/apiserver/handler_apis.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								cmd/kubernetes-discovery/pkg/apiserver/handler_apis.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						apierrors "k8s.io/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apiserver"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/labels"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiregistrationapi "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration"
 | 
				
			||||||
 | 
						apiregistrationv1alpha1api "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration/v1alpha1"
 | 
				
			||||||
 | 
						informers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/informers/apiregistration/internalversion"
 | 
				
			||||||
 | 
						listers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAPIs adds the handling for /apis and /apis/<group: -apiregistration.k8s.io>.
 | 
				
			||||||
 | 
					func WithAPIs(handler http.Handler, informer informers.APIServiceInformer) http.Handler {
 | 
				
			||||||
 | 
						apisHandler := &apisHandler{
 | 
				
			||||||
 | 
							lister:   informer.Lister(),
 | 
				
			||||||
 | 
							delegate: handler,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
							apisHandler.ServeHTTP(w, req)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// apisHandler servers the `/apis` endpoint.
 | 
				
			||||||
 | 
					// This is registered as a filter so that it never collides with any explictly registered endpoints
 | 
				
			||||||
 | 
					type apisHandler struct {
 | 
				
			||||||
 | 
						lister listers.APIServiceLister
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delegate http.Handler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var discoveryGroup = metav1.APIGroup{
 | 
				
			||||||
 | 
						Name: apiregistrationapi.GroupName,
 | 
				
			||||||
 | 
						Versions: []metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								GroupVersion: apiregistrationv1alpha1api.SchemeGroupVersion.String(),
 | 
				
			||||||
 | 
								Version:      apiregistrationv1alpha1api.SchemeGroupVersion.Version,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
							GroupVersion: apiregistrationv1alpha1api.SchemeGroupVersion.String(),
 | 
				
			||||||
 | 
							Version:      apiregistrationv1alpha1api.SchemeGroupVersion.Version,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						// if the URL is for OUR api group, serve it normally
 | 
				
			||||||
 | 
						if strings.HasPrefix(req.URL.Path+"/", "/apis/"+apiregistrationapi.GroupName+"/") {
 | 
				
			||||||
 | 
							r.delegate.ServeHTTP(w, req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// don't handle URLs that aren't /apis
 | 
				
			||||||
 | 
						if req.URL.Path != "/apis" && req.URL.Path != "/apis/" {
 | 
				
			||||||
 | 
							r.delegate.ServeHTTP(w, req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						discoveryGroupList := &metav1.APIGroupList{
 | 
				
			||||||
 | 
							// always add OUR api group to the list first
 | 
				
			||||||
 | 
							Groups: []metav1.APIGroup{discoveryGroup},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiServices, err := r.lister.List(labels.Everything())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						apiServicesByGroup := apiregistrationapi.SortedByGroup(apiServices)
 | 
				
			||||||
 | 
						for _, apiGroupServers := range apiServicesByGroup {
 | 
				
			||||||
 | 
							// skip the legacy group
 | 
				
			||||||
 | 
							if len(apiGroupServers[0].Spec.Group) == 0 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							discoveryGroupList.Groups = append(discoveryGroupList.Groups, *newDiscoveryAPIGroup(apiGroupServers))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						json, err := runtime.Encode(api.Codecs.LegacyCodec(), discoveryGroupList)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, err := w.Write(json); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newDiscoveryAPIGroup(apiServices []*apiregistrationapi.APIService) *metav1.APIGroup {
 | 
				
			||||||
 | 
						apiServicesByGroup := apiregistrationapi.SortedByGroup(apiServices)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						discoveryGroup := &metav1.APIGroup{
 | 
				
			||||||
 | 
							Name: apiServicesByGroup[0].Spec.Group,
 | 
				
			||||||
 | 
							PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
								GroupVersion: apiServicesByGroup[0].Spec.Group + "/" + apiServicesByGroup[0].Spec.Version,
 | 
				
			||||||
 | 
								Version:      apiServicesByGroup[0].Spec.Version,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, apiService := range apiServicesByGroup {
 | 
				
			||||||
 | 
							discoveryGroup.Versions = append(discoveryGroup.Versions,
 | 
				
			||||||
 | 
								metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
									GroupVersion: apiService.Spec.Group + "/" + apiService.Spec.Version,
 | 
				
			||||||
 | 
									Version:      apiService.Spec.Version,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return discoveryGroup
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// apiGroupHandler servers the `/apis/<group>` endpoint.
 | 
				
			||||||
 | 
					type apiGroupHandler struct {
 | 
				
			||||||
 | 
						groupName string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lister listers.APIServiceLister
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						// don't handle URLs that aren't /apis/<groupName>
 | 
				
			||||||
 | 
						if req.URL.Path != "/apis/"+r.groupName && req.URL.Path != "/apis/"+r.groupName+"/" {
 | 
				
			||||||
 | 
							http.Error(w, "", http.StatusNotFound)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiServices, err := r.lister.List(labels.Everything())
 | 
				
			||||||
 | 
						if statusErr, ok := err.(*apierrors.StatusError); ok && err != nil {
 | 
				
			||||||
 | 
							apiserver.WriteRawJSON(int(statusErr.Status().Code), statusErr.Status(), w)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiServicesForGroup := []*apiregistrationapi.APIService{}
 | 
				
			||||||
 | 
						for _, apiService := range apiServices {
 | 
				
			||||||
 | 
							if apiService.Spec.Group == r.groupName {
 | 
				
			||||||
 | 
								apiServicesForGroup = append(apiServicesForGroup, apiService)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(apiServicesForGroup) == 0 {
 | 
				
			||||||
 | 
							http.Error(w, "", http.StatusNotFound)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						json, err := runtime.Encode(api.Codecs.LegacyCodec(), newDiscoveryAPIGroup(apiServicesForGroup))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, err := w.Write(json); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										409
									
								
								cmd/kubernetes-discovery/pkg/apiserver/handler_apis_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										409
									
								
								cmd/kubernetes-discovery/pkg/apiserver/handler_apis_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,409 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"net/http/httputil"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/client/cache"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/diff"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration"
 | 
				
			||||||
 | 
						listers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type delegationHTTPHandler struct {
 | 
				
			||||||
 | 
						called bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *delegationHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						d.called = true
 | 
				
			||||||
 | 
						w.WriteHeader(http.StatusOK)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIsDelegation(t *testing.T) {
 | 
				
			||||||
 | 
						indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
 | 
				
			||||||
 | 
						delegate := &delegationHTTPHandler{}
 | 
				
			||||||
 | 
						handler := &apisHandler{
 | 
				
			||||||
 | 
							lister:   listers.NewAPIServiceLister(indexer),
 | 
				
			||||||
 | 
							delegate: delegate,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server := httptest.NewServer(handler)
 | 
				
			||||||
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pathToDelegation := map[string]bool{
 | 
				
			||||||
 | 
							"/":      true,
 | 
				
			||||||
 | 
							"/apis":  false,
 | 
				
			||||||
 | 
							"/apis/": false,
 | 
				
			||||||
 | 
							"/apis/" + apiregistration.GroupName:                     true,
 | 
				
			||||||
 | 
							"/apis/" + apiregistration.GroupName + "/":               true,
 | 
				
			||||||
 | 
							"/apis/" + apiregistration.GroupName + "/anything":       true,
 | 
				
			||||||
 | 
							"/apis/" + apiregistration.GroupName + "/anything/again": true,
 | 
				
			||||||
 | 
							"/apis/something":                                        true,
 | 
				
			||||||
 | 
							"/apis/something/nested":                                 true,
 | 
				
			||||||
 | 
							"/apis/something/nested/deeper":                          true,
 | 
				
			||||||
 | 
							"/api":     true,
 | 
				
			||||||
 | 
							"/api/v1":  true,
 | 
				
			||||||
 | 
							"/version": true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for path, expectedDelegation := range pathToDelegation {
 | 
				
			||||||
 | 
							delegate.called = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							resp, err := http.Get(server.URL + path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", path, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if resp.StatusCode != http.StatusOK {
 | 
				
			||||||
 | 
								httputil.DumpResponse(resp, true)
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", path, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if e, a := expectedDelegation, delegate.called; e != a {
 | 
				
			||||||
 | 
								t.Errorf("%s: expected %v, got %v", path, e, a)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIs(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							apiservices []*apiregistration.APIService
 | 
				
			||||||
 | 
							expected    *metav1.APIGroupList
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "empty",
 | 
				
			||||||
 | 
								apiservices: []*apiregistration.APIService{},
 | 
				
			||||||
 | 
								expected: &metav1.APIGroupList{
 | 
				
			||||||
 | 
									TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
 | 
				
			||||||
 | 
									Groups: []metav1.APIGroup{
 | 
				
			||||||
 | 
										discoveryGroup,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "simple add",
 | 
				
			||||||
 | 
								apiservices: []*apiregistration.APIService{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v1.foo"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "foo",
 | 
				
			||||||
 | 
											Version:  "v1",
 | 
				
			||||||
 | 
											Priority: 10,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v1.bar"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "bar",
 | 
				
			||||||
 | 
											Version:  "v1",
 | 
				
			||||||
 | 
											Priority: 11,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: &metav1.APIGroupList{
 | 
				
			||||||
 | 
									TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
 | 
				
			||||||
 | 
									Groups: []metav1.APIGroup{
 | 
				
			||||||
 | 
										discoveryGroup,
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name: "foo",
 | 
				
			||||||
 | 
											Versions: []metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													GroupVersion: "foo/v1",
 | 
				
			||||||
 | 
													Version:      "v1",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												GroupVersion: "foo/v1",
 | 
				
			||||||
 | 
												Version:      "v1",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name: "bar",
 | 
				
			||||||
 | 
											Versions: []metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													GroupVersion: "bar/v1",
 | 
				
			||||||
 | 
													Version:      "v1",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												GroupVersion: "bar/v1",
 | 
				
			||||||
 | 
												Version:      "v1",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "sorting",
 | 
				
			||||||
 | 
								apiservices: []*apiregistration.APIService{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v1.foo"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "foo",
 | 
				
			||||||
 | 
											Version:  "v1",
 | 
				
			||||||
 | 
											Priority: 20,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v2.bar"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "bar",
 | 
				
			||||||
 | 
											Version:  "v2",
 | 
				
			||||||
 | 
											Priority: 11,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v2.foo"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "foo",
 | 
				
			||||||
 | 
											Version:  "v2",
 | 
				
			||||||
 | 
											Priority: 1,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v1.bar"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "bar",
 | 
				
			||||||
 | 
											Version:  "v1",
 | 
				
			||||||
 | 
											Priority: 11,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: &metav1.APIGroupList{
 | 
				
			||||||
 | 
									TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
 | 
				
			||||||
 | 
									Groups: []metav1.APIGroup{
 | 
				
			||||||
 | 
										discoveryGroup,
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name: "foo",
 | 
				
			||||||
 | 
											Versions: []metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													GroupVersion: "foo/v2",
 | 
				
			||||||
 | 
													Version:      "v2",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													GroupVersion: "foo/v1",
 | 
				
			||||||
 | 
													Version:      "v1",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												GroupVersion: "foo/v2",
 | 
				
			||||||
 | 
												Version:      "v2",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name: "bar",
 | 
				
			||||||
 | 
											Versions: []metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													GroupVersion: "bar/v1",
 | 
				
			||||||
 | 
													Version:      "v1",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													GroupVersion: "bar/v2",
 | 
				
			||||||
 | 
													Version:      "v2",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
												GroupVersion: "bar/v1",
 | 
				
			||||||
 | 
												Version:      "v1",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tests {
 | 
				
			||||||
 | 
							indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
 | 
				
			||||||
 | 
							delegate := &delegationHTTPHandler{}
 | 
				
			||||||
 | 
							handler := &apisHandler{
 | 
				
			||||||
 | 
								lister:   listers.NewAPIServiceLister(indexer),
 | 
				
			||||||
 | 
								delegate: delegate,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, o := range tc.apiservices {
 | 
				
			||||||
 | 
								indexer.Add(o)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							server := httptest.NewServer(handler)
 | 
				
			||||||
 | 
							defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							resp, err := http.Get(server.URL + "/apis")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							bytes, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							actual := &metav1.APIGroupList{}
 | 
				
			||||||
 | 
							if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), bytes, actual); err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !api.Semantic.DeepEqual(tc.expected, actual) {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, diff.ObjectDiff(tc.expected, actual))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIGroupMissing(t *testing.T) {
 | 
				
			||||||
 | 
						indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
 | 
				
			||||||
 | 
						handler := &apiGroupHandler{
 | 
				
			||||||
 | 
							lister:    listers.NewAPIServiceLister(indexer),
 | 
				
			||||||
 | 
							groupName: "foo",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server := httptest.NewServer(handler)
 | 
				
			||||||
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, err := http.Get(server.URL + "/apis/groupName/foo")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp.StatusCode != http.StatusNotFound {
 | 
				
			||||||
 | 
							t.Fatalf("expected %v, got %v", resp.StatusCode, http.StatusNotFound)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// foo still has no api services for it (like it was deleted), it should 404
 | 
				
			||||||
 | 
						resp, err = http.Get(server.URL + "/apis/groupName/")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resp.StatusCode != http.StatusNotFound {
 | 
				
			||||||
 | 
							t.Fatalf("expected %v, got %v", resp.StatusCode, http.StatusNotFound)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIGroup(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							group       string
 | 
				
			||||||
 | 
							apiservices []*apiregistration.APIService
 | 
				
			||||||
 | 
							expected    *metav1.APIGroup
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:  "sorting",
 | 
				
			||||||
 | 
								group: "foo",
 | 
				
			||||||
 | 
								apiservices: []*apiregistration.APIService{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v1.foo"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "foo",
 | 
				
			||||||
 | 
											Version:  "v1",
 | 
				
			||||||
 | 
											Priority: 20,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v2.bar"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "bar",
 | 
				
			||||||
 | 
											Version:  "v2",
 | 
				
			||||||
 | 
											Priority: 11,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v2.foo"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "foo",
 | 
				
			||||||
 | 
											Version:  "v2",
 | 
				
			||||||
 | 
											Priority: 1,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										ObjectMeta: api.ObjectMeta{Name: "v1.bar"},
 | 
				
			||||||
 | 
										Spec: apiregistration.APIServiceSpec{
 | 
				
			||||||
 | 
											Group:    "bar",
 | 
				
			||||||
 | 
											Version:  "v1",
 | 
				
			||||||
 | 
											Priority: 11,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: &metav1.APIGroup{
 | 
				
			||||||
 | 
									TypeMeta: metav1.TypeMeta{Kind: "APIGroup", APIVersion: "v1"},
 | 
				
			||||||
 | 
									Name:     "foo",
 | 
				
			||||||
 | 
									Versions: []metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											GroupVersion: "foo/v2",
 | 
				
			||||||
 | 
											Version:      "v2",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											GroupVersion: "foo/v1",
 | 
				
			||||||
 | 
											Version:      "v1",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									PreferredVersion: metav1.GroupVersionForDiscovery{
 | 
				
			||||||
 | 
										GroupVersion: "foo/v2",
 | 
				
			||||||
 | 
										Version:      "v2",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tests {
 | 
				
			||||||
 | 
							indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
 | 
				
			||||||
 | 
							handler := &apiGroupHandler{
 | 
				
			||||||
 | 
								lister:    listers.NewAPIServiceLister(indexer),
 | 
				
			||||||
 | 
								groupName: "foo",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, o := range tc.apiservices {
 | 
				
			||||||
 | 
								indexer.Add(o)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							server := httptest.NewServer(handler)
 | 
				
			||||||
 | 
							defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							resp, err := http.Get(server.URL + "/apis/" + tc.group)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if resp.StatusCode != http.StatusOK {
 | 
				
			||||||
 | 
								httputil.DumpResponse(resp, true)
 | 
				
			||||||
 | 
								t.Errorf("%s", tc.name)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							bytes, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							actual := &metav1.APIGroup{}
 | 
				
			||||||
 | 
							if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), bytes, actual); err != nil {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !api.Semantic.DeepEqual(tc.expected, actual) {
 | 
				
			||||||
 | 
								t.Errorf("%s: %v", tc.name, diff.ObjectDiff(tc.expected, actual))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -538,6 +538,9 @@ function start_discovery {
 | 
				
			|||||||
        return
 | 
					        return
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ${CONTROLPLANE_SUDO} cp "${CERT_DIR}/admin.kubeconfig" "${CERT_DIR}/admin-discovery.kubeconfig"
 | 
				
			||||||
 | 
					    ${CONTROLPLANE_SUDO} ${GO_OUT}/kubectl config set-cluster local-up-cluster --kubeconfig="${CERT_DIR}/admin-discovery.kubeconfig" --insecure-skip-tls-verify --server="https://${API_HOST}:${DISCOVERY_SECURE_PORT}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DISCOVERY_SERVER_LOG=/tmp/kubernetes-discovery.log
 | 
					    DISCOVERY_SERVER_LOG=/tmp/kubernetes-discovery.log
 | 
				
			||||||
    ${CONTROLPLANE_SUDO} "${GO_OUT}/kubernetes-discovery" \
 | 
					    ${CONTROLPLANE_SUDO} "${GO_OUT}/kubernetes-discovery" \
 | 
				
			||||||
      --cert-dir="${CERT_DIR}" \
 | 
					      --cert-dir="${CERT_DIR}" \
 | 
				
			||||||
@@ -558,6 +561,9 @@ function start_discovery {
 | 
				
			|||||||
    # Wait for kubernetes-discovery to come up before launching the rest of the components.
 | 
					    # Wait for kubernetes-discovery to come up before launching the rest of the components.
 | 
				
			||||||
    echo "Waiting for kubernetes-discovery to come up"
 | 
					    echo "Waiting for kubernetes-discovery to come up"
 | 
				
			||||||
    kube::util::wait_for_url "https://${API_HOST}:${DISCOVERY_SECURE_PORT}/version" "kubernetes-discovery: " 1 ${WAIT_FOR_URL_API_SERVER} || exit 1
 | 
					    kube::util::wait_for_url "https://${API_HOST}:${DISCOVERY_SECURE_PORT}/version" "kubernetes-discovery: " 1 ${WAIT_FOR_URL_API_SERVER} || exit 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # create the "normal" api services for the core API server
 | 
				
			||||||
 | 
					    ${CONTROLPLANE_SUDO} ${GO_OUT}/kubectl create -f "${KUBE_ROOT}/cmd/kubernetes-discovery/artifacts/core-apiservices" --kubeconfig="${CERT_DIR}/admin-discovery.kubeconfig"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -832,7 +838,6 @@ if [[ "${START_MODE}" != "kubeletonly" ]]; then
 | 
				
			|||||||
  start_etcd
 | 
					  start_etcd
 | 
				
			||||||
  set_service_accounts
 | 
					  set_service_accounts
 | 
				
			||||||
  start_apiserver
 | 
					  start_apiserver
 | 
				
			||||||
  start_discovery
 | 
					 | 
				
			||||||
  start_controller_manager
 | 
					  start_controller_manager
 | 
				
			||||||
  start_kubeproxy
 | 
					  start_kubeproxy
 | 
				
			||||||
  start_kubedns
 | 
					  start_kubedns
 | 
				
			||||||
@@ -842,6 +847,11 @@ if [[ "${START_MODE}" != "nokubelet" ]]; then
 | 
				
			|||||||
  start_kubelet
 | 
					  start_kubelet
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					START_DISCOVERY=${START_DISCOVERY:-false}
 | 
				
			||||||
 | 
					if [[ "${START_DISCOVERY}" = true ]]; then
 | 
				
			||||||
 | 
					  start_discovery
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_success
 | 
					print_success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [[ "${ENABLE_DAEMON}" = false ]]; then
 | 
					if [[ "${ENABLE_DAEMON}" = false ]]; then
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user