move apigateway into apiserver (#1948)

This commit is contained in:
zryfish
2020-03-13 21:57:48 +08:00
committed by GitHub
parent dab71e710b
commit f8e7d06b07
30 changed files with 2057 additions and 257 deletions

View File

@@ -7,9 +7,18 @@ import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/runtime/schema"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/iam"
"kubesphere.io/kubesphere/pkg/apiserver/authentication"
"kubesphere.io/kubesphere/pkg/apiserver/dispatch"
"kubesphere.io/kubesphere/pkg/apiserver/filters"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/informers"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2"
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2"
monitoringv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha2"
@@ -18,14 +27,15 @@ import (
resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2"
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/db"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/simple/client/logging"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"net"
@@ -85,7 +95,7 @@ type APIServer struct {
S3Client s3.Interface
//
DBClient db.Interface
DBClient *mysql.Client
//
LdapClient ldap.Interface
@@ -104,26 +114,31 @@ func (s *APIServer) PrepareRun() error {
s.installKubeSphereAPIs()
for _, ws := range s.container.RegisteredWebServices() {
klog.V(2).Infof("%s", ws.RootPath())
}
s.Server.Handler = s.container
s.buildHandlerChain()
return nil
}
func (s *APIServer) installKubeSphereAPIs() {
urlruntime.Must(resourcev1alpha3.AddToContainer(s.container, s.InformerFactory))
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container))
// Need to refactor devops api registration, too much dependencies
//urlruntime.Must(devopsv1alpha2.AddToContainer(s.container, s.DevopsClient))
urlruntime.Must(devopsv1alpha2.AddToContainer(s.container, s.DevopsClient, s.DBClient.Database(), nil, s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory(), s.S3Client))
urlruntime.Must(loggingv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.LoggingClient))
urlruntime.Must(monitoringv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.MonitoringClient))
urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient))
urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes()))
urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory))
//urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.DBClient))
urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.DBClient.Database()))
urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config()))
urlruntime.Must(iamv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.LdapClient, s.CacheClient, s.AuthenticateOptions))
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container))
}
func (s *APIServer) Run(stopCh <-chan struct{}) error {
@@ -141,6 +156,7 @@ func (s *APIServer) Run(stopCh <-chan struct{}) error {
_ = s.Server.Shutdown(ctx)
}()
klog.V(0).Infof("Start listening on %s", s.Server.Addr)
if s.Server.TLSConfig != nil {
return s.Server.ListenAndServeTLS("", "")
} else {
@@ -148,6 +164,27 @@ func (s *APIServer) Run(stopCh <-chan struct{}) error {
}
}
func (s *APIServer) buildHandlerChain() {
requestInfoResolver := &request.RequestInfoFactory{
APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"),
GrouplessAPIPrefixes: sets.NewString("api", "kapi"),
}
failed := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
handler := s.Server.Handler
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
handler = filters.WithMultipleClusterDispatcher(handler, dispatch.DefaultClusterDispatch)
handler = filters.WithAuthorization(handler, authorizerfactory.NewAlwaysAllowAuthorizer())
handler = filters.WithAuthentication(handler, bearertoken.New(authentication.NewTokenAuthenticator(s.CacheClient)), failed)
handler = filters.WithRequestInfo(handler, requestInfoResolver)
s.Server.Handler = handler
}
func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error {
klog.V(0).Info("Start cache objects")
@@ -338,3 +375,10 @@ func getRequestIP(req *restful.Request) string {
return address
}
type errorResponder struct{}
func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
klog.Error(err)
responsewriters.InternalError(w, req, err)
}

View File

@@ -0,0 +1,29 @@
package authentication
import (
"context"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
)
type TokenAuthenticator struct {
cacheClient cache.Interface
}
func NewTokenAuthenticator(cacheClient cache.Interface) authenticator.Token {
return &TokenAuthenticator{
cacheClient: cacheClient,
}
}
func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
return &authenticator.Response{
User: &user.DefaultInfo{
Name: "admin",
UID: "",
Groups: nil,
Extra: nil,
},
}, true, nil
}

View File

@@ -0,0 +1,36 @@
package dispatch
import (
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"net/http"
"k8s.io/apimachinery/pkg/util/proxy"
)
// Dispatcher defines how to forward request to desired cluster apiserver
type Dispatcher interface {
Dispatch(w http.ResponseWriter, req *http.Request)
}
var DefaultClusterDispatch = newClusterDispatch()
type clusterDispatch struct {
transport *http.Transport
}
func newClusterDispatch() Dispatcher {
return &clusterDispatch{}
}
func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request) {
u := *req.URL
// u.Host = someHost
httpProxy := proxy.NewUpgradeAwareHandler(&u, c.transport, false, false, c)
httpProxy.ServeHTTP(w, req)
}
func (c *clusterDispatch) Error(w http.ResponseWriter, req *http.Request, err error) {
responsewriters.InternalError(w, req, err)
}

View File

@@ -0,0 +1,33 @@
package filters
import (
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/klog"
"net/http"
)
func WithAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler) http.Handler {
if auth == nil {
klog.Warningf("Authentication is disabled")
return handler
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
//authenticationStart := time.Now()
resp, ok, err := auth.AuthenticateRequest(req)
if err != nil || !ok {
if err != nil {
klog.Errorf("Unable to authenticate the request due to error: %v", err)
}
failed.ServeHTTP(w, req)
return
}
// authorization header is not required anymore in case of a successful authentication.
req.Header.Del("Authorization")
req = req.WithContext(request.WithUser(req.Context(), resp.User))
handler.ServeHTTP(w, req)
})
}

View File

@@ -0,0 +1,71 @@
package filters
import (
"context"
"errors"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
k8srequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"net/http"
)
// WithAuthorization passes all authorized requests on to handler, and returns forbidden error otherwise.
func WithAuthorization(handler http.Handler, a authorizer.Authorizer) http.Handler {
if a == nil {
klog.Warningf("Authorization is disabled")
return handler
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
attributes, err := GetAuthorizerAttributes(ctx)
if err != nil {
responsewriters.InternalError(w, req, err)
}
authorized, reason, err := a.Authorize(attributes)
if authorized == authorizer.DecisionAllow {
handler.ServeHTTP(w, req)
return
}
if err != nil {
responsewriters.InternalError(w, req, err)
return
}
klog.V(4).Infof("Forbidden: %#v, Reason: %q", req.RequestURI, reason)
w.WriteHeader(http.StatusForbidden)
})
}
func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) {
attribs := authorizer.AttributesRecord{}
user, ok := k8srequest.UserFrom(ctx)
if ok {
attribs.User = user
}
requestInfo, found := request.RequestInfoFrom(ctx)
if !found {
return nil, errors.New("no RequestInfo found in the context")
}
// Start with common attributes that apply to resource and non-resource requests
attribs.ResourceRequest = requestInfo.IsResourceRequest
attribs.Path = requestInfo.Path
attribs.Verb = requestInfo.Verb
attribs.APIGroup = requestInfo.APIGroup
attribs.APIVersion = requestInfo.APIVersion
attribs.Resource = requestInfo.Resource
attribs.Subresource = requestInfo.Subresource
attribs.Namespace = requestInfo.Namespace
attribs.Name = requestInfo.Name
return &attribs, nil
}

View File

@@ -0,0 +1,32 @@
package filters
import (
"fmt"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/dispatch"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"net/http"
)
// Multiple cluster dispatcher forward request to desired cluster based on request cluster name
// which included in request path clusters/{cluster}
func WithMultipleClusterDispatcher(handler http.Handler, dispatch dispatch.Dispatcher) http.Handler {
if dispatch == nil {
klog.V(4).Infof("Multiple cluster dispatcher is disabled")
return handler
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
info, ok := request.RequestInfoFrom(req.Context())
if !ok {
responsewriters.InternalError(w, req, fmt.Errorf(""))
return
}
if info.Cluster == "" {
handler.ServeHTTP(w, req)
} else {
dispatch.Dispatch(w, req)
}
})
}

View File

@@ -0,0 +1,44 @@
package filters
import (
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/client-go/rest"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/server/errors"
"net/http"
"net/url"
"k8s.io/apimachinery/pkg/util/proxy"
)
// WithKubeAPIServer proxy request to kubernetes service if requests path starts with /api
func WithKubeAPIServer(handler http.Handler, config *rest.Config, failed proxy.ErrorResponder) http.Handler {
kubernetes, _ := url.Parse(config.Host)
defaultTransport, err := rest.TransportFor(config)
if err != nil {
klog.Errorf("Unable to create transport from rest.Config: %v", err)
return handler
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
info, ok := request.RequestInfoFrom(req.Context())
if !ok {
err := errors.New("Unable to retrieve request info from request")
klog.Error(err)
responsewriters.InternalError(w, req, err)
}
if info.IsKubernetesRequest {
s := *req.URL
s.Host = kubernetes.Host
s.Scheme = kubernetes.Scheme
httpProxy := proxy.NewUpgradeAwareHandler(&s, defaultTransport, true, false, failed)
httpProxy.ServeHTTP(w, req)
return
}
handler.ServeHTTP(w, req)
})
}

View File

@@ -0,0 +1,22 @@
package filters
import (
"fmt"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"net/http"
)
func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
info, err := resolver.NewRequestInfo(req)
if err != nil {
responsewriters.InternalError(w, req, fmt.Errorf("failed to crate RequestInfo: %v", err))
return
}
req = req.WithContext(request.WithRequestInfo(ctx, info))
handler.ServeHTTP(w, req)
})
}

View File

@@ -0,0 +1,173 @@
package request
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/util/sets"
"net/http"
"strings"
k8srequest "k8s.io/apiserver/pkg/endpoints/request"
)
type RequestInfoResolver interface {
NewRequestInfo(req *http.Request) (*RequestInfo, error)
}
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
// master's Mux.
var specialVerbs = sets.NewString("proxy", "watch")
var kubernetesAPIPrefixes = sets.NewString("api", "apis")
// RequestInfo holds information parsed from the http.Request,
// extended from k8s.io/apiserver/pkg/endpoints/request/requestinfo.go
type RequestInfo struct {
*k8srequest.RequestInfo
// IsKubeSphereRequest indicates whether or not the request should be handled by kubernetes or kubesphere
IsKubernetesRequest bool
// Workspace of requested namespace, for non-workspaced resources, this may be empty
Workspace string
// Cluster of requested resource, this is empty in single-cluster environment
Cluster string
}
type RequestInfoFactory struct {
APIPrefixes sets.String
GrouplessAPIPrefixes sets.String
k8sRequestInfoFactory *k8srequest.RequestInfoFactory
}
// NewRequestInfo returns the information from the http request. If error is not nil, RequestInfo holds the information as best it is known before the failure
// It handles both resource and non-resource requests and fills in all the pertinent information for each.
// Valid Inputs:
//
// /apis/{api-group}/{version}/namespaces
// /api/{version}/namespaces
// /api/{version}/namespaces/{namespace}
// /api/{version}/namespaces/{namespace}/{resource}
// /api/{version}/namespaces/{namespace}/{resource}/{resourceName}
// /api/{version}/{resource}
// /api/{version}/{resource}/{resourceName}
//
// Special verbs without subresources:
// /api/{version}/proxy/{resource}/{resourceName}
// /api/{version}/proxy/namespaces/{namespace}/{resource}/{resourceName}
//
// Special verbs with subresources:
// /api/{version}/watch/{resource}
// /api/{version}/watch/namespaces/{namespace}/{resource}
//
// /kapis/{api-group}/{version}/workspaces/{workspace}/{resource}/{resourceName}
// /
// /kapis/{api-group}/{version}/namespaces/{namespace}/{resource}
// /kapis/{api-group}/{version}/namespaces/{namespace}/{resource}/{resourceName}
// With workspaces:
// /kapis/{api-group}/{version}/clusters/{cluster}/namespaces/{namespace}/{resource}
// /kapis/{api-group}/{version}/clusters/{cluster}/namespaces/{namespace}/{resource}/{resourceName}
//
func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, error) {
requestInfo := RequestInfo{
IsKubernetesRequest: false,
RequestInfo: &k8srequest.RequestInfo{
Path: req.URL.Path,
Verb: req.Method,
},
}
defer func() {
if kubernetesAPIPrefixes.Has(requestInfo.APIPrefix) {
requestInfo.IsKubernetesRequest = true
}
}()
currentParts := splitPath(req.URL.Path)
if len(currentParts) < 3 {
return &requestInfo, nil
}
if !r.APIPrefixes.Has(currentParts[0]) {
// return a non-resource request
return &requestInfo, nil
}
requestInfo.APIPrefix = currentParts[0]
currentParts = currentParts[1:]
if !r.GrouplessAPIPrefixes.Has(requestInfo.APIPrefix) {
if len(currentParts) < 2 {
return &requestInfo, nil
}
if currentParts[0] == "clusters" {
requestInfo.Cluster = currentParts[1]
currentParts = currentParts[2:]
}
if len(currentParts) < 3 {
return &requestInfo, nil
}
requestInfo.APIGroup = currentParts[0]
currentParts = currentParts[1:]
}
requestInfo.IsResourceRequest = true
requestInfo.APIVersion = currentParts[0]
currentParts = currentParts[1:]
if specialVerbs.Has(currentParts[0]) {
if len(currentParts) < 2 {
return &requestInfo, fmt.Errorf("unable to determine kind and namespace from url: %v", req.URL)
}
requestInfo.Verb = currentParts[0]
currentParts = currentParts[1:]
} else {
switch req.Method {
case "POST":
requestInfo.Verb = "create"
case "GET", "HEAD":
requestInfo.Verb = "get"
case "PUT":
requestInfo.Verb = "update"
case "PATCH":
requestInfo.Verb = "patch"
case "DELETE":
requestInfo.Verb = "delete"
default:
requestInfo.Verb = ""
}
}
return &requestInfo, nil
}
type requestInfoKeyType int
// requestInfoKey is the RequestInfo key for the context. It's of private type here. Because
// keys are interfaces and interfaces are equal when the type and the value is equal, this
// does not conflict with the keys defined in pkg/api.
const requestInfoKey requestInfoKeyType = iota
func WithRequestInfo(parent context.Context, info *RequestInfo) context.Context {
return k8srequest.WithValue(parent, requestInfoKey, info)
}
func RequestInfoFrom(ctx context.Context) (*RequestInfo, bool) {
info, ok := ctx.Value(requestInfoKey).(*RequestInfo)
return info, ok
}
// splitPath returns the segments for a URL path.
func splitPath(path string) []string {
path = strings.Trim(path, "/")
if path == "" {
return []string{}
}
return strings.Split(path, "/")
}

View File

@@ -0,0 +1 @@
package server

View File

@@ -1,70 +0,0 @@
package metrics
import (
"fmt"
"github.com/emicklei/go-restful"
"github.com/kiali/kiali/handlers"
)
// Get app metrics
func GetAppMetrics(request *restful.Request, response *restful.Response) {
handlers.AppMetrics(request, response)
}
// Get workload metrics
func GetWorkloadMetrics(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
workload := request.PathParameter("workload")
if len(namespace) > 0 && len(workload) > 0 {
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s&workload=%s", request.Request.URL.RawQuery, namespace, workload)
}
handlers.WorkloadMetrics(request, response)
}
// Get service metrics
func GetServiceMetrics(request *restful.Request, response *restful.Response) {
handlers.ServiceMetrics(request, response)
}
// Get namespace metrics
func GetNamespaceMetrics(request *restful.Request, response *restful.Response) {
handlers.NamespaceMetrics(request, response)
}
// Get service graph for namespace
func GetNamespaceGraph(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
if len(namespace) > 0 {
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s", request.Request.URL.RawQuery, namespace)
}
handlers.GetNamespaceGraph(request, response)
}
// Get service graph for namespaces
func GetNamespacesGraph(request *restful.Request, response *restful.Response) {
handlers.GraphNamespaces(request, response)
}
// Get namespace health
func GetNamespaceHealth(request *restful.Request, response *restful.Response) {
handlers.NamespaceHealth(request, response)
}
// Get workload health
func GetWorkloadHealth(request *restful.Request, response *restful.Response) {
handlers.WorkloadHealth(request, response)
}
// Get app health
func GetAppHealth(request *restful.Request, response *restful.Response) {
handlers.AppHealth(request, response)
}
// Get service health
func GetServiceHealth(request *restful.Request, response *restful.Response) {
handlers.ServiceHealth(request, response)
}

View File

@@ -1,45 +0,0 @@
package tracing
import (
"fmt"
"github.com/emicklei/go-restful"
"io/ioutil"
"log"
"net/http"
)
var JaegerQueryUrl = "http://jaeger-query.istio-system.svc:16686/jaeger"
func GetServiceTracing(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
service := request.PathParameter("service")
serviceName := fmt.Sprintf("%s.%s", service, namespace)
url := fmt.Sprintf("%s/api/traces?%s&service=%s", JaegerQueryUrl, request.Request.URL.RawQuery, serviceName)
resp, err := http.Get(url)
if err != nil {
log.Printf("query jaeger faile with err %v", err)
response.WriteError(http.StatusInternalServerError, err)
return
}
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
log.Printf("read response error : %v", err)
response.WriteError(http.StatusInternalServerError, err)
return
}
// need to set header for proper response
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(body)
if err != nil {
log.Printf("write response failed %v", err)
}
}

View File

@@ -38,7 +38,7 @@ const (
ok = "OK"
)
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha3"}
func AddToContainer(c *restful.Container, informerFactory informers.InformerFactory) error {

View File

@@ -1 +1,110 @@
package v1alpha2
import (
"fmt"
"github.com/emicklei/go-restful"
"github.com/kiali/kiali/handlers"
"io/ioutil"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
"net/http"
)
var JaegerQueryUrl = "http://jaeger-query.istio-system.svc:16686/jaeger"
// Get app metrics
func getAppMetrics(request *restful.Request, response *restful.Response) {
handlers.AppMetrics(request, response)
}
// Get workload metrics
func getWorkloadMetrics(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
workload := request.PathParameter("workload")
if len(namespace) > 0 && len(workload) > 0 {
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s&workload=%s", request.Request.URL.RawQuery, namespace, workload)
}
handlers.WorkloadMetrics(request, response)
}
// Get service metrics
func getServiceMetrics(request *restful.Request, response *restful.Response) {
handlers.ServiceMetrics(request, response)
}
// Get namespace metrics
func getNamespaceMetrics(request *restful.Request, response *restful.Response) {
handlers.NamespaceMetrics(request, response)
}
// Get service graph for namespace
func getNamespaceGraph(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
if len(namespace) > 0 {
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s", request.Request.URL.RawQuery, namespace)
}
handlers.GetNamespaceGraph(request, response)
}
// Get service graph for namespaces
func getNamespacesGraph(request *restful.Request, response *restful.Response) {
handlers.GraphNamespaces(request, response)
}
// Get namespace health
func getNamespaceHealth(request *restful.Request, response *restful.Response) {
handlers.NamespaceHealth(request, response)
}
// Get workload health
func getWorkloadHealth(request *restful.Request, response *restful.Response) {
handlers.WorkloadHealth(request, response)
}
// Get app health
func getAppHealth(request *restful.Request, response *restful.Response) {
handlers.AppHealth(request, response)
}
// Get service health
func getServiceHealth(request *restful.Request, response *restful.Response) {
handlers.ServiceHealth(request, response)
}
func getServiceTracing(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
service := request.PathParameter("service")
serviceName := fmt.Sprintf("%s.%s", service, namespace)
url := fmt.Sprintf("%s/api/traces?%s&service=%s", JaegerQueryUrl, request.Request.URL.RawQuery, serviceName)
resp, err := http.Get(url)
if err != nil {
klog.Errorf("query jaeger faile with err %v", err)
api.HandleInternalError(response, err)
return
}
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
klog.Errorf("read response error : %v", err)
api.HandleInternalError(response, err)
return
}
// need to set header for proper response
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(body)
if err != nil {
klog.Errorf("write response failed %v", err)
}
}

View File

@@ -5,8 +5,6 @@ import (
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/apiserver/servicemesh/metrics"
"kubesphere.io/kubesphere/pkg/apiserver/servicemesh/tracing"
"net/http"
)
@@ -14,12 +12,7 @@ const GroupName = "servicemesh.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
func AddToContainer(c *restful.Container) error {
tags := []string{"ServiceMesh"}
@@ -28,7 +21,7 @@ func addWebService(c *restful.Container) error {
// Get service metrics
// GET /namespaces/{namespace}/services/{service}/metrics
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/metrics").
To(metrics.GetServiceMetrics).
To(getServiceMetrics).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get service metrics from a specific namespace").
Param(webservice.PathParameter("namespace", "name of the namespace")).
@@ -49,7 +42,7 @@ func addWebService(c *restful.Container) error {
// Get app metrics
// Get /namespaces/{namespace}/apps/{app}/metrics
webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/metrics").
To(metrics.GetAppMetrics).
To(getAppMetrics).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get app metrics from a specific namespace").
Param(webservice.PathParameter("namespace", "name of the namespace")).
@@ -71,7 +64,7 @@ func addWebService(c *restful.Container) error {
// Get workload metrics
// Get /namespaces/{namespace}/workloads/{workload}/metrics
webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/metrics").
To(metrics.GetWorkloadMetrics).
To(getWorkloadMetrics).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get workload metrics from a specific namespace").
Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)).
@@ -94,7 +87,7 @@ func addWebService(c *restful.Container) error {
// Get namespace metrics
// Get /namespaces/{namespace}/metrics
webservice.Route(webservice.GET("/namespaces/{namespace}/metrics").
To(metrics.GetNamespaceMetrics).
To(getNamespaceMetrics).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get metrics from a specific namespace").
Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)).
@@ -115,7 +108,7 @@ func addWebService(c *restful.Container) error {
// Get namespace graph
// Get /namespaces/{namespace}/graph
webservice.Route(webservice.GET("/namespaces/{namespace}/graph").
To(metrics.GetNamespaceGraph).
To(getNamespaceGraph).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get service graph for a specific namespace").
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
@@ -132,7 +125,7 @@ func addWebService(c *restful.Container) error {
// Get namespaces graph, for multiple namespaces
// Get /namespaces/graph
webservice.Route(webservice.GET("/namespaces/graph").
To(metrics.GetNamespacesGraph).
To(getNamespacesGraph).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get graph from all namespaces").
Param(webservice.QueryParameter("duration", "duration of the query period, in seconds").DefaultValue("10m")).
@@ -147,7 +140,7 @@ func addWebService(c *restful.Container) error {
// Get namespace health
webservice.Route(webservice.GET("/namespaces/{namespace}/health").
To(metrics.GetNamespaceHealth).
To(getNamespaceHealth).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get app/service/workload health of a namespace").
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
@@ -161,7 +154,7 @@ func addWebService(c *restful.Container) error {
// Get workloads health
webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/health").
To(metrics.GetWorkloadHealth).
To(getWorkloadHealth).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get workload health").
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
@@ -173,7 +166,7 @@ func addWebService(c *restful.Container) error {
// Get app health
webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/health").
To(metrics.GetAppHealth).
To(getAppHealth).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get app health").
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
@@ -185,7 +178,7 @@ func addWebService(c *restful.Container) error {
// Get service health
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/health").
To(metrics.GetServiceHealth).
To(getServiceHealth).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Get service health").
Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)).
@@ -197,7 +190,7 @@ func addWebService(c *restful.Container) error {
// Get service tracing
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/traces").
To(tracing.GetServiceTracing).
To(getServiceTracing).
Doc("Get tracing of a service, should have servicemesh enabled first").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "namespace of service").Required(true)).

View File

@@ -53,12 +53,16 @@ type ResourceGetter struct {
}
func (r ResourceGetter) Add(resource string, getter v1alpha2.Interface) {
if r.resourcesGetters == nil {
r.resourcesGetters = make(map[string]v1alpha2.Interface)
}
r.resourcesGetters[resource] = getter
}
func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter {
resourceGetters := make(map[string]v1alpha2.Interface)
//resourceGetters[v1alpha2.Deployments] = deployments
resourceGetters[v1alpha2.ConfigMaps] = configmap.NewConfigmapSearcher(factory.KubernetesSharedInformerFactory())
resourceGetters[v1alpha2.CronJobs] = cronjob.NewCronJobSearcher(factory.KubernetesSharedInformerFactory())
resourceGetters[v1alpha2.DaemonSets] = daemonset.NewDaemonSetSearcher(factory.KubernetesSharedInformerFactory())
@@ -135,7 +139,7 @@ func (r *ResourceGetter) ListResources(namespace, resource string, conditions *p
limit = len(result) - offset
}
result = result[offset : offset+limit]
items = result[offset : offset+limit]
return &models.PageableResponse{TotalCount: len(result), Items: items}, nil
}

View File

@@ -66,7 +66,7 @@ func NewRouterOperator(client kubernetes.Interface, informers informers.SharedIn
routerTemplates := make(map[string]runtime.Object, 2)
if err != nil {
klog.Fatalf("error happened during loading external yamls, %v", err)
klog.Errorf("error happened during loading external yamls, %v", err)
}
for _, f := range yamls {

View File

@@ -1,61 +0,0 @@
/*
Copyright 2019 The KubeSphere 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 filter
import (
"k8s.io/klog"
"net"
"strings"
"time"
"github.com/emicklei/go-restful"
)
func Logging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
start := time.Now()
chain.ProcessFilter(req, resp)
klog.V(4).Infof("%s - \"%s %s %s\" %d %d %dms",
getRequestIP(req),
req.Request.Method,
req.Request.RequestURI,
req.Request.Proto,
resp.StatusCode(),
resp.ContentLength(),
time.Since(start)/time.Millisecond,
)
}
func getRequestIP(req *restful.Request) string {
address := strings.Trim(req.Request.Header.Get("X-Real-Ip"), " ")
if address != "" {
return address
}
address = strings.Trim(req.Request.Header.Get("X-Forwarded-For"), " ")
if address != "" {
return address
}
address, _, err := net.SplitHostPort(req.Request.RemoteAddr)
if err != nil {
return req.Request.RemoteAddr
}
return address
}

View File

@@ -1,24 +0,0 @@
package server
import (
"bytes"
"fmt"
"k8s.io/klog"
"net/http"
"runtime"
)
func LogStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i += 1 {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
}
klog.Error(buffer.String())
httpWriter.WriteHeader(http.StatusInternalServerError)
httpWriter.Write([]byte("recover from panic situation"))
}