kubernetes-client: Type inference broken in 6.9.0

Description

(From internal conversation with @iocanel and @metacosm)

PR #5138, in scope of #5135, provided changes to make Resources Editable.

While this improvement allows for one line edit operations similar to what Lombok’s toBuilder does, it has somehow broken type inference in some scenarios.

One such scenario is where you have elements that are parameterized as such: A<R extends HasMetadata, P>. If you have several such objects which all have the same value for P but different values for R and you want to process them as a list.

Before 6.9.0, the list was correctly inferred to be a list containing elements of type A<? extends HasMetadata, P>. With 6.9.0, the list is now inferred to contain elements of type A<? extends Editable<? extends BaseFluent<? extends BaseFluent<?>>>, P>.

With type erasure, this results in hard-to-diagnose errors because they occur outside of the user’s code, similar to:

accept(java.util.List<java.util.Map.Entry<java.lang.String,java.lang.Object>>,java.lang.String,io.fabric8.kubernetes.api.builder.Visitor...) in io.fabric8.kubernetes.api.builder.BaseFluent cannot implement accept(java.util.List<java.util.Map.Entry<java.lang.String,java.lang.Object>>,java.lang.String,io.fabric8.kubernetes.api.builder.Visitor...) in io.fabric8.kubernetes.api.builder.Visitable
[ERROR]   return type capture#1 of ? is not compatible with capture#2 of ?

You can see such an error here: https://github.com/operator-framework/java-operator-sdk/actions/runs/6456675029/job/17526638395#step:5:2605 If you look at the code, you’ll see that none of the classes or method mentioned in the error are used, which makes for a very unpleasant diagnosing experience.

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Comments: 16 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Similar problem is still ongoing in 6.9.3 (Sundrio 0.101.3)

Quarkus build reports:

Error: ]: Build step io.quarkus.kubernetes.deployment.KubernetesProcessor#build threw an exception: java.lang.NoSuchMethodError: 'io.fabric8.kubernetes.api.builder.Fluent io.fabric8.kubernetes.api.model.KubernetesListBuilder.accept(io.fabric8.kubernetes.api.builder.Visitor[])'
	at io.dekorate.ResourceRegistry.lambda$generate$2(ResourceRegistry.java:181)
	at java.base/java.util.HashMap.forEach(HashMap.java:1421)
	at io.dekorate.ResourceRegistry.generate(ResourceRegistry.java:173)
	at io.dekorate.Session.generate(Session.java:293)
	at io.dekorate.Session.close(Session.java:256)
	at io.quarkus.kubernetes.deployment.KubernetesProcessor.lambda$build$2(KubernetesProcessor.java:194)
	at java.base/java.util.Optional.ifPresent(Optional.java:178)
	at io.quarkus.kubernetes.deployment.KubernetesProcessor.build(KubernetesProcessor.java:142)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:849)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:256)
	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
	at java.base/java.lang.Thread.run(Thread.java:840)
	at org.jboss.threads.JBossThread.run(JBossThread.java:501)

	at io.quarkus.test.QuarkusProdModeTest.beforeAll(QuarkusProdModeTest.java:512)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

Triggered at:

https://github.com/dekorateio/dekorate/blob/5a9d246bdd0ecbcbfabbbbc3070f757605366a21/core/src/main/java/io/dekorate/ResourceRegistry.java#L181

        for (Decorator d : sortDecorators(union)) {
          log("Applying decorator '%s'", d.getClass().getName());
          groups.get(group).accept(d);
        }
      }

In both cases, though, this is a wider type than what would be expected. The more specific common type should be KubernetesDependentResource<? extends HasMetadata, Service>, which is what was previously correctly inferred. I should be able to resolve operations / objects that use Service as a parameter in my lambda without first having to cast.

I wasn’t trying to be overly narrow, just using the class where the resolve method is introduced. I understand that it should work without the typing.

I expanded the test to inline definitions of an AltConfigMap and AltDeployment along with dummy builders. Based upon that the next observations are:

  • the compile error goes away if the resources are not Editable
  • the compile error also goes away if BaseFluent is not used for the Builders

Further inlining all of the builder package with the test shows:

  • the compile error still persists, so it’s not related to modules nor classloading

So I think there’s a viable workaround by removing the Editable interface from the resources (but the edit / toBuilder methods can stay) - however there just seems to be something pretty funky about the sundrio typing that is confusing the compiler. I’ll poke around a little more and see if there are any changes that could be made to BaseFluent to resolve this.