ResourceGetter v1beta1 (#5416)
* add resource getter & reader Signed-off-by: Wenhao Zhou <wenhaozhou@yunify.com> * add resource v1beta1 handler * delete gvrToGvk map instead of using the dynamicRESTMapper for getting gvk, and rename the ResourceLister to ResourceGetter * add unregisteredMiddleware filter Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> * add secret contains benchmark & add fieldSelector to resourcev1beta1 Signed-off-by: Wenhao Zhou <wenhaozhou@yunify.com> * delete crds models Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> * delete parameterExtractor and instead of requestInfo Signed-off-by: Wenhao Zhou <wenhaozhou@yunify.com> * add benchmark test * move fieldSelector to DefaultObjectMetaFilter Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> * move fieldSelector to DefaultObjectMetaFilter * change registeredGv type to set Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> * update filter chains Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> * fix fieldSelector cannot work Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> * fix: list known type do not need served label Signed-off-by: wenhaozhou <wenhaozhou@yunify.com> --------- Signed-off-by: Wenhao Zhou <wenhaozhou@yunify.com> Signed-off-by: wenhaozhou <wenhaozhou@yunify.com>
This commit is contained in:
@@ -27,7 +27,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -38,18 +38,15 @@ import (
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/klog/v2"
|
||||
runtimecache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1"
|
||||
iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"
|
||||
notificationv2beta1 "kubesphere.io/api/notification/v2beta1"
|
||||
notificationv2beta2 "kubesphere.io/api/notification/v2beta2"
|
||||
tenantv1alpha1 "kubesphere.io/api/tenant/v1alpha1"
|
||||
typesv1beta1 "kubesphere.io/api/types/v1beta1"
|
||||
runtimecache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
notificationv1 "kubesphere.io/kubesphere/pkg/kapis/notification/v1"
|
||||
notificationkapisv2beta1 "kubesphere.io/kubesphere/pkg/kapis/notification/v2beta1"
|
||||
notificationkapisv2beta2 "kubesphere.io/kubesphere/pkg/kapis/notification/v2beta2"
|
||||
|
||||
audit "kubesphere.io/kubesphere/pkg/apiserver/auditing"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic"
|
||||
@@ -67,6 +64,7 @@ import (
|
||||
apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/dispatch"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/filters"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/proxies"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
alertingv1 "kubesphere.io/kubesphere/pkg/kapis/alerting/v1"
|
||||
@@ -74,7 +72,6 @@ import (
|
||||
alertingv2beta1 "kubesphere.io/kubesphere/pkg/kapis/alerting/v2beta1"
|
||||
clusterkapisv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/cluster/v1alpha1"
|
||||
configv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/config/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/kapis/crd"
|
||||
kapisdevops "kubesphere.io/kubesphere/pkg/kapis/devops"
|
||||
edgeruntimev1alpha1 "kubesphere.io/kubesphere/pkg/kapis/edgeruntime/v1alpha1"
|
||||
gatewayv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/gateway/v1alpha1"
|
||||
@@ -83,6 +80,9 @@ import (
|
||||
meteringv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/metering/v1alpha1"
|
||||
monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3"
|
||||
networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2"
|
||||
notificationv1 "kubesphere.io/kubesphere/pkg/kapis/notification/v1"
|
||||
notificationkapisv2beta1 "kubesphere.io/kubesphere/pkg/kapis/notification/v2beta1"
|
||||
notificationkapisv2beta2 "kubesphere.io/kubesphere/pkg/kapis/notification/v2beta2"
|
||||
"kubesphere.io/kubesphere/pkg/kapis/oauth"
|
||||
openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1"
|
||||
openpitrixv2alpha1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v2alpha1"
|
||||
@@ -101,6 +101,7 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/models/openpitrix"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/loginrecord"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user"
|
||||
resourcev1beta1 "kubesphere.io/kubesphere/pkg/models/resources/v1beta1"
|
||||
"kubesphere.io/kubesphere/pkg/server/healthz"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/auditing"
|
||||
@@ -182,7 +183,6 @@ func (s *APIServer) PrepareRun(stopCh <-chan struct{}) error {
|
||||
})
|
||||
|
||||
s.installKubeSphereAPIs(stopCh)
|
||||
s.installCRDAPIs()
|
||||
s.installMetricsAPI()
|
||||
s.installHealthz()
|
||||
s.container.Filter(monitorRequest)
|
||||
@@ -282,14 +282,6 @@ func (s *APIServer) installKubeSphereAPIs(stopCh <-chan struct{}) {
|
||||
urlruntime.Must(gatewayv1alpha1.AddToContainer(s.container, s.Config.GatewayOptions, s.RuntimeCache, s.RuntimeClient, s.InformerFactory, s.KubernetesClient.Kubernetes(), s.LoggingClient))
|
||||
}
|
||||
|
||||
// installCRDAPIs Install CRDs to the KAPIs with List and Get options
|
||||
func (s *APIServer) installCRDAPIs() {
|
||||
crds := &extv1.CustomResourceDefinitionList{}
|
||||
// TODO Maybe we need a better label name
|
||||
urlruntime.Must(s.RuntimeClient.List(context.TODO(), crds, runtimeclient.MatchingLabels{"kubesphere.io/resource-served": "true"}))
|
||||
urlruntime.Must(crd.AddToContainer(s.container, s.RuntimeClient, s.RuntimeCache, crds))
|
||||
}
|
||||
|
||||
// installHealthz creates the healthz endpoint for this server
|
||||
func (s *APIServer) installHealthz() {
|
||||
urlruntime.Must(healthz.InstallHandler(s.container, []healthz.HealthChecker{}...))
|
||||
@@ -350,6 +342,9 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) {
|
||||
handler := s.Server.Handler
|
||||
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
|
||||
|
||||
middleware := proxies.NewUnregisteredMiddleware(s.container, resourcev1beta1.New(s.RuntimeClient, s.RuntimeCache))
|
||||
handler = filters.WithMiddleware(handler, middleware)
|
||||
|
||||
if s.Config.AuditingOptions.Enable {
|
||||
handler = filters.WithAuditing(handler,
|
||||
audit.NewAuditing(s.InformerFactory, s.Config.AuditingOptions, stopCh))
|
||||
@@ -392,7 +387,6 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) {
|
||||
userLister)))
|
||||
handler = filters.WithAuthentication(handler, authn)
|
||||
handler = filters.WithRequestInfo(handler, requestInfoResolver)
|
||||
|
||||
s.Server.Handler = handler
|
||||
}
|
||||
|
||||
|
||||
21
pkg/apiserver/filters/middleware.go
Normal file
21
pkg/apiserver/filters/middleware.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package filters
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Middleware interface {
|
||||
Handle(w http.ResponseWriter, req *http.Request) bool
|
||||
}
|
||||
|
||||
func WithMiddleware(next http.Handler, middlewares ...Middleware) http.Handler {
|
||||
if middlewares == nil {
|
||||
return next
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
for _, middleware := range middlewares {
|
||||
if middleware.Handle(w, req) {
|
||||
return
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
108
pkg/apiserver/proxies/unregistered.go
Normal file
108
pkg/apiserver/proxies/unregistered.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package proxies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/filters"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1beta1"
|
||||
)
|
||||
|
||||
type unregisteredMiddleware struct {
|
||||
registeredGv sets.String
|
||||
resourceGetter v1beta1.ResourceGetter
|
||||
}
|
||||
|
||||
func NewUnregisteredMiddleware(c *restful.Container, resourceGetter v1beta1.ResourceGetter) filters.Middleware {
|
||||
middleware := &unregisteredMiddleware{
|
||||
registeredGv: sets.NewString(),
|
||||
resourceGetter: resourceGetter,
|
||||
}
|
||||
|
||||
for _, ws := range c.RegisteredWebServices() {
|
||||
rootPath := ws.RootPath()
|
||||
if strings.HasPrefix(rootPath, "/kapis") {
|
||||
middleware.registeredGv.Insert(rootPath)
|
||||
}
|
||||
}
|
||||
|
||||
return middleware
|
||||
}
|
||||
|
||||
func (u *unregisteredMiddleware) Handle(w http.ResponseWriter, req *http.Request) bool {
|
||||
if req.Method != http.MethodGet {
|
||||
return false
|
||||
}
|
||||
|
||||
reqInfo, exist := request.RequestInfoFrom(req.Context())
|
||||
if !exist {
|
||||
return false
|
||||
}
|
||||
|
||||
if reqInfo.IsKubernetesRequest {
|
||||
return false
|
||||
}
|
||||
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: reqInfo.APIGroup,
|
||||
Version: reqInfo.APIVersion,
|
||||
Resource: reqInfo.Resource,
|
||||
}
|
||||
|
||||
if gvr.Group == "" ||
|
||||
gvr.Version == "" ||
|
||||
gvr.Resource == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
rootPath := fmt.Sprintf("/kapis/%s/%s", gvr.Group, gvr.Version)
|
||||
if u.registeredGv.Has(rootPath) {
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
listReq bool
|
||||
q *query.Query
|
||||
)
|
||||
restfulReq := restful.NewRequest(req)
|
||||
restfulResp := restful.NewResponse(w)
|
||||
if reqInfo.Name == "" {
|
||||
listReq = true
|
||||
q = query.ParseQueryParameter(restfulReq)
|
||||
}
|
||||
|
||||
var (
|
||||
result interface{}
|
||||
err error
|
||||
)
|
||||
if listReq {
|
||||
result, err = u.resourceGetter.ListResources(gvr, reqInfo.Namespace, q)
|
||||
} else {
|
||||
result, err = u.resourceGetter.GetResource(gvr, reqInfo.Name, reqInfo.Namespace)
|
||||
}
|
||||
handleResponse(result, err, restfulResp, restfulReq)
|
||||
return true
|
||||
}
|
||||
|
||||
func handleResponse(result interface{}, err error, resp *restful.Response, req *restful.Request) {
|
||||
resp.SetRequestAccepts(restful.MIME_JSON)
|
||||
if err != nil {
|
||||
if err == v1beta1.ErrResourceNotSupported {
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
klog.Error(err)
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
@@ -36,6 +36,7 @@ const (
|
||||
)
|
||||
|
||||
// Query represents api search terms
|
||||
// TODO add fieldSelector
|
||||
type Query struct {
|
||||
Pagination *Pagination
|
||||
|
||||
|
||||
Reference in New Issue
Block a user