mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
add summarizing discovery controller and handlers
This commit is contained in:
parent
79f497bca7
commit
fb9c109953
@ -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(
|
||||
name = "go_default_library",
|
||||
srcs = ["apiserver.go"],
|
||||
srcs = [
|
||||
"apiserver.go",
|
||||
"apiservice_controller.go",
|
||||
"handler_apis.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubernetes-discovery/pkg/apis/apiregistration: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",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors: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/filters:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/registry/generic:go_default_library",
|
||||
"//pkg/runtime: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",
|
||||
"//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
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"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"
|
||||
genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"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/cmd/kubernetes-discovery/pkg/apis/apiregistration"
|
||||
"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"
|
||||
)
|
||||
|
||||
// legacyAPIServiceName is the fixed name of the only non-groupified API version
|
||||
const legacyAPIServiceName = "v1."
|
||||
|
||||
// TODO move to genericapiserver or something like that
|
||||
// RESTOptionsGetter is used to construct storage for a particular resource
|
||||
type RESTOptionsGetter interface {
|
||||
@ -44,6 +61,14 @@ type Config struct {
|
||||
// APIDiscoveryServer contains state for a Kubernetes cluster master/api server.
|
||||
type APIDiscoveryServer struct {
|
||||
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 {
|
||||
@ -67,26 +92,108 @@ func (c *Config) SkipComplete() completedConfig {
|
||||
|
||||
// New returns a new instance of APIDiscoveryServer from the given config.
|
||||
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
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &APIDiscoveryServer{
|
||||
GenericAPIServer: genericServer,
|
||||
GenericAPIServer: genericServer,
|
||||
handledAPIServices: sets.String{},
|
||||
lister: informerFactory.Apiregistration().InternalVersion().APIServices().Lister(),
|
||||
}
|
||||
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName)
|
||||
apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion
|
||||
|
||||
v1alpha1storage := map[string]rest.Storage{}
|
||||
v1alpha1storage["apiservices"] = apiservicestorage.NewREST(c.RESTOptionsGetter.NewFor(apiregistration.Resource("apiservices")))
|
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage
|
||||
|
||||
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
${CONTROLPLANE_SUDO} "${GO_OUT}/kubernetes-discovery" \
|
||||
--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.
|
||||
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
|
||||
|
||||
# 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
|
||||
set_service_accounts
|
||||
start_apiserver
|
||||
start_discovery
|
||||
start_controller_manager
|
||||
start_kubeproxy
|
||||
start_kubedns
|
||||
@ -842,6 +847,11 @@ if [[ "${START_MODE}" != "nokubelet" ]]; then
|
||||
start_kubelet
|
||||
fi
|
||||
|
||||
START_DISCOVERY=${START_DISCOVERY:-false}
|
||||
if [[ "${START_DISCOVERY}" = true ]]; then
|
||||
start_discovery
|
||||
fi
|
||||
|
||||
print_success
|
||||
|
||||
if [[ "${ENABLE_DAEMON}" = false ]]; then
|
||||
|
Loading…
Reference in New Issue
Block a user