diff --git a/cmd/kubernetes-discovery/artifacts/local-cluster-up/kubernetes-discover-pod.yaml b/cmd/kubernetes-discovery/artifacts/local-cluster-up/kubernetes-discover-pod.yaml index 0e65928b291..fe4d84dd5f7 100644 --- a/cmd/kubernetes-discovery/artifacts/local-cluster-up/kubernetes-discover-pod.yaml +++ b/cmd/kubernetes-discovery/artifacts/local-cluster-up/kubernetes-discover-pod.yaml @@ -17,6 +17,24 @@ spec: - name: kubernetes-discovery image: kubernetes-discovery:latest imagePullPolicy: Never + livenessProbe: + failureThreshold: 3 + httpGet: + path: /version + port: 443 + scheme: HTTPS + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + readinessProbe: + failureThreshold: 3 + httpGet: + path: /version + port: 443 + scheme: HTTPS + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 args: - "--proxy-client-cert-file=/var/run/auth-proxy-client/tls.crt" - "--proxy-client-key-file=/var/run/auth-proxy-client/tls.key" diff --git a/cmd/kubernetes-discovery/pkg/apiserver/BUILD b/cmd/kubernetes-discovery/pkg/apiserver/BUILD index 05b0752c848..ab1f47c32e3 100644 --- a/cmd/kubernetes-discovery/pkg/apiserver/BUILD +++ b/cmd/kubernetes-discovery/pkg/apiserver/BUILD @@ -34,6 +34,9 @@ go_library( "//pkg/apiserver/filters:go_default_library", "//pkg/auth/handlers:go_default_library", "//pkg/client/cache:go_default_library", + "//pkg/client/clientset_generated/clientset:go_default_library", + "//pkg/client/informers/informers_generated:go_default_library", + "//pkg/client/listers/core/v1:go_default_library", "//pkg/client/restclient:go_default_library", "//pkg/client/transport:go_default_library", "//pkg/controller:go_default_library", @@ -64,10 +67,12 @@ go_test( "//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/api/v1:go_default_library", "//pkg/apis/meta/v1:go_default_library", "//pkg/apiserver/request:go_default_library", "//pkg/auth/user:go_default_library", "//pkg/client/cache:go_default_library", + "//pkg/client/listers/core/v1:go_default_library", "//pkg/runtime:go_default_library", "//pkg/util/diff:go_default_library", "//pkg/util/sets:go_default_library", diff --git a/cmd/kubernetes-discovery/pkg/apiserver/apiserver.go b/cmd/kubernetes-discovery/pkg/apiserver/apiserver.go index ad7ffdaf634..644892801ba 100644 --- a/cmd/kubernetes-discovery/pkg/apiserver/apiserver.go +++ b/cmd/kubernetes-discovery/pkg/apiserver/apiserver.go @@ -25,6 +25,9 @@ import ( "k8s.io/kubernetes/pkg/api/rest" apiserverfilters "k8s.io/kubernetes/pkg/apiserver/filters" authhandlers "k8s.io/kubernetes/pkg/auth/handlers" + kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + kubeinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated" + v1listers "k8s.io/kubernetes/pkg/client/listers/core/v1" "k8s.io/kubernetes/pkg/genericapiserver" genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters" "k8s.io/kubernetes/pkg/registry/generic" @@ -33,7 +36,7 @@ import ( "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration" "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apis/apiregistration/v1alpha1" - clientset "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/clientset_generated/clientset" + discoveryclientset "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/informers" listers "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/client/listers/apiregistration/internalversion" @@ -44,7 +47,8 @@ import ( const legacyAPIServiceName = "v1." type Config struct { - GenericConfig *genericapiserver.Config + GenericConfig *genericapiserver.Config + CoreAPIServerClient kubeclientset.Interface // ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use // this to confirm the proxy's identity @@ -73,6 +77,11 @@ type APIDiscoveryServer struct { // controller state lister listers.APIServiceLister + // serviceLister is used by the discovery handler to determine whether or not to try to expose the group + serviceLister v1listers.ServiceLister + // endpointsLister is used by the discovery handler to determine whether or not to try to expose the group + endpointsLister v1listers.EndpointsLister + // proxyMux intercepts requests that need to be proxied to backing API servers proxyMux *http.ServeMux } @@ -100,16 +109,19 @@ func (c *Config) SkipComplete() completedConfig { func (c completedConfig) New() (*APIDiscoveryServer, error) { informerFactory := informers.NewSharedInformerFactory( internalclientset.NewForConfigOrDie(c.Config.GenericConfig.LoopbackClientConfig), - clientset.NewForConfigOrDie(c.Config.GenericConfig.LoopbackClientConfig), + discoveryclientset.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. ) + kubeInformers := kubeinformers.NewSharedInformerFactory(nil, c.CoreAPIServerClient, 5*time.Minute) proxyMux := http.NewServeMux() // 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, - proxyMux: proxyMux, + informers: informerFactory, + proxyMux: proxyMux, + serviceLister: kubeInformers.Core().V1().Services().Lister(), + endpointsLister: kubeInformers.Core().V1().Endpoints().Lister(), }).handlerChain genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time @@ -124,6 +136,8 @@ func (c completedConfig) New() (*APIDiscoveryServer, error) { proxyClientKey: c.ProxyClientKey, proxyHandlers: map[string]*proxyHandler{}, lister: informerFactory.Apiregistration().InternalVersion().APIServices().Lister(), + serviceLister: kubeInformers.Core().V1().Services().Lister(), + endpointsLister: kubeInformers.Core().V1().Endpoints().Lister(), proxyMux: proxyMux, } @@ -141,6 +155,7 @@ func (c completedConfig) New() (*APIDiscoveryServer, error) { s.GenericAPIServer.AddPostStartHook("start-informers", func(context genericapiserver.PostStartHookContext) error { informerFactory.Start(wait.NeverStop) + kubeInformers.Start(wait.NeverStop) return nil }) s.GenericAPIServer.AddPostStartHook("apiservice-registration-controller", func(context genericapiserver.PostStartHookContext) error { @@ -153,8 +168,10 @@ func (c completedConfig) New() (*APIDiscoveryServer, error) { // handlerChainConfig is the config used to build the custom handler chain for this api server type handlerChainConfig struct { - informers informers.SharedInformerFactory - proxyMux *http.ServeMux + informers informers.SharedInformerFactory + proxyMux *http.ServeMux + serviceLister v1listers.ServiceLister + endpointsLister v1listers.EndpointsLister } // handlerChain is a method to build the handler chain for this API server. We need a custom handler chain so that we @@ -162,7 +179,7 @@ type handlerChainConfig struct { // 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 := WithAPIs(apiHandler, h.informers.Apiregistration().InternalVersion().APIServices(), h.serviceLister, h.endpointsLister) handler = apiserverfilters.WithAuthorization(handler, c.RequestContextMapper, c.Authorizer) @@ -223,8 +240,10 @@ func (s *APIDiscoveryServer) AddAPIService(apiService *apiregistration.APIServic // it's time to register the group discovery endpoint groupPath := "/apis/" + apiService.Spec.Group groupDiscoveryHandler := &apiGroupHandler{ - groupName: apiService.Spec.Group, - lister: s.lister, + groupName: apiService.Spec.Group, + lister: s.lister, + serviceLister: s.serviceLister, + endpointsLister: s.endpointsLister, } // discovery is protected s.GenericAPIServer.HandlerContainer.UnlistedRoutes.Handle(groupPath, groupDiscoveryHandler) diff --git a/cmd/kubernetes-discovery/pkg/apiserver/handler_apis.go b/cmd/kubernetes-discovery/pkg/apiserver/handler_apis.go index 7b0331e5dac..518f1d0af8e 100644 --- a/cmd/kubernetes-discovery/pkg/apiserver/handler_apis.go +++ b/cmd/kubernetes-discovery/pkg/apiserver/handler_apis.go @@ -24,6 +24,7 @@ import ( apierrors "k8s.io/kubernetes/pkg/api/errors" metav1 "k8s.io/kubernetes/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/apiserver" + v1listers "k8s.io/kubernetes/pkg/client/listers/core/v1" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" @@ -34,10 +35,12 @@ import ( ) // WithAPIs adds the handling for /apis and /apis/. -func WithAPIs(handler http.Handler, informer informers.APIServiceInformer) http.Handler { +func WithAPIs(handler http.Handler, informer informers.APIServiceInformer, serviceLister v1listers.ServiceLister, endpointsLister v1listers.EndpointsLister) http.Handler { apisHandler := &apisHandler{ - lister: informer.Lister(), - delegate: handler, + lister: informer.Lister(), + delegate: handler, + serviceLister: serviceLister, + endpointsLister: endpointsLister, } return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { apisHandler.ServeHTTP(w, req) @@ -49,6 +52,9 @@ func WithAPIs(handler http.Handler, informer informers.APIServiceInformer) http. type apisHandler struct { lister listers.APIServiceLister + serviceLister v1listers.ServiceLister + endpointsLister v1listers.EndpointsLister + delegate http.Handler } @@ -95,7 +101,10 @@ func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { if len(apiGroupServers[0].Spec.Group) == 0 { continue } - discoveryGroupList.Groups = append(discoveryGroupList.Groups, *newDiscoveryAPIGroup(apiGroupServers)) + discoveryGroup := convertToDiscoveryAPIGroup(apiGroupServers, r.serviceLister, r.endpointsLister) + if discoveryGroup != nil { + discoveryGroupList.Groups = append(discoveryGroupList.Groups, *discoveryGroup) + } } json, err := runtime.Encode(api.Codecs.LegacyCodec(), discoveryGroupList) @@ -108,18 +117,46 @@ func (r *apisHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } -func newDiscoveryAPIGroup(apiServices []*apiregistrationapi.APIService) *metav1.APIGroup { +// convertToDiscoveryAPIGroup takes apiservices in a single group and returns a discovery compatible object. +// if none of the services are available, it will return nil. +func convertToDiscoveryAPIGroup(apiServices []*apiregistrationapi.APIService, serviceLister v1listers.ServiceLister, endpointsLister v1listers.EndpointsLister) *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, - }, - } + var discoveryGroup *metav1.APIGroup for _, apiService := range apiServicesByGroup { + // skip any API services without actual services + if _, err := serviceLister.Services(apiService.Spec.Service.Namespace).Get(apiService.Spec.Service.Name); err != nil { + continue + } + + hasActiveEndpoints := false + endpoints, err := endpointsLister.Endpoints(apiService.Spec.Service.Namespace).Get(apiService.Spec.Service.Name) + // skip any API services without endpoints + if err != nil { + continue + } + for _, subset := range endpoints.Subsets { + if len(subset.Addresses) > 0 { + hasActiveEndpoints = true + break + } + } + if !hasActiveEndpoints { + continue + } + + // the first APIService which is valid becomes the default + if discoveryGroup == nil { + discoveryGroup = &metav1.APIGroup{ + Name: apiService.Spec.Group, + PreferredVersion: metav1.GroupVersionForDiscovery{ + GroupVersion: apiService.Spec.Group + "/" + apiService.Spec.Version, + Version: apiService.Spec.Version, + }, + } + } + discoveryGroup.Versions = append(discoveryGroup.Versions, metav1.GroupVersionForDiscovery{ GroupVersion: apiService.Spec.Group + "/" + apiService.Spec.Version, @@ -136,6 +173,9 @@ type apiGroupHandler struct { groupName string lister listers.APIServiceLister + + serviceLister v1listers.ServiceLister + endpointsLister v1listers.EndpointsLister } func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { @@ -167,7 +207,12 @@ func (r *apiGroupHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - json, err := runtime.Encode(api.Codecs.LegacyCodec(), newDiscoveryAPIGroup(apiServicesForGroup)) + discoveryGroup := convertToDiscoveryAPIGroup(apiServicesForGroup, r.serviceLister, r.endpointsLister) + if discoveryGroup == nil { + http.Error(w, "", http.StatusNotFound) + return + } + json, err := runtime.Encode(api.Codecs.LegacyCodec(), discoveryGroup) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/cmd/kubernetes-discovery/pkg/apiserver/handler_apis_test.go b/cmd/kubernetes-discovery/pkg/apiserver/handler_apis_test.go index 606e6ac1333..28b91c8d497 100644 --- a/cmd/kubernetes-discovery/pkg/apiserver/handler_apis_test.go +++ b/cmd/kubernetes-discovery/pkg/apiserver/handler_apis_test.go @@ -24,8 +24,10 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + corev1 "k8s.io/kubernetes/pkg/api/v1" metav1 "k8s.io/kubernetes/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/client/cache" + v1listers "k8s.io/kubernetes/pkg/client/listers/core/v1" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/diff" @@ -111,6 +113,10 @@ func TestAPIs(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v1.foo"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "foo", Version: "v1", Priority: 10, @@ -119,6 +125,10 @@ func TestAPIs(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v1.bar"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "bar", Version: "v1", Priority: 11, @@ -164,6 +174,10 @@ func TestAPIs(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v1.foo"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "foo", Version: "v1", Priority: 20, @@ -172,6 +186,10 @@ func TestAPIs(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v2.bar"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "bar", Version: "v2", Priority: 11, @@ -180,6 +198,10 @@ func TestAPIs(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v2.foo"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "foo", Version: "v2", Priority: 1, @@ -188,6 +210,10 @@ func TestAPIs(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v1.bar"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "bar", Version: "v1", Priority: 11, @@ -239,14 +265,26 @@ func TestAPIs(t *testing.T) { for _, tc := range tests { indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + serviceIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + endpointsIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) delegate := &delegationHTTPHandler{} handler := &apisHandler{ - lister: listers.NewAPIServiceLister(indexer), - delegate: delegate, + serviceLister: v1listers.NewServiceLister(serviceIndexer), + endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer), + lister: listers.NewAPIServiceLister(indexer), + delegate: delegate, } for _, o := range tc.apiservices { indexer.Add(o) } + serviceIndexer.Add(&corev1.Service{ObjectMeta: corev1.ObjectMeta{Namespace: "ns", Name: "api"}}) + endpointsIndexer.Add(&corev1.Endpoints{ + ObjectMeta: corev1.ObjectMeta{Namespace: "ns", Name: "api"}, + Subsets: []corev1.EndpointSubset{ + {Addresses: []corev1.EndpointAddress{{}}}, + }, + }, + ) server := httptest.NewServer(handler) defer server.Close() @@ -316,6 +354,10 @@ func TestAPIGroup(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v1.foo"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "foo", Version: "v1", Priority: 20, @@ -324,6 +366,10 @@ func TestAPIGroup(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v2.bar"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "bar", Version: "v2", Priority: 11, @@ -332,6 +378,10 @@ func TestAPIGroup(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v2.foo"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "foo", Version: "v2", Priority: 1, @@ -340,6 +390,10 @@ func TestAPIGroup(t *testing.T) { { ObjectMeta: api.ObjectMeta{Name: "v1.bar"}, Spec: apiregistration.APIServiceSpec{ + Service: apiregistration.ServiceReference{ + Namespace: "ns", + Name: "api", + }, Group: "bar", Version: "v1", Priority: 11, @@ -369,13 +423,25 @@ func TestAPIGroup(t *testing.T) { for _, tc := range tests { indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + serviceIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + endpointsIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) handler := &apiGroupHandler{ - lister: listers.NewAPIServiceLister(indexer), - groupName: "foo", + lister: listers.NewAPIServiceLister(indexer), + serviceLister: v1listers.NewServiceLister(serviceIndexer), + endpointsLister: v1listers.NewEndpointsLister(endpointsIndexer), + groupName: "foo", } for _, o := range tc.apiservices { indexer.Add(o) } + serviceIndexer.Add(&corev1.Service{ObjectMeta: corev1.ObjectMeta{Namespace: "ns", Name: "api"}}) + endpointsIndexer.Add(&corev1.Endpoints{ + ObjectMeta: corev1.ObjectMeta{Namespace: "ns", Name: "api"}, + Subsets: []corev1.EndpointSubset{ + {Addresses: []corev1.EndpointAddress{{}}}, + }, + }, + ) server := httptest.NewServer(handler) defer server.Close() @@ -386,8 +452,8 @@ func TestAPIGroup(t *testing.T) { continue } if resp.StatusCode != http.StatusOK { - httputil.DumpResponse(resp, true) - t.Errorf("%s", tc.name) + response, _ := httputil.DumpResponse(resp, true) + t.Errorf("%s: %v", tc.name, string(response)) continue } bytes, err := ioutil.ReadAll(resp.Body) diff --git a/cmd/kubernetes-discovery/pkg/cmd/server/BUILD b/cmd/kubernetes-discovery/pkg/cmd/server/BUILD index adafc5ac48e..38b4c8e1414 100644 --- a/cmd/kubernetes-discovery/pkg/cmd/server/BUILD +++ b/cmd/kubernetes-discovery/pkg/cmd/server/BUILD @@ -16,6 +16,8 @@ go_library( "//cmd/kubernetes-discovery/pkg/apiserver:go_default_library", "//cmd/kubernetes-discovery/pkg/legacy:go_default_library", "//pkg/api:go_default_library", + "//pkg/client/clientset_generated/clientset:go_default_library", + "//pkg/client/restclient:go_default_library", "//pkg/genericapiserver:go_default_library", "//pkg/genericapiserver/filters:go_default_library", "//pkg/genericapiserver/options:go_default_library", diff --git a/cmd/kubernetes-discovery/pkg/cmd/server/start.go b/cmd/kubernetes-discovery/pkg/cmd/server/start.go index e40cf76ba8c..de0b7b09794 100644 --- a/cmd/kubernetes-discovery/pkg/cmd/server/start.go +++ b/cmd/kubernetes-discovery/pkg/cmd/server/start.go @@ -27,6 +27,8 @@ import ( "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/apiserver" "k8s.io/kubernetes/cmd/kubernetes-discovery/pkg/legacy" "k8s.io/kubernetes/pkg/api" + kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver/filters" genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" @@ -135,9 +137,19 @@ func (o DiscoveryServerOptions) RunDiscoveryServer() error { return err } + kubeconfig, err := restclient.InClusterConfig() + if err != nil { + return err + } + coreAPIServerClient, err := kubeclientset.NewForConfig(kubeconfig) + if err != nil { + return err + } + config := apiserver.Config{ - GenericConfig: genericAPIServerConfig, - RESTOptionsGetter: &restOptionsFactory{storageConfig: &o.Etcd.StorageConfig}, + GenericConfig: genericAPIServerConfig, + RESTOptionsGetter: &restOptionsFactory{storageConfig: &o.Etcd.StorageConfig}, + CoreAPIServerClient: coreAPIServerClient, } config.ProxyClientCert, err = ioutil.ReadFile(o.ProxyClientCertFile) diff --git a/hack/local-up-discovery.sh b/hack/local-up-discovery.sh index 53fcf0d2d84..6a280081966 100755 --- a/hack/local-up-discovery.sh +++ b/hack/local-up-discovery.sh @@ -59,7 +59,9 @@ function start_discovery { # grant permission to run delegated authentication and authorization checks kubectl_core delete clusterrolebinding discovery:system:auth-delegator > /dev/null 2>&1 || true + kubectl_core delete clusterrolebinding discovery:system:kubernetes-discovery > /dev/null 2>&1 || true kubectl_core create clusterrolebinding discovery:system:auth-delegator --clusterrole=system:auth-delegator --serviceaccount=kube-public:kubernetes-discovery + kubectl_core create clusterrolebinding discovery:system:kubernetes-discovery --clusterrole=system:kubernetes-discovery --serviceaccount=kube-public:kubernetes-discovery # make sure the resources we're about to create don't exist kubectl_core -n kube-public delete secret auth-proxy-client serving-etcd serving-discovery discovery-etcd > /dev/null 2>&1 || true diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go index 5b7ce01b361..05ce8a39211 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go @@ -230,6 +230,14 @@ func ClusterRoles() []rbac.ClusterRole { rbac.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews").RuleOrDie(), }, }, + { + // a role to use for the API registry, summarization, and proxy handling + ObjectMeta: api.ObjectMeta{Name: "system:kubernetes-discovery"}, + Rules: []rbac.PolicyRule{ + // it needs to see all services so that it knows whether the ones it points to exist or not + rbac.NewRule(Read...).Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(), + }, + }, { // a role to use for bootstrapping the kube-controller-manager so it can create the shared informers // service accounts, and secrets that we need to create separate identities for other controllers diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml index 91a15ab653c..289c942936c 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml @@ -469,6 +469,24 @@ items: verbs: - list - watch +- apiVersion: rbac.authorization.k8s.io/v1alpha1 + kind: ClusterRole + metadata: + creationTimestamp: null + labels: + kubernetes.io/bootstrapping: rbac-defaults + name: system:kubernetes-discovery + rules: + - apiGroups: + - "" + attributeRestrictions: null + resources: + - endpoints + - services + verbs: + - get + - list + - watch - apiVersion: rbac.authorization.k8s.io/v1alpha1 kind: ClusterRole metadata: