Skip to content

Extract controller configuration handling and provide Quarkus extension #238

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

Merged
merged 83 commits into from
Dec 18, 2020

Conversation

metacosm
Copy link
Collaborator

The idea is for Quarkus to be able to replace the default implementation by an implementation computed at build time.

@s-soroosh
Copy link
Contributor

If I understand it correctly the intention is making the Quarkus native builds possible, right?
If so, I think we can support Quarkus without adding anything additional that is going to be exposed to the developer. otherwise would be great if you could elaborate its advantage.

@metacosm
Copy link
Collaborator Author

metacosm commented Nov 26, 2020

If I understand it correctly the intention is making the Quarkus native builds possible, right?

The intent is to be able to replace a lot (all) of what is currently done at runtime (e.g. annotation processing, reflection) by build time processing when running in a Quarkus environment.

If so, I think we can support Quarkus without adding anything additional that is going to be exposed to the developer. otherwise would be great if you could elaborate its advantage.

What do you mean by "exposed to the developer"? As things are, the intention is that things will keep working as they currently are when not using Quarkus but the configuration implementation will be swapped out by Quarkus.

@s-soroosh
Copy link
Contributor

What do you mean by "exposed to the developer"? As things are, the intention is that things will keep working as they currently are when not using Quarkus but the configuration implementation will be swapped out by Quarkus.

The controller interface is having a new method called getConfiguration() as part of the interface.
I as a developer would rather to not expose to the stuff that is supposed to be handled by the framework and I should have nothing to deal with them.

I think we can achieve the same things just by giving a hint to the graalVM to keep the Reflection metadata in the image. By doing so we don't need to put the effort into having 2 different implementations.

@metacosm
Copy link
Collaborator Author

Note that this change is not Quarkus-specific: if we want to be able to externalize the configuration, we will need to provide different implementations of how the configuration is retrieved/computed. This is also helpful for Spring Boot or other frameworks for example, where each could provide their own way of retrieving the configuration.

If adding a getConfiguration method to the ResourceController interface is considered too intrusive, we could consider introducing an SPI that would need to be conformed to by implementation providers.

Another point that I think is worth mentioning: there's a lot more to Quarkus than native compilation. Its JVM mode also provides reduced memory size, faster startup, smaller binary size. I think it's worth making sure that we can support all these aspects and not simply focus on native compilation. In order to be able to support these other benefits, more work is needed, especially when things can be properly computed at build time instead of runtime. Registering things for reflection is quite costly even for native compilation.

@metacosm metacosm force-pushed the quarkus-support branch 2 times, most recently from be7b6d8 to 70f6bf7 Compare November 27, 2020 12:37
@kirek007
Copy link
Contributor

kirek007 commented Nov 27, 2020

I'm all in for replacing whole runtime logic with built time configuration.

@metacosm metacosm changed the title Extract controller configuration handling into an interface Extract controller configuration handling and provide Quarkus extension Dec 1, 2020
@metacosm
Copy link
Collaborator Author

metacosm commented Dec 4, 2020

So things are getting interesting and I'm making some rather significant changes which I would like input on. The main idea behind these changes is to extract the parts that are done to make it easier to work with native compilation and isolate them in such a way that they can be used when needed but don't impact a future Quarkus extension. The parts in question are all the runtime configuration aspects along with the annotation processor and mapper parts as they would be not only be redundant with what Quarkus would be doing but actually be detrimental.

What do you think @adam-sandor @csviri @psycho-ir @kirek007?

@s-soroosh
Copy link
Contributor

So things are getting interesting and I'm making some rather significant changes which I would like input on. The main idea behind these changes is to extract the parts that are done to make it easier to work with native compilation and isolate them in such a way that they can be used when needed but don't impact a future Quarkus extension. The parts in question are all the runtime configuration aspects along with the annotation processor and mapper parts as they would be not only be redundant with what Quarkus would be doing but actually be detrimental.

What do you think @adam-sandor @csviri @psycho-ir @kirek007?

Do you think it makes sense to write a one-pager doc about the change? I'm not sure about others but at least it would be very helpful for me and also can be added to the decision log or part of the SDK doc later on.

@metacosm
Copy link
Collaborator Author

metacosm commented Dec 4, 2020

@psycho-ir the changes are now made in this PR (though I still need to go through the tests to see if everything works as expected). The main idea as previously mentioned: there's a new runtime-configuration module that people are need to depend on to be able to use the current behavior with the annotation processing and the controller to CR class mapping. Basically, that module provides a default configuration service which extracts configuration from the Controller implementations at runtime or using annotation processing at built time.

Both approaches are discouraged when using Quarkus, which is why this PR makes that behavior optional so that other implementations such as one based on Quarkus, could implement a ConfigurationService that resolves the configuration some other way. This PR also adds a layer to be able to use external configuration of Controllers though it doesn't yet work completely as defined in #237.

The new approach is visible in the sample projects where projects now depend on the runtime-configuration module which they use by creating their Operator using the DefaultConfigurationService.instance() method. For Quarkus, the ConfigurationService is automatically injected. That's also the case for Spring Boot, though Spring Boot uses the current default configuration mechanism.

Of note, an api module was extracted from the operator-framework one so that both it and the runtime-configuration modules could depend on it without risking dependency cycles, though in the end, I'm not sure that was needed but I think it does make things a little cleaner.

@metacosm metacosm marked this pull request as ready for review December 4, 2020 23:11
@metacosm
Copy link
Collaborator Author

metacosm commented Dec 4, 2020

Quarkus extension is not finished yet but I'd like to get some feedback on the rest of the changes.

@metacosm metacosm self-assigned this Dec 8, 2020
@metacosm metacosm force-pushed the quarkus-support branch 3 times, most recently from 1267463 to e200c2e Compare December 16, 2020 21:47
@metacosm metacosm marked this pull request as draft December 16, 2020 21:48
@metacosm metacosm marked this pull request as ready for review December 17, 2020 13:12
@metacosm
Copy link
Collaborator Author

Seems like the issue with the tests is the fact that the sample classes are currently in the operator-framework-core module which doesn't have the annotation processor, resulting in the classes not being registered to be later found.

The purpose is to be able to use a different implementation depending on
the execution context. For example, a Quarkus-specific implementation
would process the annotation at build time instead of doing so at
runtime.
@@ -18,6 +18,6 @@ public void returnsLatestOfEventType() {
new EventList(
Arrays.asList(mock(Event.class), new TimerEvent("2", null), event2, mock(Event.class)));

assertThat(eventList.getLatestOfType(TimerEvent.class).get()).isSameInstanceAs(event2);
Assertions.assertThat(eventList.getLatestOfType(TimerEvent.class).get()).isEqualTo(event2);
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO the static import makes it cleaner

AnnotationValue::asBoolean,
() -> true),
valueOrDefault(
controllerAnnotation, "isClusterScoped", AnnotationValue::asBoolean, () -> false),
Copy link
Contributor

Choose a reason for hiding this comment

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

is isClusterScoped used anywhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's used when the controller is registered with the Operator to know under which scope to register (namespace vs. cluster).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Or at least, it should be but, indeed, it doesn't appear to be used at the moment. I'd keep it, though, because that's an option that we'd want to enable for controllers. That said, I'd rather fix this issue (and possibly others) into subsequent PRs than keeping this one open any longer. It's a pain to maintain and a huge Damocles' sword over people's head :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you point me to the code? Couldn't find it in the changeset.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or at least, it should be but, indeed, it doesn't appear to be used at the moment. I'd keep it, though, because that's an option that we'd want to enable for controllers. That said, I'd rather fix this issue (and possibly others) into subsequent PRs than keeping this one open any longer. It's a pain to maintain and a huge Damocles' sword over people's head :)

As an alternative, it can be removed from the code and be addressed as part of the relevant issue.
IMHO it Would be good practice to develop a feature that has definition of done.

this.crClass = crClass;
this.doneableClassName = doneableClassName;
this.watchAllNamespaces = this.namespaces.contains(WATCH_ALL_NAMESPACES_MARKER);
this.retryConfiguration =
Copy link
Contributor

Choose a reason for hiding this comment

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

how can the user configure the retry in quarkus projects?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I need to add the configuration part, indeed. I will create an issue for it.

Operator operator = new Operator(new DefaultKubernetesClient());
operator.registerController(new WebServerController());
Operator operator = new Operator(new DefaultKubernetesClient(),
DefaultConfigurationService.instance());
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not really easy to understand what DefaultConfigurationService is :(.
I think there is a way to make better before the final release.

Copy link
Collaborator Author

@metacosm metacosm Dec 17, 2020

Choose a reason for hiding this comment

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

The problem is to be able to provide the configuration from the annotation processor in the absence of injection. What would you propose to make this simpler/clearer?
The same thing could apply to DefaultKubernetesClient for that matter… it shouldn't be exposed either, IMO.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ConfigurationProvider might be a better name?

Copy link
Contributor

Choose a reason for hiding this comment

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

From the user's point of view, there is only one ConfigurationService in the classpath belonging to the imported dependency, so would be best to hide it completely from the user by default.
I'm fine with the current approach and naming though, can become simpler in next iterations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@psycho-ir the problem is that we cannot put it there by default because then it would be pulled in by the Quarkus implementation as well, which is exactly what I'm trying to avoid 😄

@s-soroosh s-soroosh self-requested a review December 18, 2020 09:31
@metacosm metacosm merged commit 8a58f48 into operator-framework:master Dec 18, 2020
@metacosm metacosm deleted the quarkus-support branch December 18, 2020 10:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants