Skip to content

ReconcilerUtils.setSpec() cannot find the spec if it is a Custom Resource #1589

Closed
@fsjovatsen

Description

@fsjovatsen

Bug Report

What did you do?

When updating an instance of a Custom Resource we get an exception in the ReconcilerUtils.setSpec(HasMetadata resource, Object spec):

io.javaoperatorsdk.operator.AggregatedOperatorException: Exception during workflow.
	at io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult.createFinalException(WorkflowReconcileResult.java:54) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult.throwAggregateExceptionIfErrorsPresent(WorkflowReconcileResult.java:48) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow.reconcile(Workflow.java:56) ~[operator-framework-core-3.2.0.jar:na]
	at no.fintlabs.FlaisReconiler.reconcile(FlaisReconiler.java:50) ~[flais-operator-starter-1.0.0-rc-4.jar:na]
	at no.fintlabs.FlaisReconiler.reconcile(FlaisReconiler.java:20) ~[flais-operator-starter-1.0.0-rc-4.jar:na]
	at io.javaoperatorsdk.operator.processing.Controller$1.execute(Controller.java:130) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.Controller$1.execute(Controller.java:88) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.api.monitoring.Metrics.timeControllerExecution(Metrics.java:197) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.Controller.reconcile(Controller.java:87) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.reconcileExecution(ReconciliationDispatcher.java:130) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleReconcile(ReconciliationDispatcher.java:110) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleDispatch(ReconciliationDispatcher.java:81) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleExecution(ReconciliationDispatcher.java:54) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.event.EventProcessor$ReconcilerExecutor.run(EventProcessor.java:395) ~[operator-framework-core-3.2.0.jar:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.lang.IllegalStateException: No spec found on resource
	at io.javaoperatorsdk.operator.ReconcilerUtils.setSpec(ReconcilerUtils.java:112) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericResourceUpdatePreProcessor$3.updateClonedActual(GenericResourceUpdatePreProcessor.java:40) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericResourceUpdatePreProcessor.replaceSpecOnActual(GenericResourceUpdatePreProcessor.java:48) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource.update(KubernetesDependentResource.java:132) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource.update(CRUDKubernetesDependentResource.java:17) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource.handleUpdate(AbstractDependentResource.java:111) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource.handleUpdate(KubernetesDependentResource.java:119) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource.handleUpdate(KubernetesDependentResource.java:32) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.AbstractDependentResource.reconcile(AbstractDependentResource.java:50) ~[operator-framework-core-3.2.0.jar:na]
	at io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileExecutor$NodeReconcileExecutor.run(WorkflowReconcileExecutor.java:166) ~[operator-framework-core-3.2.0.jar:na]
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:na]
	... 3 common frames omitted
Caused by: java.lang.NoSuchMethodException: no.fintlabs.onepassword.OnePasswordCrd.setSpec(no.fintlabs.onepassword.OnePasswordSpec)
	at java.base/java.lang.Class.getMethod(Class.java:2227) ~[na:na]
	at io.javaoperatorsdk.operator.ReconcilerUtils.setSpec(ReconcilerUtils.java:109) ~[operator-framework-core-3.2.0.jar:na]
	... 15 common frames omitted

This is only happening when we update, not on create.

This is the desired method from the dependent resource class. This is how I create the CRD instance.

    @Override
    protected OnePasswordCrd desired(FlaisApplicationCrd primary, Context<FlaisApplicationCrd> context) {

        OnePasswordCrd onePasswordCrd = new OnePasswordCrd();
        onePasswordCrd.getMetadata().setLabels(LabelFactory.updateRecommendedLabels(primary));
        onePasswordCrd.getMetadata().setName(primary.getMetadata().getName());
        onePasswordCrd.getMetadata().setNamespace(primary.getMetadata().getNamespace());
        onePasswordCrd.getSpec().setItemPath(primary.getSpec().getOnePassword().getItemPath());

        return onePasswordCrd;
    }

This is the CRD class.

@Group("onepassword.com")
@Version("v1")
@Kind("OnePasswordItem")
public class OnePasswordCrd extends CustomResource<OnePasswordSpec, Void> implements Namespaced {
    @Override
    protected OnePasswordSpec initSpec() {
        return new OnePasswordSpec();
    }
}

This is the spec class.

@Data
public class OnePasswordSpec {
    private String itemPath;
}

One can see the source code here

What did you expect to see?

No exception and the resource updated.

What did you see instead? Under which circumstances?

See the description further up.

Environment

Kubernetes cluster type:

Azure Kubernetes Service

io.javaoperatorsdk.operator-framework:3.2.0

openjdk 17.0.2 2022-01-18 LTS

Client Version: v1.24.0
Kustomize Version: v4.5.4
Server Version: v1.22.11

Possible Solution

What happens is that if it is a CustomResource we're not able to get the setSpec() method.
The reason seems to be related to reflection. If I do resource.getClass().getMethod("setSpec", Object.class)
instead of resource.getClass().getMethod("setSpec", spec.getClass()) it seems to work.

It might be a solution to check if the resource extends CustomResource and then do
resource.getClass().getMethod("setSpec", Object.class)

Additional context

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions