Skip to content

Conversation

@Jefftree
Copy link
Member

@Jefftree Jefftree commented Mar 10, 2022

Support Cache Busting in kube-openapi.

Also fix a bug where lazy marshaling wasn't performed on OpenAPI V3.

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 10, 2022
@k8s-ci-robot k8s-ci-robot requested review from roycaihw and sttts March 10, 2022 19:51
@Jefftree
Copy link
Member Author

/cc @apelisse

@k8s-ci-robot k8s-ci-robot requested a review from apelisse March 10, 2022 20:37
@Jefftree Jefftree changed the title [WIP] Cache busting Cache busting Mar 14, 2022
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 14, 2022
@Jefftree
Copy link
Member Author

/assign @apelisse
/cc @alexzielenski

@k8s-ci-robot
Copy link
Contributor

@Jefftree: GitHub didn't allow me to request PR reviews from the following users: alexzielenski.

Note that only kubernetes members and repo collaborators can review this PR, and authors cannot review their own PRs.

In response to this:

/assign @apelisse
/cc @alexzielenski

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

if err != nil {
return nil, err
}
discovery.Paths[k] = "openapi/v3/" + k + "?hash=" + unquoteETag(etag)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While consuming this API in a test I found it a bit unwieldy. I expected discovery.Paths to be a groupVersion -> ETAG map, rather than a groupVersion -> path map. I ended up using url.Parse to extract the hash query param from the path since the client-go discovery client for OpenAPI V3 asks for the hash by itself.

Also it is worth pointing out that in the future more query params may be added to the OpenAPI endpoints, so it may become inconvenient to deal with these paths with an assumed URL structure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may also in the future end up wanting to extend the discovery map to include additional metadata. Should the type of discovery.Paths[k] be a map[string]interface{} rather than simply a string? So that for example the response might be:

{
    "paths": {
        "batch/v1": {
            "hash": <ETAG>
        }
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't disagree with that, but it should be a URL rather than the hash so it's built automatically (clients shouldn't even have to know what to do with the hash, it should just contain the URL they need to download as a relative link).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While consuming this API in a test I found it a bit unwieldy. I expected discovery.Paths to be a groupVersion -> ETAG map, rather than a groupVersion -> path map. I ended up using url.Parse to extract the hash query param from the path since the client-go discovery client for OpenAPI V3 asks for the hash by itself.

Interesting, why didn't you just want to download the relative path given?

Copy link
Member Author

@Jefftree Jefftree Mar 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may also in the future end up wanting to extend the discovery map to include additional metadata.

Good idea, I'll change the structure.

I ended up using url.Parse to extract the hash query param from the path since the client-go discovery client

Thanks for catching this inconsistency. Question for both of you: Would it make more sense for the client-go interface to directly consume the URL (eg: OpenAPISchema(URL string)) instead of the current OpenAPISchema(gvPath, hash string). I agree we shouldn't assume an URL structure and going the route of parsing the URL may add more complexity in the future

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't disagree with that, but it should be a URL rather than the hash so it's built automatically (clients shouldn't even have to know what to do with the hash, it should just contain the URL they need to download as a relative link).

In that case a URL could be included as one of the keys in the map I proposed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the Discovery schema format to

{
    "paths": {
        "apis/batch/v1": {
            "url":  "/openapi/v3/apis/batch/v1?hash=XXX"
        }
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't disagree that having an object rather than a direct mapping gives us more flexibility in the future, but we shouldn't need the hash per-se.

if err != nil {
return nil, err
}
discovery.Paths[k] = "openapi/v3/" + k + "?hash=" + unquoteETag(etag)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't disagree with that, but it should be a URL rather than the hash so it's built automatically (clients shouldn't even have to know what to do with the hash, it should just contain the URL they need to download as a relative link).

@Jefftree Jefftree force-pushed the cache-busting branch 5 times, most recently from ba6b055 to 768107d Compare March 16, 2022 18:45
Copy link
Member

@alexzielenski alexzielenski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks clean. Very minor nits

Comment on lines 249 to 266
if hash := r.URL.Query().Get("hash"); hash != "" && hash != etag {
u := constructURL(group, etag)
http.Redirect(w, r, u, 301)
return
}
applyCacheBusting(w, r, etag)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Piling up on Alex's comment, a lot of the conditions here are very similar to the conditions in applyCacheBusting. Try to remove the applyCacheBusting method below and move the code here, I think you'll be able to do a few other simplifications.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I don't code enough these days, I'll do it for fun. Not addressing the Vary comment.

Suggested change
if hash := r.URL.Query().Get("hash"); hash != "" && hash != etag {
u := constructURL(group, etag)
http.Redirect(w, r, u, 301)
return
}
applyCacheBusting(w, r, etag)
if hash := r.URL.Query().Get("hash"); hash != "" {
// The Vary header is required because the Accept header can
// change the contents returned. This prevents clients from caching
// protobuf as JSON and vice versa.
// Do you need Vary in the 301?!
w.Header().Set("Vary", "*")
if hash != etag {
u := constructURL(group, etag)
http.Redirect(w, r, u, 301)
return
}
// Only set these headers when a hash is given.
w.Header().Set("Cache-Control", "public, immutable")
// Set the Expires directive to the maximum value of one year from the request,
// effectively indicating that the cache never expires.
w.Header().Set("Expires", time.Now().AddDate(1, 0, 0).Format(time.RFC1123))
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we're not redirecting if no hash is given?

@k8s-ci-robot k8s-ci-robot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Mar 21, 2022
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 22, 2022
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Mar 22, 2022
@apelisse
Copy link
Member

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Mar 22, 2022
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: apelisse, Jefftree

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 22, 2022
@k8s-ci-robot k8s-ci-robot merged commit 89ac9db into kubernetes:master Mar 22, 2022
@Jefftree Jefftree deleted the cache-busting branch March 21, 2023 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants