Skip to content

Using the CRUDKubernetesDependentResource with a cluster-scoped secondary resource results in KubernetesClientException: namespace cannot be null #1682

Closed
@silenium-dev

Description

@silenium-dev

Bug Report

What did you do?

I've used a namespace with the Dependent Resource Feature, the dependent resource implementation below:

@KubernetesDependent(labelSelector = ProjectReconciler.SELECTOR)
class NamespaceDR : CRUDKubernetesDependentResource<Namespace, Project>(Namespace::class.java) {
    override fun desired(primary: Project, context: Context<Project>): Namespace {
        return NamespaceBuilder()
            .withNewMetadata()
            .withName(primary.targetNamespace)
            .addToLabels(ProjectReconciler.SELECTOR, primary.metadata.name)
            .endMetadata()
            .build()
    }
}

What did you expect to see?

It creates, updates and deletes the namespace according to changes in the primary resource.

What did you see instead? Under which circumstances?

As soon as I've created the primary resource, the operator logs the following exception:

Caused by: io.fabric8.kubernetes.client.KubernetesClientException: namespace cannot be null
	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:140)
	at io.javaoperatorsdk.operator.processing.Controller$1.execute(Controller.java:103)
	at io.javaoperatorsdk.operator.api.monitoring.Metrics.timeControllerExecution(Metrics.java:206)
	at io.javaoperatorsdk.operator.processing.Controller.reconcile(Controller.java:102)
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.reconcileExecution(ReconciliationDispatcher.java:141)
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleReconcile(ReconciliationDispatcher.java:121)
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleDispatch(ReconciliationDispatcher.java:91)
	at io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.handleExecution(ReconciliationDispatcher.java:64)
	at io.javaoperatorsdk.operator.processing.event.EventProcessor$ReconcilerExecutor.run(EventProcessor.java:415)
	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

Kubernetes cluster type:

I'm using a local minikube on libvirt and CRI-O.

$ Mention java-operator-sdk version from pom.xml file

java-operator-sdk version used: 4.2.0

$ java -version

openjdk version "17.0.5" 2022-10-18
OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08, mixed mode, sharing)

$ kubectl version

Client Version: version.Info{Major:"1", Minor:"24", GitVersion:"v1.24.4", GitCommit:"95ee5ab382d64cfe6c28967f36b53970b8374491", GitTreeState:"clean", BuildDate:"2022-08-17T18:54:23Z", GoVersion:"go1.18.5", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v4.5.4
Server Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.3", GitCommit:"434bfd82814af038ad94d62ebe59b133fcb50506", GitTreeState:"clean", BuildDate:"2022-10-12T10:49:09Z", GoVersion:"go1.19.2", Compiler:"gc", Platform:"linux/amd64"}

Possible Solution

A possible fix would be to check in KubernetesDependentResource.prepare() whether the secondary resource implements Namespaced:

@Override
protected Resource<R> prepare(R desired, P primary, String actionName) {
    log.debug("{} target resource with type: {}, with id: {}",
            actionName,
            desired.getClass(),
            ResourceID.fromResource(desired));
    if (addOwnerReference()) {
        desired.addOwnerReference(primary);
    } else if (useDefaultAnnotationsToIdentifyPrimary()) {
        addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary);
    }
    if (desired instanceof Namespaced) {
        return client.resource(desired).inNamespace(desired.getMetadata().getNamespace());
    } else {
        return client.resource(desired);
    }
}

Additional context

I'm using the Quarkiverse Operator SDK extension for Quarkus to integrate the Operator SDK with Quarkus, but this should be causing the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions