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

Generate apis types from existed crd definition yaml #3082

Closed
ArenaSu opened this issue Nov 17, 2022 · 14 comments
Closed

Generate apis types from existed crd definition yaml #3082

ArenaSu opened this issue Nov 17, 2022 · 14 comments
Labels
kind/documentation Categorizes issue or PR as related to documentation. kind/support Categorizes issue or PR as a support question.

Comments

@ArenaSu
Copy link

ArenaSu commented Nov 17, 2022

What do you want to happen?

How to generate apis types from existed crd definition yaml ? For example i have a crd definition yaml, and i want to generate golang apis types, is there a way to do it ?

The Scenario is below:
My project (generated by kubebuilder) import openfunction v0.7.0 which import keda(github.com/kedacore/keda/v2) v2.4.0 .Meanwhile my project import another inner project which import keda(github.com/kedacore/keda/v2) v2.8.1. Then it conflicts and error occurred when exec go mod tidy. I want to generate keda v2.4.0 api types from v2.4.0 crds and generate keda v2.8.1 api types from v2.8.1 crds. Then move the api types to different directories and import them inner.

And i know kubernetes-client Java have a action named "CRD Java Model Generate", it can generate apis types from crd definition yaml.

Extra Labels

No response

@ArenaSu ArenaSu added the kind/feature Categorizes issue or PR as related to a new feature. label Nov 17, 2022
@camilamacedo86
Copy link
Member

camilamacedo86 commented Nov 17, 2022

Hi @ArenaSu,

It should be possible. Mainly with the tool, you will need to call create API and generate only the controller. However, we need to do some manual changes, for now, see that:

Therefore, would be great to do (IMHO):

Create a doc in the reference (https://book.kubebuilder.io/reference/reference.html) such as Working with external/third-party apis

Also, help to address the issue #1999 is welcome

Would you like to contribute with?

@camilamacedo86 camilamacedo86 added kind/support Categorizes issue or PR as a support question. kind/documentation Categorizes issue or PR as related to documentation. and removed kind/feature Categorizes issue or PR as related to a new feature. labels Nov 17, 2022
@jmrodri
Copy link
Contributor

jmrodri commented Nov 17, 2022

/label help-wanted

@k8s-ci-robot
Copy link
Contributor

@jmrodri: The label(s) /label help-wanted cannot be applied. These labels are supported: api-review, tide/merge-method-merge, tide/merge-method-rebase, tide/merge-method-squash, team/katacoda, refactor. Is this label configured under labels -> additional_labels or labels -> restricted_labels in plugin.yaml?

In response to this:

/label help-wanted

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.

@jmrodri
Copy link
Contributor

jmrodri commented Nov 17, 2022

/cc @varshaprasad96 @everettraven

@jmrodri
Copy link
Contributor

jmrodri commented Nov 17, 2022

/cc @jmrodri

@varshaprasad96
Copy link
Member

varshaprasad96 commented Nov 18, 2022

Adding few pointers here, in case it helps:
Just to understand right, the scenario looks like this:

  • Module A imports [email protected]
  • Module B imports [email protected]
  • You require both Module A and Module B in your project.
    Doing so, go mod tidy errors, since I assume both are incompatible and breaking. This seems to be a Golang issue.

This dependency cycle in a project is highly not suggested since go cannot differentiate between the two. In order to use 2 versions of the same package in a project, you would have to use 2 different major versions, (ie) you can have something/pkg and something/v2/pkg. More details on the same are also here. The other fragile workaround (I would not suggest this) is to fork both the versions locally, and use replace directives in go.mod.

Meanwhile, coming to the best practices in Kubernetes world - it would be helpful to understand your use case. Having multiple GVKs in a same project can definitely be possible, though only one version is eventually stored in etcd server on cluster. In your case, if v2.8.1 and v2.4.0 are incompatible (I assume), which means both of them belong to different versions of custom resources (say CR/v1 or maybe CR/v2). This indirectly also means that though you may need to generate _types individually for both, they in turn can be translated to have different major versions. Unfortunately, here we have a thin line of conversion between semantic and alphanumeric versioning which needs to be sorted out based on the use case and nature of APIs.

@camilamacedo86
Copy link
Member

Oh thank you @varshaprasad96,
No I got that I misunderstood the question.

@ArenaSu maybe nit trying to create controllers for Kinds that are created by other projects.
actually is looking to import the definitions from another project and then re-created the Kind/Api.

I'd suggest you do not do that. And instead to do the steps described on my previous comment: #3082 (comment)

@ArenaSu
Copy link
Author

ArenaSu commented Nov 21, 2022

Adding few pointers here, in case it helps: Just to understand right, the scenario looks like this:

  • Module A imports [email protected]
  • Module B imports [email protected]
  • You require both Module A and Module B in your project.
    Doing so, go mod tidy errors, since I assume both are incompatible and breaking. This seems to be a Golang issue.

This dependency cycle in a project is highly not suggested since go cannot differentiate between the two. In order to use 2 versions of the same package in a project, you would have to use 2 different major versions, (ie) you can have something/pkg and something/v2/pkg. More details on the same are also here. The other fragile workaround (I would not suggest this) is to fork both the versions locally, and use replace directives in go.mod.

Meanwhile, coming to the best practices in Kubernetes world - it would be helpful to understand your use case. Having multiple GVKs in a same project can definitely be possible, though only one version is eventually stored in etcd server on cluster. In your case, if v2.8.1 and v2.4.0 are incompatible (I assume), which means both of them belong to different versions of custom resources (say CR/v1 or maybe CR/v2). This indirectly also means that though you may need to generate _types individually for both, they in turn can be translated to have different major versions. Unfortunately, here we have a thin line of conversion between semantic and alphanumeric versioning which needs to be sorted out based on the use case and nature of APIs.

The Scenario is below:

  1. k8s cluster A installed [email protected]
  2. k8s cluster B installed [email protected]
  3. two apps (crd controller and apiserver in cluster C only used to manage multi cluster resources) outside of the two clusters need to create and get two version keda crd resources

@camilamacedo86 @varshaprasad96 It's a very common scenario i think. What i want to know is how to generate apis types from crd definition yamls and i think it can solve the problem indeed (as CRD Java Model Generate action do). And i'm sorry that i do not find the method from your comments. Do i miss the point ?

@varshaprasad96
Copy link
Member

@ArenaSu I'm not personally aware of any tool that generates go code from CRDs. The usual option is generally unmarshalling the yaml and generating the struct out of it (https://pkg.go.dev/sigs.k8s.io/[email protected]#Unmarshal) to handle the objects further.

@everettraven
Copy link
Contributor

@ArenaSu since you are using CRDs from a Go package that already exists with type definitions. While you may not be able to import both v2.4.0 and v2.8.1, you can create you own type definitions by copying their implementation and then renaming them as you see fit for use in your project. Personally I think this would be easier than trying to find a tool to do that from a CRD YAML file (which I am not aware of one that exists for Go).

I am still curious if there is a better way to handle a scenario like this rather than having to re-implement the type definitions. I would assume that there aren't incompatible changes between the CRDs since they are both the same major version, but there may be new fields added to them from v2.4.0 --> v2.8.1 (This is also assuming they are versioning their CRDs in this way. You would likely need to verify that there are no incompatible API changes).

If your operator can already tell which cluster requires which version of the CRD to use you could have certain logic to populate the fields that only exist in the corresponding version of the CRD. This way you would only need to import and use the definitions from keda v2.8.1 and use as is for cluster B but when you need to do something on cluster A you only use the fields that exist in a keda v2.4.0 CRD (you may end up having to use an unstructured.Unstructured object to represent creation of a v2.4.0 CR but I'm not entirely sure here).

@ArenaSu
Copy link
Author

ArenaSu commented Nov 22, 2022

@everettraven @camilamacedo86 I get it, thanks a lot. I do use runtime.RawExtension and unstructured.Unstructured to make multi-version crds compatible and handle them for different clusters respectively over not only keda but also many other crds. And i will copy multi-version crd definition code to my project to solve the problem. Um it's a "strenuous" work.

@ArenaSu ArenaSu closed this as completed Nov 22, 2022
@ArenaSu ArenaSu reopened this Dec 16, 2022
@ArenaSu ArenaSu closed this as completed Dec 16, 2022
@ArenaSu
Copy link
Author

ArenaSu commented Dec 30, 2022

@everettraven @camilamacedo86 Hi, i am trying to develop a tool to generate api types code from CRD yaml.But i have no experience before.And i am reading the controller-tools source code to find the way how to create a standard generator. I would be very grateful if you give me some ideas or suggestions to generate code from apiextensions CustomResourceDefinition as you are experts in this area. Maybe some useful libs or the specialized way to do it?

@ArenaSu ArenaSu reopened this Dec 30, 2022
@varshaprasad96
Copy link
Member

varshaprasad96 commented Jan 3, 2023

@ArenaSu I've had the experience of developing custom generators using controller-tool as a library and hence got the urge to answer the question on that :) So adding some inputs here, in case it helps!

Note:
If you're looking to parse yaml and generate an api type, I doubt controller-tools is the right tool. I'm still going to go ahead and explain the utilities of controller-tools in case you are looking to decode the library and would like to use any helpers for your use case.

The major utilities which c-t provides us with is the ability to define markers in the format we want, parse them with any custom logic and scaffold out relevant bits of code (the output maybe a yaml, a go file or anything).

Step 1:
Declaring markers and parsing them:

Step 2:
Parse the marker:
This is done using gengo for which we have wrappers in controller-tools. An example for the same is here: https://pkg.go.dev/sigs.k8s.io/controller-tools/pkg/crd#Generator.Generate. We have several helpers around the parsing logic: https://pkg.go.dev/sigs.k8s.io/controller-tools/pkg/markers#MarkerValues.Get

Step 3:
Generate output:
The output can be of any form. An example of writing yaml is here: https://pkg.go.dev/sigs.k8s.io/controller-tools/pkg/genall#GenerationContext.WriteYAML, and one for writing go files is: https://github.com/kubernetes-sigs/controller-tools/blob/v0.11.1/pkg/deepcopy/gen.go#L290. The GenerationContext wraps around all these functionalities. It just boils down to having bytes of data and utilizing other go tool to write the output in the format we want.

Also not to complicate more, but just suggesting on one of the methods on how to parse CRD/yaml effectively (and dynamically - when we don't know the field names) - is to convert the yaml into AST (https://pkg.go.dev/go/ast), decode the same and create api types. This is more involved process.

@ArenaSu
Copy link
Author

ArenaSu commented Jan 4, 2023

@ArenaSu I've had the experience of developing custom generators using controller-tool as a library and hence got the urge to answer the question on that :) So adding some inputs here, in case it helps!
...

Thank you very much.It do help a lot.I will dig into the method what you list and try to develop the tool.

@ArenaSu ArenaSu closed this as completed Jan 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/documentation Categorizes issue or PR as related to documentation. kind/support Categorizes issue or PR as a support question.
Projects
None yet
Development

No branches or pull requests

6 participants