Skip to content
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 protobuf binary version of OpenAPI spec #45836

Merged
merged 5 commits into from
May 20, 2017

Conversation

mbohlool
Copy link
Contributor

@mbohlool mbohlool commented May 15, 2017

Fixes #45833
Partially fixes #42841

OpenAPI spec is now available in protobuf binary and gzip format (with ETag support)

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label May 15, 2017
@k8s-github-robot k8s-github-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. release-note Denotes a PR that will be considered when it comes time to generate release notes. labels May 15, 2017
@mbohlool mbohlool requested a review from pwittrock May 15, 2017 20:06
@pwittrock
Copy link
Member

@mehdy looks like some bazel issue

@pwittrock
Copy link
Member

@apelisse @mengqiy

files := []fileInfo{
{".json", o.swaggerBytes, MIME_JSON},
{".pb", o.swaggerPb, MIME_PB},
{".pb.gz", o.swaggerPbGz, MIME_PB_GZ},
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can remove this handler and use https://github.com/NYTimes/gziphandler?

Copy link
Member

Choose a reason for hiding this comment

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

We would get the gzipped json for free too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

While I love reusing code, adding dependencies to gogep is painful and this is only 3-4 line more code here. I would like to keep it if it is not that bad.

Copy link
Member

Choose a reason for hiding this comment

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

Sure!

{".json", o.swaggerBytes, MIME_JSON},
{".pb", o.swaggerPb, MIME_PB},
{".pb.gz", o.swaggerPbGz, MIME_PB_GZ},
{".json.sha512", o.swaggerHash[:], MIME_BINARY},
Copy link
Member

Choose a reason for hiding this comment

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

I assume we expose the sha for caching. Could we use a http ETag instead? As this would make the implementation more transparent on both side? I can help with that if needed.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Etag look like a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ETag implemented. PTAL.

return fmt.Errorf("Serving path must ends with \".json\".")
}

servePathBase := servePath[:len(servePath)-5]
Copy link
Member

Choose a reason for hiding this comment

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

What is 5? Is it len(".json")?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I've added a constant for ".json" and change this to len(...)

@pwittrock
Copy link
Member

FYI:

hack/make-rules/../../hack/verify-federation-openapi-spec.sh

}

// RegisterOpenAPIService registers a handler to provides standard OpenAPI specification.
// Note: servePath is the path to the filename with no extension. Supported extensions
Copy link
Member

Choose a reason for hiding this comment

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

Am I mis-reading or is that the opposite of what it actually is. servePath is the path to the filename WITH the .json extension.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, I forgot to remove this comment after some iteration to the code. Done.

func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *openapi.Config, mux *genericmux.PathRecorderMux) (err error) {

if !strings.HasSuffix(servePath, ".json") {
return fmt.Errorf("Serving path must ends with \".json\".")
Copy link
Member

Choose a reason for hiding this comment

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

Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because OpenAPI spec is in json format. We can relax this limitation if we had a reason for it but right now it helps me remove the .json and add extensions for other formats.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think go vet will ask for this to become errors.New instead of fmt.Errorf.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried go vet it didn't complain about fmt.Errorf and I guess we are doing the same in other places, I don't have strong opinion on either.

@pwittrock
Copy link
Member

@alexandercampbell

func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *openapi.Config, mux *genericmux.PathRecorderMux) (err error) {

if !strings.HasSuffix(servePath, ".json") {
return fmt.Errorf("Serving path must ends with \".json\".")
Copy link
Contributor

Choose a reason for hiding this comment

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

I think go vet will ask for this to become errors.New instead of fmt.Errorf.

func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *openapi.Config, mux *genericmux.PathRecorderMux) (err error) {

if !strings.HasSuffix(servePath, ".json") {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like this because it has the potential to break piping. What if my JSON information is fed in from /dev/stdin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure if I understand this. The caller should specify an http serving path for the file and I am enforcing the path ends with json. It cannot be any pipe like stdin.

MIME_JSON = "application/json"
MIME_PB = "application/com.github.googleapis.gnostic.OpenAPIv2+protobuf"
MIME_PB_GZ = "application/x-gzip"
MIME_BINARY = "application/octet-stream"
Copy link
Contributor

Choose a reason for hiding this comment

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

Good constants to have.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately (or fortunately) we don't need this anymore after etag implementation.

mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
resp := restful.NewResponse(w)
if r.URL.Path != path {
resp.WriteErrorString(http.StatusNotFound, "Path not found!")
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't this need to return early?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it will and I don't think this check serve anything here, just looked at other HandleFuncs in our code and they all checked this, I will look into this more and remove this if statement if it doesn't help in any way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I didn't understand your comment earlier, yes, this needs an early return for sure. fixed.

return nil
}

func (o *openAPI) makeAdditionalFormats() error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Docstring on this would be nice. I can't figure out the purpose of this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I like methods to be self explanatory so instead of adding comments to this, I restructure it to two toProtoBinary and toGzip method that is clear what they do.

@@ -187,6 +187,7 @@ func getConfig(fullMethods bool) (*openapi.Config, *restful.Container) {
InfoProps: spec.InfoProps{
Title: "TestAPI",
Description: "Test API",
Version: "unversioned",
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't look gofmt'd. (maybe a Github rendering issue?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@mbohlool
Copy link
Contributor Author

Comments addressed. Thanks to @apelisse @sttts for ETag suggestion. PTAL.

protocolList []string
servePath string
definitions map[string]openapi.OpenAPIDefinition
}

func computeEtag(data []byte) string {
sha := sha512.Sum512(data)
return fmt.Sprintf("\"%s\"",
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a good situation to either use %q or a backtick string literal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. fixed.

Copy link
Member

@apelisse apelisse left a comment

Choose a reason for hiding this comment

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

Thank you @mbohlool ! That's awesome

mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != path {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Path not found!"))
Copy link
Member

Choose a reason for hiding this comment

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

it is indeed missing the early return

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks!

protocolList []string
servePath string
definitions map[string]openapi.OpenAPIDefinition
}

func computeEtag(data []byte) string {
sha := sha512.Sum512(data)
return fmt.Sprintf("\"%s\"",
Copy link
Member

Choose a reason for hiding this comment

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

You could have used fmt.Sprintf("\"%x\"", sha512.Sum512(data)) here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like that. Done.

}

files := []fileInfo{
{".json", o.swaggerBytes, computeEtag(o.swaggerBytes)},
Copy link
Member

Choose a reason for hiding this comment

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

Could you compute etag in the loop? That would remove the extra field in the struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@apelisse
Copy link
Member

apelisse commented May 16, 2017

Client side cache can be implemented using https://github.com/gregjones/httpcache/ and https://github.com/gregjones/httpcache/diskcache

@mbohlool
Copy link
Contributor Author

@apelisse @alexandercampbell Addressed your comments. PTAL.

@alexandercampbell
Copy link
Contributor

I don't see any tests for this code. Are you planning on adding some?

@mbohlool
Copy link
Contributor Author

@alexandercampbell To test this properly, I need some functionality from gnostic (converting proto back to json). It should be there, we can either wait for that and keep this PR open or merge this and when that is available, we can add the test. what do you think?

@alexandercampbell
Copy link
Contributor

@mbohlool any way to get some unit tests on some of the smaller functions in this PR?

@pwittrock
Copy link
Member

If unit testing is the only remaining issue, since @mbohlool is going to be out for a week, and we have 3 weeks of stabilization after feature freeze, how about we merge this and introduce the unit tests next week after he returns?

@alexandercampbell
Copy link
Contributor

Sure. LGTM.

@pwittrock
Copy link
Member

/lgtm

@lavalamp
Copy link
Member

Thanks for the quick changes!

/lgtm
/approve

@k8s-github-robot k8s-github-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. and removed lgtm "Looks good to me", indicates that a PR is ready to be merged. labels May 18, 2017
@lavalamp
Copy link
Member

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 18, 2017
@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: lavalamp, mbohlool, pwittrock

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@k8s-github-robot k8s-github-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 19, 2017
@mbohlool mbohlool added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 19, 2017
@mbohlool
Copy link
Contributor Author

reapplied LGTM. test was failing because an extra file got into one of the commits (a file that I planned for next PR that is adding tests for this). I only removed that file with no other changes.

@k8s-github-robot k8s-github-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 19, 2017
@mbohlool
Copy link
Contributor Author

@k8s-bot verify test this (it is not failing locally for me)

@mbohlool
Copy link
Contributor Author

@k8s-bot pull-kubernetes-federation-e2e-gce test this

@mbohlool
Copy link
Contributor Author

@k8s-bot pull-kubernetes-federation-e2e-gce test this

@mbohlool
Copy link
Contributor Author

@k8s-bot kops aws e2e test this

@mbohlool mbohlool added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 20, 2017
@k8s-ci-robot
Copy link
Contributor

k8s-ci-robot commented May 20, 2017

@mbohlool: The following test(s) failed:

Test name Commit Details Rerun command
pull-kubernetes-federation-e2e-gce 601570a link @k8s-bot pull-kubernetes-federation-e2e-gce test this

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

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. I understand the commands that are listed here.

@k8s-github-robot
Copy link

Automatic merge from submit-queue

@k8s-github-robot k8s-github-robot merged commit a8bff0e into kubernetes:master May 20, 2017
@pwittrock
Copy link
Member

pwittrock commented May 20, 2017 via email

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. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Server OpenAPI spec in protobuf binary format swagger.json is missing from API discovery
9 participants