From 73aa71d7dfa9cbf6a8e5ef3a7a4b25fe898a8b6c Mon Sep 17 00:00:00 2001 From: Jian Qiu Date: Tue, 25 Oct 2022 17:05:30 +0800 Subject: [PATCH] fix: access backend service by specific region/zone (#15223) Co-authored-by: Qiu Jian --- pkg/apigateway/handler/backendproxy.go | 61 +++++++++++++++++++++++++- pkg/mcclient/session.go | 22 +++++++--- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/pkg/apigateway/handler/backendproxy.go b/pkg/apigateway/handler/backendproxy.go index 1a0a70788a..355ea1db65 100644 --- a/pkg/apigateway/handler/backendproxy.go +++ b/pkg/apigateway/handler/backendproxy.go @@ -16,6 +16,7 @@ package handler import ( "context" + "fmt" "net/http" "net/url" "strings" @@ -39,14 +40,40 @@ func NewBackendServiceProxyHandler(prefix string) *SBackendServiceProxyHandler { } } +func removeLeadingSlash(p string) string { + for len(p) > 0 && p[0] == '/' { + p = p[1:] + } + return p +} + func (h *SBackendServiceProxyHandler) requestManipulator(ctx context.Context, r *http.Request) (*http.Request, error) { // remove leading prefixes /api/s/ path := r.URL.Path[len("/api/s/"):] + path = removeLeadingSlash(path) slashPos := strings.Index(path, "/") if slashPos <= 0 { return r, httperrors.NewBadRequestError("invalid request URL %s", r.URL.Path) } path = path[slashPos:] + if strings.HasPrefix(path, "/r/") { + path = path[len("/r/"):] + path = removeLeadingSlash(path) + slashPos := strings.Index(path, "/") + if slashPos <= 0 { + return r, httperrors.NewBadRequestError("invalid request URL %s", r.URL.Path) + } + path = path[slashPos:] + if strings.HasPrefix(path, "/z/") { + path = path[len("/z/"):] + path = removeLeadingSlash(path) + slashPos := strings.Index(path, "/") + if slashPos <= 0 { + return r, httperrors.NewBadRequestError("invalid request URL %s", r.URL.Path) + } + path = path[slashPos:] + } + } log.Debugf("Path: %s => %s", r.URL.Path, path) r.URL = &url.URL{ Path: path, @@ -67,8 +94,40 @@ func (h *SBackendServiceProxyHandler) fetchReverseEndpoint() *proxy.SEndpointFac if len(serviceName) == 0 { return "", httperrors.NewBadRequestError("no service") } + path := r.URL.Path + serviceSeg := fmt.Sprintf("/api/s/%s/", serviceName) + pos := strings.Index(path, serviceSeg) + if pos < 0 { + return "", httperrors.NewBadRequestError("malformed URL, expect service") + } + path = path[pos+len(serviceSeg):] + path = removeLeadingSlash(path) + region := FetchRegion(r) + zone := "" + if strings.HasPrefix(path, "r/") { + path = path[len("r/"):] + path = removeLeadingSlash(path) + slashPos := strings.Index(path, "/") + if slashPos <= 0 { + return "", httperrors.NewBadRequestError("malformed URL, expect region") + } + region = path[:slashPos] + path = path[slashPos+1:] + if strings.HasPrefix(path, "z/") { + path = path[len("z/"):] + path = removeLeadingSlash(path) + slashPos := strings.Index(path, "/") + if slashPos <= 0 { + return "", httperrors.NewBadRequestError("malformed URL, expect zone") + } + zone = path[:slashPos] + } + } endpointType := "internalURL" - session := auth.GetAdminSession(ctx, FetchRegion(r)) + session := auth.GetAdminSession(ctx, region) + if len(zone) > 0 { + session.SetZone(zone) + } ep, err := session.GetServiceURL(serviceName, endpointType, "") if err != nil { return "", httperrors.NewBadRequestError("invalid service %s: %s", serviceName, err) diff --git a/pkg/mcclient/session.go b/pkg/mcclient/session.go index 4b555186d4..cd32e06e36 100644 --- a/pkg/mcclient/session.go +++ b/pkg/mcclient/session.go @@ -107,6 +107,10 @@ func (this *ClientSession) GetClient() *Client { return this.client } +func (this *ClientSession) SetZone(zone string) { + this.zone = zone +} + func getApiVersionByServiceType(serviceType string) string { switch serviceType { case "compute": @@ -152,14 +156,14 @@ func (this *ClientSession) GetServiceVersionURLs(service, endpointType, apiVersi } service = this.getServiceName(service, apiVersion) if endpointType == api.EndpointInterfaceApigateway { - return this.getApigatewayServiceURLs(service, endpointType) + return this.getApigatewayServiceURLs(service, this.region, this.zone, endpointType) } else { - return this.getServiceVersionURLs(service, endpointType) + return this.getServiceVersionURLs(service, this.region, this.zone, endpointType) } } -func (this *ClientSession) getApigatewayServiceURLs(service, endpointType string) ([]string, error) { - urls, err := this.getServiceVersionURLs(service, "") +func (this *ClientSession) getApigatewayServiceURLs(service, region, zone, endpointType string) ([]string, error) { + urls, err := this.getServiceVersionURLs(service, region, zone, "") if err != nil { return nil, errors.Wrap(err, "getServiceVersionURLs") } @@ -171,6 +175,12 @@ func (this *ClientSession) getApigatewayServiceURLs(service, endpointType string return nil, errors.Wrapf(err, "invalue auth_url %s", prefix) } prefix = httputils.JoinPath(prefix[:lastSlashPos], "api/s", service) + if len(region) > 0 { + prefix = httputils.JoinPath(prefix, "r", region) + if len(zone) > 0 { + prefix = httputils.JoinPath(prefix, "z", zone) + } + } rets := make([]string, len(urls)) for i, url := range urls { if len(url) < 9 { @@ -189,12 +199,12 @@ func (this *ClientSession) getApigatewayServiceURLs(service, endpointType string return rets, nil } -func (this *ClientSession) getServiceVersionURLs(service, endpointType string) ([]string, error) { +func (this *ClientSession) getServiceVersionURLs(service, region, zone, endpointType string) ([]string, error) { catalog := this.GetServiceCatalog() if gotypes.IsNil(catalog) { return []string{this.client.authUrl}, nil } - urls, err := catalog.GetServiceURLs(service, this.region, this.zone, endpointType) + urls, err := catalog.GetServiceURLs(service, region, zone, endpointType) // HACK! in case of fail to get kestone url or schema of keystone changed, always trust authUrl if service == api.SERVICE_TYPE && (err != nil || len(urls) == 0 || (len(this.client.authUrl) != 0 && this.client.authUrl[:5] != urls[0][:5])) { var msg string