Skip to content

Commit eeed323

Browse files
authored
fix: NPE when no namespace is provided in kube config (#899)
* fix: fail explicitly if current namespace is requested but not available Fixes #897 * fix: retain annotation for reflective access
1 parent 45b187d commit eeed323

File tree

5 files changed

+34
-30
lines changed

5 files changed

+34
-30
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/KubernetesDependent.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.javaoperatorsdk.operator.api.config;
22

33
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
46
import java.lang.annotation.Target;
57

68
import static io.javaoperatorsdk.operator.api.reconciler.Constants.EMPTY_STRING;
79

10+
@Retention(RetentionPolicy.RUNTIME)
811
@Target({ElementType.TYPE})
912
public @interface KubernetesDependent {
1013

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Set;
55

66
import io.fabric8.kubernetes.api.model.HasMetadata;
7+
import io.javaoperatorsdk.operator.OperatorException;
78
import io.javaoperatorsdk.operator.ReconcilerUtils;
89
import io.javaoperatorsdk.operator.api.reconciler.Constants;
910

@@ -67,7 +68,12 @@ default Set<String> getEffectiveNamespaces() {
6768
throw new IllegalStateException(
6869
"Parent ConfigurationService must be set before calling this method");
6970
}
70-
targetNamespaces = Collections.singleton(parent.getClientConfiguration().getNamespace());
71+
String namespace = parent.getClientConfiguration().getNamespace();
72+
if (namespace == null) {
73+
throw new OperatorException(
74+
"Couldn't retrieve the currently connected namespace. Make sure it's correctly set in your ~/.kube/config file, using, e.g. 'kubectl config set-context <your context> --namespace=<your namespace>'");
75+
}
76+
targetNamespaces = Collections.singleton(namespace);
7177
}
7278
return targetNamespaces;
7379
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.util.LinkedList;
44
import java.util.List;
5-
import java.util.Objects;
65

76
import org.slf4j.Logger;
87
import org.slf4j.LoggerFactory;
@@ -34,7 +33,7 @@
3433
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
3534
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
3635

37-
@SuppressWarnings({"rawtypes", "unchecked"})
36+
@SuppressWarnings({"unchecked"})
3837
@Ignore
3938
public class Controller<R extends HasMetadata> implements Reconciler<R>,
4039
LifecycleAware, EventSourceInitializer<R> {
@@ -195,6 +194,10 @@ public void start() throws OperatorException {
195194
final var specVersion = "v1";
196195
log.info("Starting '{}' controller for reconciler: {}, resource: {}", controllerName,
197196
reconciler.getClass().getCanonicalName(), resClass.getCanonicalName());
197+
198+
// fail early if we're missing the current namespace information
199+
failOnMissingCurrentNS();
200+
198201
try {
199202
// check that the custom resource is known by the cluster if configured that way
200203
final CustomResourceDefinition crd; // todo: check proper CRD spec version based on config
@@ -210,13 +213,6 @@ public void start() throws OperatorException {
210213
CustomResourceUtils.assertCustomResource(resClass, crd);
211214
}
212215

213-
if (failOnMissingCurrentNS()) {
214-
throw new OperatorException(
215-
"Controller '"
216-
+ controllerName
217-
+ "' is configured to watch the current namespace but it couldn't be inferred from the current configuration.");
218-
}
219-
220216
final var context = new EventSourceContext<>(
221217
eventSourceManager.getControllerResourceEventSource().getResourceCache(),
222218
configurationService(), kubernetesClient);
@@ -263,19 +259,18 @@ private void throwMissingCRDException(String crdName, String specVersion, String
263259
}
264260

265261
/**
266-
* Determines whether we should fail because the current namespace is request as target namespace
267-
* but is missing
268-
*
269-
* @return {@code true} if the current namespace is requested but is missing, {@code false}
270-
* otherwise
262+
* Throws an {@link OperatorException} if the controller is configured to watch the current
263+
* namespace but it's absent from the configuration.
271264
*/
272-
private boolean failOnMissingCurrentNS() {
273-
if (configuration.watchCurrentNamespace()) {
274-
final var effectiveNamespaces = configuration.getEffectiveNamespaces();
275-
return effectiveNamespaces.size() == 1
276-
&& effectiveNamespaces.stream().allMatch(Objects::isNull);
265+
private void failOnMissingCurrentNS() {
266+
try {
267+
configuration.getEffectiveNamespaces();
268+
} catch (OperatorException e) {
269+
throw new OperatorException(
270+
"Controller '"
271+
+ configuration.getName()
272+
+ "' is configured to watch the current namespace but it couldn't be inferred from the current configuration.");
277273
}
278-
return false;
279274
}
280275

281276
public EventSourceManager<R> getEventSourceManager() {

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerConfiguration.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ static <R extends HasMetadata, P extends HasMetadata> InformerConfigurationBuild
133133
InformerConfiguration<R, P> configuration) {
134134
return new InformerConfigurationBuilder<R, P>(configuration.getResourceClass(),
135135
configuration.getConfigurationService())
136-
.withNamespaces(configuration.getNamespaces())
137-
.withLabelSelector(configuration.getLabelSelector())
138-
.skippingEventPropagationIfUnchanged(
139-
configuration.isSkipUpdateEventPropagationIfNoChange())
140-
.withAssociatedSecondaryResourceIdentifier(
141-
configuration.getAssociatedResourceIdentifier())
142-
.withPrimaryResourcesRetriever(configuration.getPrimaryResourcesRetriever());
136+
.withNamespaces(configuration.getNamespaces())
137+
.withLabelSelector(configuration.getLabelSelector())
138+
.skippingEventPropagationIfUnchanged(
139+
configuration.isSkipUpdateEventPropagationIfNoChange())
140+
.withAssociatedSecondaryResourceIdentifier(
141+
configuration.getAssociatedResourceIdentifier())
142+
.withPrimaryResourcesRetriever(configuration.getPrimaryResourcesRetriever());
143143
}
144144
}

operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AnnotationConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public ResourceEventFilter<R> getEventFilter() {
123123
public List<DependentResourceConfiguration> getDependentResources() {
124124
if (dependentConfigurations == null) {
125125
final var dependents = valueOrDefault(annotation, ControllerConfiguration::dependents,
126-
new Dependent[]{});
126+
new Dependent[] {});
127127
if (dependents.length > 0) {
128128
dependentConfigurations = new ArrayList<>(dependents.length);
129129
for (Dependent dependent : dependents) {
@@ -133,7 +133,7 @@ public List<DependentResourceConfiguration> getDependentResources() {
133133
if (HasMetadata.class.isAssignableFrom(resourceType)) {
134134
final var kubeDependent = dependentType.getAnnotation(KubernetesDependent.class);
135135
final var namespaces =
136-
valueOrDefault(kubeDependent, KubernetesDependent::namespaces, new String[]{});
136+
valueOrDefault(kubeDependent, KubernetesDependent::namespaces, new String[] {});
137137
final var labelSelector =
138138
valueOrDefault(kubeDependent, KubernetesDependent::labelSelector, null);
139139
final var owned = valueOrDefault(kubeDependent, KubernetesDependent::owned,

0 commit comments

Comments
 (0)