-
Notifications
You must be signed in to change notification settings - Fork 369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🐛 Fix KCPRequestInfoResolver for kind: Cluster #2961
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ package requestinfo | |
import ( | ||
"net/http" | ||
"regexp" | ||
"strings" | ||
|
||
"k8s.io/apiserver/pkg/endpoints/request" | ||
) | ||
|
@@ -33,16 +34,28 @@ func NewKCPRequestInfoResolver() *KCPRequestInfoResolver { | |
} | ||
} | ||
|
||
var clustersRE = regexp.MustCompile(`/clusters/[^/]+/(.*)$`) | ||
var clustersRE = regexp.MustCompile(`/clusters/[^/]+(/.*)$`) | ||
|
||
func (k *KCPRequestInfoResolver) NewRequestInfo(req *http.Request) (*request.RequestInfo, error) { | ||
matches := clustersRE.FindStringSubmatch(req.URL.Path) | ||
if len(matches) == 2 { | ||
// matches[0] is the leftmost pattern that matches (which includes /clusters/...) - skip over that | ||
strippedURL := matches[1] | ||
t := req.Clone(req.Context()) | ||
t.URL.Path = strippedURL | ||
return k.delegate.NewRequestInfo(t) | ||
if !containsPrefix([]string{"/apis/", "/api/"}, req.URL.Path) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with my comment above, this line could go and we would be back at the old code, right? |
||
matches := clustersRE.FindStringSubmatch(req.URL.Path) | ||
if len(matches) == 2 { | ||
// matches[0] is the leftmost pattern that matches (which includes /clusters/...) - skip over that | ||
strippedURL := matches[1] | ||
t := req.Clone(req.Context()) | ||
t.URL.Path = strippedURL | ||
return k.delegate.NewRequestInfo(t) | ||
} | ||
} | ||
return k.delegate.NewRequestInfo(req) | ||
} | ||
|
||
// containsPrefix returns true if the specified path has a prefix that contained in given prefix Set. | ||
func containsPrefix(prefixSet []string, path string) bool { | ||
for _, prefix := range prefixSet { | ||
if strings.HasPrefix(path, prefix) { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,114 @@ | ||||||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||||||
Copyright 2023 The KCP 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 requestinfo | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||
"net/url" | ||||||||||||||||||||||||||||||||
"testing" | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
"github.com/stretchr/testify/require" | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
func TestRequestInfoResolver(t *testing.T) { | ||||||||||||||||||||||||||||||||
tests := map[string]struct { | ||||||||||||||||||||||||||||||||
path string | ||||||||||||||||||||||||||||||||
expectedPath string | ||||||||||||||||||||||||||||||||
expectedResource string | ||||||||||||||||||||||||||||||||
expectedSubresource string | ||||||||||||||||||||||||||||||||
}{ | ||||||||||||||||||||||||||||||||
"path with /api prefix": { | ||||||||||||||||||||||||||||||||
path: "/api/v1/namespaces/default/pods", | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are these valid paths? You need to prefix by cluster or service, there's no default logical cluster to access without specifying. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. at some point we redirected to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw, these tests here might (or not, to be verifiy) actually be correct. In There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also strip the kcp/pkg/cache/server/config.go Lines 172 to 186 in 88d6071
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we strip the cluster prefix, why do we need this special requestinfo resolver in the first place? |
||||||||||||||||||||||||||||||||
expectedPath: "/api/v1/namespaces/default/pods", | ||||||||||||||||||||||||||||||||
expectedResource: "pods", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /apis prefix": { | ||||||||||||||||||||||||||||||||
path: "/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody", | ||||||||||||||||||||||||||||||||
expectedResource: "cowboys", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /apis prefix -- status subresource": { | ||||||||||||||||||||||||||||||||
path: "/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody/status", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody/status", | ||||||||||||||||||||||||||||||||
expectedResource: "cowboys", | ||||||||||||||||||||||||||||||||
expectedSubresource: "status", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /apis prefix -- clusters resource": { | ||||||||||||||||||||||||||||||||
path: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody", | ||||||||||||||||||||||||||||||||
expectedResource: "clusters", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /apis prefix -- clusters resource, status subresource": { | ||||||||||||||||||||||||||||||||
path: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody/status", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody/status", | ||||||||||||||||||||||||||||||||
expectedResource: "clusters", | ||||||||||||||||||||||||||||||||
expectedSubresource: "status", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /clusters prefix": { | ||||||||||||||||||||||||||||||||
path: "/clusters/root/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody", | ||||||||||||||||||||||||||||||||
expectedResource: "cowboys", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /clusters prefix -- status subresource": { | ||||||||||||||||||||||||||||||||
path: "/clusters/root/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody/status", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/cowboys/woody/status", | ||||||||||||||||||||||||||||||||
expectedResource: "cowboys", | ||||||||||||||||||||||||||||||||
expectedSubresource: "status", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /clusters prefix -- clusters resource": { | ||||||||||||||||||||||||||||||||
path: "/clusters/root/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody", | ||||||||||||||||||||||||||||||||
expectedResource: "clusters", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /clusters prefix -- clusters resource, status subresource": { | ||||||||||||||||||||||||||||||||
path: "/clusters/root:my-ws/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody/status", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody/status", | ||||||||||||||||||||||||||||||||
expectedResource: "clusters", | ||||||||||||||||||||||||||||||||
expectedSubresource: "status", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /services prefix": { | ||||||||||||||||||||||||||||||||
path: "/services/apiexport/root:my-ws/my-service/clusters/*/api/v1/configmaps", | ||||||||||||||||||||||||||||||||
expectedPath: "/api/v1/configmaps", | ||||||||||||||||||||||||||||||||
expectedResource: "configmaps", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
"path with /services prefix -- clusters resource, status subresource": { | ||||||||||||||||||||||||||||||||
path: "/services/apiexport/root:my-ws/my-service/clusters/*/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody/status", | ||||||||||||||||||||||||||||||||
expectedPath: "/apis/wildwest.dev/v1alpha1/namespaces/default/clusters/woody/status", | ||||||||||||||||||||||||||||||||
expectedResource: "clusters", | ||||||||||||||||||||||||||||||||
expectedSubresource: "status", | ||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
for name, test := range tests { | ||||||||||||||||||||||||||||||||
t.Run(name, func(t *testing.T) { | ||||||||||||||||||||||||||||||||
url, err := url.Parse(test.path) | ||||||||||||||||||||||||||||||||
require.NoError(t, err, "error parsing path %q to URL", test.path) | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
req := &http.Request{ | ||||||||||||||||||||||||||||||||
URL: url, | ||||||||||||||||||||||||||||||||
Method: http.MethodGet, | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
requestInfo, err := NewKCPRequestInfoResolver().NewRequestInfo(req) | ||||||||||||||||||||||||||||||||
require.NoError(t, err, "unexpected error") | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
require.Equal(t, test.expectedPath, requestInfo.Path, "unexpected requestInfo.Path") | ||||||||||||||||||||||||||||||||
require.Equal(t, test.expectedResource, requestInfo.Resource, "unexpected requestInfo.Resource") | ||||||||||||||||||||||||||||||||
require.Equal(t, test.expectedSubresource, requestInfo.Subresource, "unexpected requestInfo.Subresource") | ||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
am curious: why don't we start with
^
aka match the beginning of the string? Now we would also match/foo/cluster/...
. Don't think that's intentional.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to handle all
/services/...
URLs. The request resolver was actually added to handle the/services/...
URLs - 963ffdeSome more additional context is in #2479 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the service URLs rechnically have nothing to do with the main kcp server. Would rather expect them to strip the prefix first before apply this resolver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im bit lost. Will this solve :ncdc: comment about 60s timeouts? As if I want to build service with streaming to VW backed services, this might be an issue.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So expectations is that WE ALWAYS trim first prefix for
/services
, and first/clusters
?