move apigateway into apiserver (#1948)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
29
pkg/apiserver/authentication/token_authenticator.go
Normal file
29
pkg/apiserver/authentication/token_authenticator.go
Normal 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
|
||||
}
|
||||
36
pkg/apiserver/dispatch/dispatch.go
Normal file
36
pkg/apiserver/dispatch/dispatch.go
Normal 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)
|
||||
}
|
||||
33
pkg/apiserver/filters/authentication.go
Normal file
33
pkg/apiserver/filters/authentication.go
Normal 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)
|
||||
})
|
||||
}
|
||||
71
pkg/apiserver/filters/authorization.go
Normal file
71
pkg/apiserver/filters/authorization.go
Normal 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
|
||||
}
|
||||
32
pkg/apiserver/filters/dispatch.go
Normal file
32
pkg/apiserver/filters/dispatch.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
44
pkg/apiserver/filters/kubeapiserver.go
Normal file
44
pkg/apiserver/filters/kubeapiserver.go
Normal 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)
|
||||
})
|
||||
}
|
||||
22
pkg/apiserver/filters/requestinfo.go
Normal file
22
pkg/apiserver/filters/requestinfo.go
Normal 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)
|
||||
})
|
||||
}
|
||||
173
pkg/apiserver/request/requestinfo.go
Normal file
173
pkg/apiserver/request/requestinfo.go
Normal 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, "/")
|
||||
}
|
||||
1
pkg/apiserver/server/handler.go
Normal file
1
pkg/apiserver/server/handler.go
Normal file
@@ -0,0 +1 @@
|
||||
package server
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)).
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
Reference in New Issue
Block a user