-
Notifications
You must be signed in to change notification settings - Fork 89
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
Multiversion CRDs & Conversion Webhooks #321
Conversation
87669c6
to
3902c69
Compare
d1f07bb
to
3999690
Compare
b4cd599
to
108d366
Compare
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 @ulucinar LGTM. I left two comments for documenting. I validated the PR in the provider-aws tests and custom converters effort.
return errors.Wrap(err, "cannot convert the conversion source object into the map[string]any representation") | ||
} | ||
gvk := dst.GetObjectKind().GroupVersionKind() | ||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(srcMap, dst); err != nil { |
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.
nit: Only for documenting, we may consider adding a filtering option for deciding whether to copy all possible fields to the destination. This can also be handled in converters.
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 agree. Let's see the frequency of the need for that functionality and if we frequently need to do this, then we can add a filtering API. Currently, as discussed, we can just let the RoundTrip
copy everything and in the conversion function, we can unset the unwanted fields. This is comparable effort to preparing a filter, i.e., instead of passing a filter to RoundTrip
, unsetting a field in a custom converter. Here, the assumption is we are dealing with a custom converter, which can be strongly typed.
// RoundTrip round-trips from `src` to `dst` via an unstructured map[string]any | ||
// representation of the `src` object and applies the registered webhook | ||
// conversion functions. | ||
func RoundTrip(dst, src resource.Terraformed) error { |
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.
Is this indirection because we don't have a registry object on the provider side? Now, we consume the universal variable instance
in the upjet side, right? Is there any specific reason for this? Is creating and consuming a registry object on the provider side an option?
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 question. We need to invoke the registered conversion functions from the conversion webhooks. So, the conversion webhook needs to know the chain of the conversion functions available for a (spoke or hub) version. Unfortunately, unlike the reconcilers where the reconciliations for a given GVK happen under the context of a reconciler object that we register with the controller-runtime manager, a similar context variable that's directly owned (initialized) by the provider does not exist. Hence, we use a global registry that can convert a given managed resource kind to the conversion functions associated with it. We can initialize this global registry in the provider codebase but I've preferred to do this in upjet so that we will not need to repeat this for different providers.
The conversion.Convertible
implementation that upjet generates currently relies on this global registry that upjet initializes using the conversion function configuration provided by the provider. We could extend upjet provider configuration and let the provider specify the global conversion registry to upjet and then, upjet could use that registry instead of the one that it directly owns (in the generated conversion.Convertible
implementations). For now, I've avoided this indirection by letting upjet own this global registry.
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.
Related to this discussion, we had better get rid of this global registry (whether it's owned by upjet or the provider) if we can find a way to set the context for the conversion webhooks. To the best of my knowledge, it's currently not possible with how the conversion.Convertible
implementations are hooked into the controller runtime's webhook server. Of course, if we just replace the webhook server implementation itself and use a custom machinery instead of the conversion.Convertible
, we can achieve this but I don't believe it would be worth the effort.
Signed-off-by: Alper Rifat Ulucinar <[email protected]>
Signed-off-by: Alper Rifat Ulucinar <[email protected]>
Signed-off-by: Alper Rifat Ulucinar <[email protected]>
Signed-off-by: Alper Rifat Ulucinar <[email protected]>
Signed-off-by: Alper Rifat Ulucinar <[email protected]>
- Do not generate empty conversion hub or spoke files. Signed-off-by: Alper Rifat Ulucinar <[email protected]>
…ller.Options.StartWebhooks Signed-off-by: Alper Rifat Ulucinar <[email protected]>
Signed-off-by: Alper Rifat Ulucinar <[email protected]>
Signed-off-by: Sergen Yalçın <[email protected]>
- Add unit tests for conversion.RoundTrip Signed-off-by: Alper Rifat Ulucinar <[email protected]>
…ngrades Signed-off-by: Alper Rifat Ulucinar <[email protected]>
0256347
to
fa398eb
Compare
Description of your changes
Depends on: #326, #331
This PR adds support for generating CRDs with multiple versions and generating the conversion.Hub and conversion.Convertible implementations for the resource.Terraformed resources.
The generated webhooks will invoke a chain of
Conversion
s while converting from/toHub
versions to/from the spoke versions. There are currently two conversion types,conversion.PavedConversion
andconversion.ManagedConversion
.PavedConversion
is an optimizedConversion
between two fieldpath.Paved objects.PavedConversion
implementations for a specific source and target version pair are chained together and the source and the destination objects are paved once at the beginning of the chainedPavedConversion.ConvertPaved
calls. The targetfieldpath.Paved
object is then converted into the originalresource.Terraformed
object at the end of the chained calls. This prevents the intermediate conversions between thefieldpath.Paved
and theresource.Terraformed
representations of the same object. Thefieldpath.Paved
representation is convenient for writing genericConversion
implementations not bound to a specific type.ManagedConversion
defines aConversion
from a specific source resource.Managed to a target one. GenericConversion
implementations may prefer to implement thePavedConversion
interface. Implementations of theManagedConversion
can do type assertions to the specific source and target types and so, they are expected to be strongly typed.We are also providing some high-level
Conversion
s to implement some common API conversion tasks.conversion.NewFieldRenameConversion
returns a newPavedConversion
that implements a field renaming conversion from a specified source version to a specified target version of an API.conversion.NewCustomConverter
can be used to initialize a newManagedConversion
that invokes a specified conversion function on a source and destinationresource.Managed
pair.The upjet resource configuration framework is extended with a configuration parameter to register a chain of
Conversion
s to be invoked on aHub
object while converting between thisHub
and the spokes. A sample configuration is as follows:Or, you can register a strongly typed conversion using the
conversion.NewCustomConverter
function as follows:This PR also introduces a
Conversion
registry through which one can register theConversion
s to be invoked by the generated conversion webhooks. Because the configuredConversion
s are owned by the config.Provider for the provider, the following example call can be used to register those configuredConversion
s:, where
o.Provider
is the*config.Provider
instance that represents the upjet provider's configuration.I have:
make reviewable
to ensure this PR is ready for review.backport release-x.y
labels to auto-backport this PR if necessary.How has this code been tested
Tested against https://github.com/ulucinar/upjet-provider-template/tree/multiversion-crds and crossplane-contrib/provider-upjet-aws#1075.
TODO: