-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Add webapi v2 endpoints for creating discovery token and enrolling eks with labels #50472
base: master
Are you sure you want to change the base?
Conversation
b51dded
to
0a6aebc
Compare
we do set version with app hash as a etag on the response headers for |
// used for creating tokens used during guided discover flows | ||
h.POST("/webapi/token", h.WithAuth(h.createTokenForDiscoveryHandle)) | ||
// used for creating tokens used during guided discover flows | ||
h.POST("/v2/webapi/token", h.WithAuth(h.createTokenForDiscoveryHandle)) |
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.
Not sure this will align well with our weird v1 auto prefix:
Lines 618 to 622 in c222030
// request is going to the API? | |
if strings.HasPrefix(r.URL.Path, apiPrefix) { | |
http.StripPrefix(apiPrefix, h).ServeHTTP(w, r) | |
return | |
} |
This /v2/ will make /v1/v2/webapi/token
to work as well 😆
We need to fix the weird prefixing issue first
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.
One possible way is to inverse the handler.
Append the /v1/
to every single route h.*("/v1..."
.
if strings.HasPrefix(r.URL.Path,"/webapi") {
addPrefix(apiPrefix, h).ServeHTTP(w, r)
return
}
func addPrefix(prefix string, h Handler) Handler {
if prefix == "" {
return h
}
return http.HandlerFunc(func(w ResponseWriter, r *Request) {
p := path.Join(prefix,r.URL.Path)
rp := path.Join(prefix,r.URL.RawPath)
r2 := new(Request)
*r2 = *r
r2.URL = new(url.URL)
*r2.URL = *r.URL
r2.URL.Path = p
r2.URL.RawPath = rp
h.ServeHTTP(w, r2)
})
}
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.
web/packages/teleport/src/services/webUiVersion/webUiVersion.ts
Outdated
Show resolved
Hide resolved
web/packages/teleport/src/services/webUiVersion/webUiVersion.ts
Outdated
Show resolved
Hide resolved
web/packages/teleport/src/services/webUiVersion/webUiVersion.ts
Outdated
Show resolved
Hide resolved
web/packages/teleport/src/services/webUiVersion/webUiVersion.ts
Outdated
Show resolved
Hide resolved
I'm not sure pulling the version from the HTML document makes sense here. If a user runs into this error, it's because they have a newer web UI that knows how to use the new V2 endpoint with labels, but they ended up hitting an older proxy that doesn't know how to handle the request. If you show them the version from the HTML document, you're going to show them the newer web UI version, which is actually new enough. If we want this error message to make sense, we need to show the version of the proxy that is too old. (This means we probably need the backend to surface this information) |
4db33f1
to
15a7e75
Compare
15a7e75
to
0729ad9
Compare
410b710
to
538b042
Compare
// apiPrefixRegex matches pathnames starting with /v<version num>/<any characters> | ||
var apiPrefixRegex = regexp.MustCompile(`^/v(\d+)/(.+)`) | ||
|
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.
I ended up going with this approach to keep the old behavior the same. I could've done something stricter where regex is like ^/v(\d+)/(webapi|enterprise|scripts|.well-known|workload-identity)
but once you introduce another non-prefix leading path, you have to remember to update this regex.
For cases where route is double prefixed like this /v1/v2/webapi
, I do another regex match on the path after the first prefix to prevent that.
I couldn't see other issues with this approach but let me know.
@@ -62,7 +61,7 @@ func httpTransport(insecure bool, pool *x509.CertPool) *http.Transport { | |||
|
|||
func NewWebClient(url string, opts ...roundtrip.ClientParam) (*WebClient, error) { | |||
opts = append(opts, roundtrip.SanitizerEnabled(true)) | |||
clt, err := roundtrip.NewClient(url, teleport.WebAPIVersion, opts...) | |||
clt, err := roundtrip.NewClient(url, "", opts...) |
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 version parameter refers to this field, and from what i can tell, its only purpose is to just append the version prefix when calling this function (which we use a lot in our tests, and in auth here)
I didn't think it was necessary to define the version, since we are just going to strip it off anyways, and it wouldn't work with v2
endpoints
538b042
to
c6d7694
Compare
c6d7694
to
3ed0168
Compare
3ed0168
to
67c5614
Compare
@@ -29,6 +52,12 @@ export default function parseError(json) { | |||
return msg; | |||
} | |||
|
|||
export function parseProxyVersion(json): ProxyVersion | null { |
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 won't return null
but undefined
.
export function parseProxyVersion(json): ProxyVersion | null { | |
export function parseProxyVersion(json): ProxyVersion | undefined { |
web/packages/build/vite/html.ts
Outdated
@@ -117,6 +117,7 @@ export function htmlPlugin(target: string): Plugin { | |||
|
|||
replaceMetaTag('grv_csrf_token', source, result); | |||
replaceMetaTag('grv_bearer_token', source, result); | |||
replaceMetaTag('teleport_version', source, result); |
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 a leftover, right?
err: unknown | ||
): never { | ||
if (err instanceof ApiError && err.response.status === 404) { | ||
if (err.proxyVersion && err.proxyVersion.string) { |
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.
Having proxyVersion
will be probably useful in the future, but for this particular feature we won't be able to show it. Returning proxy version will land in the same version as the V2 endpoint, so we either won't show the error or show the more generic version from below.
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.
yeah that's the plan, to make it fallback to the generic messaging for the time being
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 we can remove the more specific version as we will never show it.
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.
oh that's true! i'll remove it
); | ||
}); | ||
|
||
test('enrollEksClusters with labbels calls v2', async () => { |
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.
test('enrollEksClusters with labbels calls v2', async () => { | |
test('enrollEksClusters with labels calls v2', async () => { |
* Currently, the proxy version field is only returned | ||
* with api routes "not found" error. | ||
* | ||
* Used to determine out dated proxies. |
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.
* Used to determine out dated proxies. | |
* Used to determine outdated proxies. |
@@ -125,7 +125,7 @@ test('generate command', async () => { | |||
|
|||
// Test create is still called with 404 ping error. | |||
jest.clearAllMocks(); | |||
let error = new ApiError('', { status: 404 } as Response); | |||
let error = new ApiError('', { status: 404 } as Response, null); |
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.
I think the proxyVersion
in ApiError
could be optional, so we wouldn't have to pass null
everywhere.
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.
i made it into an object param
@@ -29,6 +52,12 @@ export default function parseError(json) { | |||
return msg; | |||
} | |||
|
|||
export function parseProxyVersion(json): ProxyVersion | null { | |||
if (json && json.fields && json.fields.proxyVersion) { |
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.
Optional chaining?
web/packages/teleport/src/config.ts
Outdated
getJoinTokenUrlV2() { | ||
return cfg.api.joinTokenPathV2; | ||
}, | ||
|
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 might get a bit unruly now that I think about it. We have the same "path" like /webapi/tokens
but now each of the different http methods might go to some separate version. so this could theoretically have as many different versions as we have http methods.
I don't suggest any change here, but we should think about how to handle this in the future. perhaps we could maybe pass a version number as argument or something. or maybe this isnt a problem at al and im just overthinking it
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.
i did mark old endpoints for deletion eg // TODO(kimlisa): DELETE IN 19.0 - replaced by /v2/webapi/token
, so with clean up it wouldn't be so bad?
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.
oh that reminds me, perhaps i can also mark it deprecated
in case anyone tries to use these endpoints outside of the service/*
directory
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.
but we wont delete the old one in v19. because other services use this path, just not the v2. for example. GET doesnt use v2.
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.
oh i see what you mean now. though with these specific endpoints marked for deletion, there is only one HTTP verb attached to it
but you raise a really good question... how do we want to handle same paths but with differing HTTP verbs and versions?
i think we have to start being specific about the api endpoints, something like this maybe?
// before:
usersPath: '/v1/webapi/users' <--- used with multiple HTTP verbs
// after:
user: {
create: '/v1/webapi/users',
update: '/v1/webapi/users/:username',
delete: '/v1/webapi/users/:username', <-- kept for backward compat
deleteV2: '/v2/webapi/users/:username', <-- new feature
}
we start doing something like it here
would that be a good solution? for example webapi/token
, it'll look like this:
discoveryToken: {
// TODO(kimlisa): DELETE IN 19.0 - replaced by /v2/webapi/token
create: '/v1/webapi/token'
createV2: '/v2/webapi/token'
}
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.
yeah i think an object is best. i suggested that to ryan when he added those paths in and i think it'd be a good solution to versioning in the future as well. at least, i dont have any better suggestions yet.
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.
ok moving forward with this, i also updated RFD #50743 with it
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.
Thanks for the update!
web/packages/teleport/src/services/webUiVersion/webUiVersion.ts
Outdated
Show resolved
Hide resolved
lib/web/apiserver.go
Outdated
if matches := apiPrefixRegex.FindStringSubmatch(r.URL.Path); matches != nil && len(matches) > 1 { | ||
versionNum := matches[1] | ||
if versionNum == "1" { | ||
// Try path matching again with the stripped prefix. |
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.
i can't seem to follow this. wouldn't this catch any version prefixed api path, and if its not v1, then it replies with not found? where does it send the other version number requests to? how does it break out of this
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 this entire block of code is only ran if the first attempt at routing match failed. if the routing match failed, and the path is prefixed with v1
we strip it and serve it again (so essentially each path gets a second chance)
i also added more comments to this block on the newest changes that may explain it more?
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.
that makes sense, thats why its called notFound handler now. ok i like it
…d err Include proxy version with the path not found error
67c5614
to
fb39fee
Compare
part of #46976
suggested_labels
(this PR)extraLabels
(Discover EKS: allow custom labels for Kube Server #49420)Both scenarios if a user tries to create token/resource and provides labels, if requests goes to an older proxy, it'll look like the request succeeded but the labels will not have been set. So this PR defines
v2
endpoints, so that if request goes to an older proxy, a 404 error will return, which we will assume it's because of version mismatch.This PR also returns the version number of proxy when a route wasn't matched.
Rendered example of route not matched 404 error:
if
proxyVersion
is returned:if
proxyVersion
is not returned: