Description
Bug Report
What did you do?
I created a Dependent Resource class that extends KubernetesCRUDDependentResource<Namespace, MyCustomResource>
. The goal of this class was to be able to create a Namespace dynamically upon the provisioning of a MyCustomResource.
What did you see instead? Under which circumstances?
When I created a MyCustomResource, I got a NullPointerException because the Namespace::getSpec()
method always returns null, since a Kubernetes Namespace
object has no spec
. The stacktrace can be found here:
- io.littlehorse.operator.lhcluster.dependents.TenantNamespaceDR -> java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "spec" is null
at io.javaoperatorsdk.operator.ReconcilerUtils.getSpecClass(ReconcilerUtils.java:178)
at io.javaoperatorsdk.operator.ReconcilerUtils.setSpec(ReconcilerUtils.java:109)
at io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericResourceUpdatePreProcessor$3.updateClonedActual(GenericResourceUpdatePreProcessor.java:40)
at io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericResourceUpdatePreProcessor.replaceSpecOnActual(GenericResourceUpdatePreProcessor.java:48)
at io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource.update(KubernetesDependentResource.java:138)
at io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource.update(CRUDKubernetesDependentResource.java:16)
at io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource.handleUpdate(AbstractDependentResource.java:144)
at io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource.handleUpdate(KubernetesDependentResource.java:125)
at io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource.handleUpdate(KubernetesDependentResource.java:32)
at io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource.reconcile(AbstractDependentResource.java:72)
at io.javaoperatorsdk.operator.processing.dependent.SingleDependentResourceReconciler.reconcile(SingleDependentResourceReconciler.java:19)
at io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource.reconcile(AbstractDependentResource.java:52)
at io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileExecutor$NodeReconcileExecutor.doRun(WorkflowReconcileExecutor.java:124)
at io.javaoperatorsdk.operator.processing.dependent.workflow.NodeExecutor.run(NodeExecutor.java:22)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
at io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowResult.throwAggregateExceptionIfErrorsPresent(WorkflowResult.java:40)
at io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult.throwAggregateExceptionIfErrorsPresent(WorkflowReconcileResult.java:9)
at io.javaoperatorsdk.operator.processing.dependent.workflow.DefaultWorkflow.reconcile(DefaultWorkflow.java:92)
at io.javaoperatorsdk.operator.processing.Controller$1.execute(Controller.java:133)
at io.javaoperatorsdk.operator.processing.Controller$1.execute(Controller.java:96)
at io.javaoperatorsdk.operator.api.monitoring.Metrics.timeControllerExecution(Metrics.java:209)
at io.javaoperatorsdk.operator.processing.Controller.reconcile(Controller.java:95)
at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.reconcileExecution(ReconciliationDispatcher.java:139)
at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleReconcile(ReconciliationDispatcher.java:119)
at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleDispatch(ReconciliationDispatcher.java:89)
at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleExecution(ReconciliationDispatcher.java:62)
at io.javaoperatorsdk.operator.processing.event.EventProcessor$ReconcilerExecutor.run(EventProcessor.java:414)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
Environment
Running the Java Operator SDK 4.3.0
. I was running the Operator locally on my own terminal, pointed at a KIND cluster. Java version 17.
Possible Solution
Often, people do not use custom resources to create a Namespace (and some would consider it to be bad practice). Therefore, you could just say "too bad, you're out of luck, Colt!" and leave it as-is.
Alternatively, we could guard for Namespace objects (which don't have a Spec) in the implementations of the Updater
interfaces.