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

OpenAPI Provider #157

Merged
merged 27 commits into from
May 30, 2024
Merged

Conversation

guicassolato
Copy link
Contributor

@guicassolato guicassolato commented May 4, 2024

What type of PR is this?
/kind feature

What this PR does / why we need it:
Adds new openapi provider.

Which issue(s) this PR fixes:
Closes #147

Does this PR introduce a user-facing change?:

- New OpenAPI Provider

Try this PR out:

make build
./ingress2gateway print --providers openapi3 --input-file=pkg/i2gw/providers/openapi3/fixtures/input/1-petstore3.yaml

@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. kind/feature Categorizes issue or PR as related to a new feature. labels May 4, 2024
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label May 4, 2024
@k8s-ci-robot
Copy link
Contributor

Welcome @guicassolato!

It looks like this is your first PR to kubernetes-sigs/ingress2gateway 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-sigs/ingress2gateway has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label May 4, 2024
@k8s-ci-robot
Copy link
Contributor

Hi @guicassolato. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label May 4, 2024
@guicassolato
Copy link
Contributor Author

guicassolato commented May 4, 2024

@arkodg @youngnick @LiorLieberman et al

Sharing this as a draft for some initial feedback.

Quite a few TODOs ahead still, including:

  • Gateways and parentRefs
  • backendRefs
  • gatewayClass
  • Name objects after the OAS title (?)
  • Input with multiple OAS docs (?)

Apart from more testing of course.

Please check out the test cases already covered under providers/openapi/fixtures.

Copy link
Member

@LiorLieberman LiorLieberman left a comment

Choose a reason for hiding this comment

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

Thanks @guicassolato, that a very useful contribution! left some comments

pkg/i2gw/providers/openapi/README.md Outdated Show resolved Hide resolved
pkg/i2gw/providers/openapi/README.md Outdated Show resolved Hide resolved
## Known limitations

* Only offline translation supported – i.e. `--input_file` required
* All API operation [paths](https://swagger.io/specification/v3/#paths-object) treated as `Exact` type – i.e. no support for [path templating](https://swagger.io/specification/v3/#path-templating), therefore no `PathPrefix`, nor `RegularExpression` path types output
Copy link
Member

Choose a reason for hiding this comment

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

Curious to know why no support for path templating? a limitation you encountered ?

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'm afraid there aren't many good options for that. At least, not that I could think of.

  1. Delete the part in each path from the first variable placeholder, all the way to the end of the path, and use PathPrefix type → some nasty stuff could happen with the ordering of the route rules
  2. Use RegularExpression type → not core; what flavour of regex would we use?

Path templates are not like server variables that in some case give us at least a closed set of possible values (enum.)

pkg/i2gw/providers/openapi/openapi.go Outdated Show resolved Hide resolved
Comment on lines 41 to 49
spec, err := loader.LoadFromFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to load OpenAPI spec: %w", err)
}

if err := spec.Validate(ctx); err != nil {
return nil, fmt.Errorf("invalid OpenAPI 3.x spec: %w", err)
}

Copy link
Member

Choose a reason for hiding this comment

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

@levikobi @mlavacca I think we should think about whether we want to preserve this all supported providers by default functionality.

For example here, if we merge this PR, if someone provides --input-file but doesn't provide provider this will always fail (because a regular yaml/json input file would never be a valid openapi spec.

@guicassolato, regardless to the above, I think running openapi provider should always be mutually exclusive to other providers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What's the use case for converting to all supported providers anyway?

Copy link
Member

Choose a reason for hiding this comment

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

thats how we started, I think we need to change it, but will have a separate issue for that

Comment on lines 36 to 37
s.mu.Lock()
defer s.mu.Unlock()
Copy link
Member

Choose a reason for hiding this comment

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

why do we need a lock? couldn't find a place where you run addResource or getResource concurrently

Copy link
Contributor Author

@guicassolato guicassolato May 4, 2024

Choose a reason for hiding this comment

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

It's a good practice in general. Since the dependency is injected, the converter cannot prevent external usage of the storage.

In fact, I now realise my implementation is poor. A concurrent call to ReadResourcesFromFile would cause the entire storage to be invalidated while completely bypassing the lock, when arguably the right thing to do would be to use the context to infer whether reading a resource should be additive to the storage or clear it first. At the very least, do the latter, but not forcibly recreate the storage. I will fix that.

From the perspective of the openapi package, there is no way to tell what a client will do with those two competing processes.

pkg/i2gw/providers/openapi/utils.go Outdated Show resolved Hide resolved
@mlavacca
Copy link
Member

mlavacca commented May 7, 2024

/cc @mlavacca

@k8s-ci-robot k8s-ci-robot requested a review from mlavacca May 7, 2024 15:50
@guicassolato
Copy link
Contributor Author

I want to mark this as ready for review, but not before coming to a decision about how to proceed for backendRefs and gatewayClassName.

It was suggested to use command-line args for those. I would like to know how people feel about that?

A few questions to drive the conversation next should we say this indeed is the desired approach:

  • Should I just define those as flags of the print command?
  • Since they don't have meaning for other providers converting from Ingress resources, will providers like istio, ingress-nginx, kong, etc ignore the values when supplied?
  • Perhaps call those options --default-backend-name and --default-gateway-controller-name?
  • Or they could be namespaced specifically for this provider – e.g. --openapi3-bakcend-name and --openapi3-gateway-controller-name – tho I can see a possible mess in the future should other providers ask for other similar options. If it is provider-specific, should the options be required when --providers includes openapi3 and forbidden otherwise?
  • Provider-specific options could also be serialised into a single command-line flag (e.g. --provider-options "backend-name=x,controller-name=y")
  • Is it starting to get a little bit too much to pass in the call to ToGatewayAPIResources? Should we change the API to accept a more generic PrintOptions type?

cc @youngnick @arkodg @LiorLieberman

@LiorLieberman
Copy link
Member

/cc @arkodg

@k8s-ci-robot k8s-ci-robot requested a review from arkodg May 8, 2024 09:22
@mlavacca
Copy link
Member

mlavacca commented May 8, 2024

I want to mark this as ready for review, but not before coming to a decision about how to proceed for backendRefs and gatewayClassName.

It was suggested to use command-line args for those. I would like to know how people feel about that?

A few questions to drive the conversation next should we say this indeed is the desired approach:

  • Should I just define those as flags of the print command?
  • Since they don't have meaning for other providers converting from Ingress resources, will providers like istio, ingress-nginx, kong, etc ignore the values when supplied?
  • Perhaps call those options --default-backend-name and --default-gateway-controller-name?
  • Or they could be namespaced specifically for this provider – e.g. --openapi3-bakcend-name and --openapi3-gateway-controller-name – tho I can see a possible mess in the future should other providers ask for other similar options. If it is provider-specific, should the options be required when --providers includes openapi3 and forbidden otherwise?

+1 on this approach. having access to generic --default-backend-name, --default-gateway-controller-name flags might be confusing for users. I'd prefer having openapi3--prefixed command line flags to avoid such a confusion. openapi is a special case, and I do cannot think of many other such special cases in the future

  • Provider-specific options could also be serialised into a single command-line flag (e.g. --provider-options "backend-name=x,controller-name=y")
  • Is it starting to get a little bit too much to pass in the call to ToGatewayAPIResources? Should we change the API to accept a more generic PrintOptions type?

+1 on introducing a PrintOptions struct to pass all the needed parameters.

Copy link
Member

@mlavacca mlavacca left a comment

Choose a reason for hiding this comment

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

Thanks for this PR! I left some initial generic comments in the review

pkg/i2gw/providers/common/utils.go Outdated Show resolved Hide resolved
pkg/i2gw/providers/openapi3/openapi.go Outdated Show resolved Hide resolved
}

type storage struct {
mu sync.RWMutex
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need thread safety here? I haven't seen any concurrency in the code, did I miss something?

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 to answer it here: #157 (comment)

TL;DR - I think not having it is a road to problems in the future.

Not supposed to sound unnecessarily ominous here... I do believe things change, such as – I'm making stuff up here, ok? – start supporting multiple input files (or an input directory equivalently) and, if so, why not calling ReadResourcesFromFile concurrently?

It's a small price to pay IMO, but if more people believe we're better off without the mutex, I can remove it – even if I don't understand why.

Comment on lines 64 to 70
gatewayResources := i2gw.GatewayResources{
Gateways: make(map[types.NamespacedName]gatewayv1.Gateway),
HTTPRoutes: make(map[types.NamespacedName]gatewayv1.HTTPRoute),
TLSRoutes: make(map[types.NamespacedName]gatewayv1alpha2.TLSRoute),
TCPRoutes: make(map[types.NamespacedName]gatewayv1alpha2.TCPRoute),
ReferenceGrants: make(map[types.NamespacedName]gatewayv1beta1.ReferenceGrant),
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need to perform this initialization 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.

For TLSRoutes, TCPRoutes and ReferenceGrants probably not.

Nil maps are safe to read and it kinds of make sense to me that the caller will not try to write back on the GatewayResources struct without checking first. Well, if it does, it's the caller's problem anyway.

Besides, returning nil for TLSRoutes, TCPRoutes and ReferenceGrants seems semantically the right thing to do, I reckon. I'll fix it.

Thanks!

… (mapping, filtering), instead of custom implementation
Copy link
Member

@LiorLieberman LiorLieberman left a comment

Choose a reason for hiding this comment

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

Thanks @guicassolato !
added my comments, mostly nits.

PROVIDER.md Outdated Show resolved Hide resolved
cmd/print.go Outdated Show resolved Hide resolved
cmd/print.go Outdated Show resolved Hide resolved
cmd/print.go Outdated Show resolved Hide resolved
cmd/print.go Outdated Show resolved Hide resolved
pkg/i2gw/providers/openapi3/converter.go Outdated Show resolved Hide resolved
backendRef: toBackendRef(""),
}

if ps := conf.ProviderSpecific[ProviderName]; ps != nil {
Copy link
Member

Choose a reason for hiding this comment

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

what ensures that all those flags are set if ps != nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They don't have to. toNamespacedName and toBackendRef funcs will fallback to the defaults, which are analogous in effect to an empty gatewayClassName.

IOW, we need this nil check not to panic, but we don't need to check each individual flag.

Comment on lines +103 to +105
resourcesNamePrefixes[resourcesNamePrefix]++
if resourcesNamePrefixes[resourcesNamePrefix] > 1 {
resourcesNamePrefix = fmt.Sprintf("%s-%d", resourcesNamePrefix, resourcesNamePrefixes[resourcesNamePrefix]+1)
Copy link
Member

Choose a reason for hiding this comment

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

can we add logs and/or comments here to explain the logic?

It will also be useful for @Devaansh-Kumar project to build notifications system for conversion results.

Copy link
Member

Choose a reason for hiding this comment

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

similar comments for the the helper functions below, adding a line explaining when we you take a not obvious decisions will be very useful

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I can add the comment.

Out of curiosity though, what would a more obvious decision here be in your opinion?

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've added comments. Please let me know if we want logs as well, in which case I'd probably also expect some definition regarding #157 (comment), to avoid multiple formats of log messages.

Copy link
Member

Choose a reason for hiding this comment

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

sorry, I did not meant that your choice was not obvious as "I would choose something else". But when a user is invoking the tool, I am not sure it will be straight forward to them.
@Devaansh-Kumar is working on a notification system, so he might consider this as an INFO notification, and comments in the code will help him.
Hopefully this makes sense.

No need for logs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No problem, @LiorLieberman! You're absolutely right about making things clear. What's obvious to some people may not be to others indeed. I'm glad you brought it up. Thanks!

Comment on lines 106 to 107
log.Printf("%s provider: invalid OpenAPI 3.x spec: %v", ProviderName, err)
return nil, nil
Copy link
Member

Choose a reason for hiding this comment

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

why nil and not error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe I didn't get what you meant at #157 (comment)? Or shall we make readSpecFromFile return an error, then ReadResourcesFromFile catch the error and, depending on the type, raise it or not?

Copy link
Member

Choose a reason for hiding this comment

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

What I meant in #157 (comment) is the latter. And yes, for that comment it was important that ReadResourcesFromFile would not return error, otherwise the tool will fail for all providers.

However, I opened #158 to change the default mode to choose no providers. Will soon get to do this.

So I think for now you should make readSpecFromFile return an error, log this error in ReadResourcesFromFile and return nil. Once I make a PR for #158 I will change that to return the error.

Copy link
Member

Choose a reason for hiding this comment

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

Did you implemented this?

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 guess I missed this one. Doing it now...

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 Author

Choose a reason for hiding this comment

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

Oops. Forgot to fix the tests. Doing it now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now done.

pkg/i2gw/providers/openapi3/storage.go Outdated Show resolved Hide resolved
* provider-specific conf renamed as provided-specific flags
* mutex to read/write provider-specific flag definitions wrapped within a type along with the definitions themselves
* minor string handling enhancements (concatenation, trim prefix)
* additional comments explaining logics and reasoning throughout the code (thread-safety, helper funcs and expressions, etc)
Copy link
Member

@LiorLieberman LiorLieberman left a comment

Choose a reason for hiding this comment

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

I think I answered all the open comments.

This looks good to me, going to approve it and I asked @mlavacca to review and leave final lgtm.

Thanks Gui!
/approve

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 23, 2024
Copy link
Member

@mlavacca mlavacca left a comment

Choose a reason for hiding this comment

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

Looks great to me, thanks @guicassolato!

/ok-to-test
/approve
/lgtm

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels May 29, 2024
@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 29, 2024
@LiorLieberman
Copy link
Member

/hold for implementing the requested changes from last review.

@k8s-ci-robot k8s-ci-robot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. and removed lgtm "Looks good to me", indicates that a PR is ready to be merged. labels May 29, 2024
@guicassolato guicassolato force-pushed the openapi-provider branch 3 times, most recently from 33f49b0 to b6d703e Compare May 29, 2024 10:27
Copy link
Member

@mlavacca mlavacca left a comment

Choose a reason for hiding this comment

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

/lgtm

I'll let @LiorLieberman take a final look and remove the unhold the PR

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

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: guicassolato, LiorLieberman, mlavacca

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:
  • OWNERS [LiorLieberman,mlavacca]

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

@LiorLieberman
Copy link
Member

/unhold

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label May 30, 2024
@LiorLieberman
Copy link
Member

@guicassolato can you change the release notes to be slightly more detailed? It will make my life easier when releasing a new version

@k8s-ci-robot k8s-ci-robot merged commit 21f9c46 into kubernetes-sigs:main May 30, 2024
4 checks passed
@guicassolato
Copy link
Contributor Author

@guicassolato can you change the release notes to be slightly more detailed? It will make my life easier when releasing a new version

It was merged before I could address this, @LiorLieberman 😞

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. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. 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.

OpenAPI to Gateway API support
5 participants